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 sun.invoke.util.VerifyAccess; 29 import java.lang.invoke.LambdaForm.Name; 30 31 import sun.invoke.util.Wrapper; 32 33 import java.io.*; 34 import java.util.*; 35 36 import jdk.internal.org.objectweb.asm.*; 37 38 import java.lang.reflect.*; 39 import static java.lang.invoke.MethodHandleStatics.*; 40 import static java.lang.invoke.MethodHandleNatives.Constants.*; 41 import sun.invoke.util.VerifyType; 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 LF = "java/lang/invoke/LambdaForm"; 52 private static final String LFN = "java/lang/invoke/LambdaForm$Name"; 53 private static final String CLS = "java/lang/Class"; 54 private static final String OBJ = "java/lang/Object"; 55 private static final String OBJARY = "[Ljava/lang/Object;"; 56 57 private static final String LF_SIG = "L" + LF + ";"; 58 private static final String LFN_SIG = "L" + LFN + ";"; 59 private static final String LL_SIG = "(L" + OBJ + ";)L" + OBJ + ";"; 60 61 /** Name of its super class*/ 62 private static final String superName = LF; 63 64 /** Name of new class */ 65 private final String className; 66 67 /** Name of the source file (for stack trace printing). */ 68 private final String sourceFile; 69 70 private final LambdaForm lambdaForm; 71 private final String invokerName; 72 private final MethodType invokerType; 73 private final int[] localsMap; 74 75 /** ASM bytecode generation. */ 76 private ClassWriter cw; 77 private MethodVisitor mv; 78 79 private static final MemberName.Factory MEMBERNAME_FACTORY = MemberName.getFactory(); 80 private static final Class<?> HOST_CLASS = LambdaForm.class; 81 82 private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize, 83 String className, String invokerName, MethodType invokerType) { 84 if (invokerName.contains(".")) { 85 int p = invokerName.indexOf("."); 86 className = invokerName.substring(0, p); 87 invokerName = invokerName.substring(p+1); 88 } 89 if (DUMP_CLASS_FILES) { 90 className = makeDumpableClassName(className); 91 } 92 this.className = superName + "$" + className; 93 this.sourceFile = "LambdaForm$" + className; 94 this.lambdaForm = lambdaForm; 95 this.invokerName = invokerName; 96 this.invokerType = invokerType; 97 this.localsMap = new int[localsMapSize]; 98 } 99 100 private InvokerBytecodeGenerator(String className, String invokerName, MethodType invokerType) { 101 this(null, invokerType.parameterCount(), 102 className, invokerName, invokerType); 103 // Create an array to map name indexes to locals indexes. 104 for (int i = 0; i < localsMap.length; i++) { 105 localsMap[i] = invokerType.parameterSlotCount() - invokerType.parameterSlotDepth(i); 106 } 107 } 108 109 private InvokerBytecodeGenerator(String className, LambdaForm form, MethodType invokerType) { 110 this(form, form.names.length, 111 className, form.debugName, invokerType); 112 // Create an array to map name indexes to locals indexes. 113 Name[] names = form.names; 114 for (int i = 0, index = 0; i < localsMap.length; i++) { 115 localsMap[i] = index; 116 index += Wrapper.forBasicType(names[i].type).stackSlots(); 117 } 118 } 119 120 121 /** instance counters for dumped classes */ 122 private final static HashMap<String,Integer> DUMP_CLASS_FILES_COUNTERS; 123 /** debugging flag for saving generated class files */ 124 private final static File DUMP_CLASS_FILES_DIR; 125 126 static { 127 if (DUMP_CLASS_FILES) { 128 DUMP_CLASS_FILES_COUNTERS = new HashMap<>(); 129 try { 130 File dumpDir = new File("DUMP_CLASS_FILES"); 131 if (!dumpDir.exists()) { 132 dumpDir.mkdirs(); 133 } 134 DUMP_CLASS_FILES_DIR = dumpDir; 135 System.out.println("Dumping class files to "+DUMP_CLASS_FILES_DIR+"/..."); 136 } catch (Exception e) { 137 throw newInternalError(e); 138 } 139 } else { 140 DUMP_CLASS_FILES_COUNTERS = null; 141 DUMP_CLASS_FILES_DIR = null; 142 } 143 } 144 145 static void maybeDump(final String className, final byte[] classFile) { 146 if (DUMP_CLASS_FILES) { 147 System.out.println("dump: " + className); 148 java.security.AccessController.doPrivileged( 149 new java.security.PrivilegedAction<Void>() { 150 public Void run() { 151 try { 152 String dumpName = className; 153 //dumpName = dumpName.replace('/', '-'); 154 File dumpFile = new File(DUMP_CLASS_FILES_DIR, dumpName+".class"); 155 dumpFile.getParentFile().mkdirs(); 156 FileOutputStream file = new FileOutputStream(dumpFile); 157 file.write(classFile); 158 file.close(); 159 return null; 160 } catch (IOException ex) { 161 throw newInternalError(ex); 162 } 163 } 164 }); 165 } 166 167 } 168 169 private static String makeDumpableClassName(String className) { 170 Integer ctr; 171 synchronized (DUMP_CLASS_FILES_COUNTERS) { 172 ctr = DUMP_CLASS_FILES_COUNTERS.get(className); 173 if (ctr == null) ctr = 0; 174 DUMP_CLASS_FILES_COUNTERS.put(className, ctr+1); 175 } 176 String sfx = ctr.toString(); 177 while (sfx.length() < 3) 178 sfx = "0"+sfx; 179 className += sfx; 180 return className; 181 } 182 183 class CpPatch { 184 final int index; 185 final String placeholder; 186 final Object value; 187 CpPatch(int index, String placeholder, Object value) { 188 this.index = index; 189 this.placeholder = placeholder; 190 this.value = value; 191 } 192 public String toString() { 193 return "CpPatch/index="+index+",placeholder="+placeholder+",value="+value; 194 } 195 } 196 197 Map<Object, CpPatch> cpPatches = new HashMap<>(); 198 199 int cph = 0; // for counting constant placeholders 200 201 String constantPlaceholder(Object arg) { 202 String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + cph++; 203 if (DUMP_CLASS_FILES) cpPlaceholder += " <<" + arg.toString() + ">>"; // debugging aid 204 if (cpPatches.containsKey(cpPlaceholder)) { 205 throw new InternalError("observed CP placeholder twice: " + cpPlaceholder); 206 } 207 // insert placeholder in CP and remember the patch 208 int index = cw.newConst((Object) cpPlaceholder); // TODO check if aready in the constant pool 209 cpPatches.put(cpPlaceholder, new CpPatch(index, cpPlaceholder, arg)); 210 return cpPlaceholder; 211 } 212 213 Object[] cpPatches(byte[] classFile) { 214 int size = getConstantPoolSize(classFile); 215 Object[] res = new Object[size]; 216 for (CpPatch p : cpPatches.values()) { 217 if (p.index >= size) 218 throw new InternalError("in cpool["+size+"]: "+p+"\n"+Arrays.toString(Arrays.copyOf(classFile, 20))); 219 res[p.index] = p.value; 220 } 221 return res; 222 } 223 224 /** 225 * Extract the number of constant pool entries from a given class file. 226 * 227 * @param classFile the bytes of the class file in question. 228 * @return the number of entries in the constant pool. 229 */ 230 private static int getConstantPoolSize(byte[] classFile) { 231 // The first few bytes: 232 // u4 magic; 233 // u2 minor_version; 234 // u2 major_version; 235 // u2 constant_pool_count; 236 return ((classFile[8] & 0xFF) << 8) | (classFile[9] & 0xFF); 237 } 238 239 /** 240 * Extract the MemberName of a newly-defined method. 241 */ 242 private MemberName loadMethod(byte[] classFile) { 243 Class<?> invokerClass = loadAndInitializeInvokerClass(classFile, cpPatches(classFile)); 244 return resolveInvokerMember(invokerClass, invokerName, invokerType); 245 } 246 247 /** 248 * Define a given class as anonymous class in the runtime system. 249 */ 250 private static Class<?> loadAndInitializeInvokerClass(byte[] classBytes, Object[] patches) { 251 Class<?> invokerClass = UNSAFE.defineAnonymousClass(HOST_CLASS, classBytes, patches); 252 UNSAFE.ensureClassInitialized(invokerClass); // Make sure the class is initialized; VM might complain. 253 return invokerClass; 254 } 255 256 private static MemberName resolveInvokerMember(Class<?> invokerClass, String name, MethodType type) { 257 MemberName member = new MemberName(invokerClass, name, type, REF_invokeStatic); 258 //System.out.println("resolveInvokerMember => "+member); 259 //for (Method m : invokerClass.getDeclaredMethods()) System.out.println(" "+m); 260 try { 261 member = MEMBERNAME_FACTORY.resolveOrFail(REF_invokeStatic, member, HOST_CLASS, ReflectiveOperationException.class); 262 } catch (ReflectiveOperationException e) { 263 throw newInternalError(e); 264 } 265 //System.out.println("resolveInvokerMember => "+member); 266 return member; 267 } 268 269 /** 270 * Set up class file generation. 271 */ 272 private void classFilePrologue() { 273 cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES); 274 cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, className, null, superName, null); 275 cw.visitSource(sourceFile, null); 276 277 String invokerDesc = invokerType.toMethodDescriptorString(); 278 mv = cw.visitMethod(Opcodes.ACC_STATIC, invokerName, invokerDesc, null, null); 279 } 280 281 /** 282 * Tear down class file generation. 283 */ 284 private void classFileEpilogue() { 285 mv.visitMaxs(0, 0); 286 mv.visitEnd(); 287 } 288 289 /* 290 * Low-level emit helpers. 291 */ 292 private void emitConst(Object con) { 293 if (con == null) { 294 mv.visitInsn(Opcodes.ACONST_NULL); 295 return; 296 } 297 if (con instanceof Integer) { 298 emitIconstInsn((int) con); 299 return; 300 } 301 if (con instanceof Long) { 302 long x = (long) con; 303 if (x == (short) x) { 304 emitIconstInsn((int) x); 305 mv.visitInsn(Opcodes.I2L); 306 return; 307 } 308 } 309 if (con instanceof Float) { 310 float x = (float) con; 311 if (x == (short) x) { 312 emitIconstInsn((int) x); 313 mv.visitInsn(Opcodes.I2F); 314 return; 315 } 316 } 317 if (con instanceof Double) { 318 double x = (double) con; 319 if (x == (short) x) { 320 emitIconstInsn((int) x); 321 mv.visitInsn(Opcodes.I2D); 322 return; 323 } 324 } 325 if (con instanceof Boolean) { 326 emitIconstInsn((boolean) con ? 1 : 0); 327 return; 328 } 329 // fall through: 330 mv.visitLdcInsn(con); 331 } 332 333 private void emitIconstInsn(int i) { 334 int opcode; 335 switch (i) { 336 case 0: opcode = Opcodes.ICONST_0; break; 337 case 1: opcode = Opcodes.ICONST_1; break; 338 case 2: opcode = Opcodes.ICONST_2; break; 339 case 3: opcode = Opcodes.ICONST_3; break; 340 case 4: opcode = Opcodes.ICONST_4; break; 341 case 5: opcode = Opcodes.ICONST_5; break; 342 default: 343 if (i == (byte) i) { 344 mv.visitIntInsn(Opcodes.BIPUSH, i & 0xFF); 345 } else if (i == (short) i) { 346 mv.visitIntInsn(Opcodes.SIPUSH, (char) i); 347 } else { 348 mv.visitLdcInsn(i); 349 } 350 return; 351 } 352 mv.visitInsn(opcode); 353 } 354 355 /* 356 * NOTE: These load/store methods use the localsMap to find the correct index! 357 */ 358 private void emitLoadInsn(char type, int index) { 359 int opcode; 360 switch (type) { 361 case 'I': opcode = Opcodes.ILOAD; break; 362 case 'J': opcode = Opcodes.LLOAD; break; 363 case 'F': opcode = Opcodes.FLOAD; break; 364 case 'D': opcode = Opcodes.DLOAD; break; 365 case 'L': opcode = Opcodes.ALOAD; break; 366 default: 367 throw new InternalError("unknown type: " + type); 368 } 369 mv.visitVarInsn(opcode, localsMap[index]); 370 } 371 private void emitAloadInsn(int index) { 372 emitLoadInsn('L', index); 373 } 374 375 private void emitStoreInsn(char type, int index) { 376 int opcode; 377 switch (type) { 378 case 'I': opcode = Opcodes.ISTORE; break; 379 case 'J': opcode = Opcodes.LSTORE; break; 380 case 'F': opcode = Opcodes.FSTORE; break; 381 case 'D': opcode = Opcodes.DSTORE; break; 382 case 'L': opcode = Opcodes.ASTORE; break; 383 default: 384 throw new InternalError("unknown type: " + type); 385 } 386 mv.visitVarInsn(opcode, localsMap[index]); 387 } 388 private void emitAstoreInsn(int index) { 389 emitStoreInsn('L', index); 390 } 391 392 /** 393 * Emit a boxing call. 394 * 395 * @param type primitive type class to box. 396 */ 397 private void emitBoxing(Class<?> type) { 398 Wrapper wrapper = Wrapper.forPrimitiveType(type); 399 String owner = "java/lang/" + wrapper.wrapperType().getSimpleName(); 400 String name = "valueOf"; 401 String desc = "(" + wrapper.basicTypeChar() + ")L" + owner + ";"; 402 mv.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc); 403 } 404 405 /** 406 * Emit an unboxing call (plus preceding checkcast). 407 * 408 * @param type wrapper type class to unbox. 409 */ 410 private void emitUnboxing(Class<?> type) { 411 Wrapper wrapper = Wrapper.forWrapperType(type); 412 String owner = "java/lang/" + wrapper.wrapperType().getSimpleName(); 413 String name = wrapper.primitiveSimpleName() + "Value"; 414 String desc = "()" + wrapper.basicTypeChar(); 415 mv.visitTypeInsn(Opcodes.CHECKCAST, owner); 416 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc); 417 } 418 419 /** 420 * Emit an implicit conversion. 421 * 422 * @param ptype type of value present on stack 423 * @param pclass type of value required on stack 424 */ 425 private void emitImplicitConversion(char ptype, Class<?> pclass) { 426 switch (ptype) { 427 case 'L': 428 if (VerifyType.isNullConversion(Object.class, pclass)) 429 return; 430 if (isStaticallyNameable(pclass)) { 431 mv.visitTypeInsn(Opcodes.CHECKCAST, getInternalName(pclass)); 432 } else { 433 mv.visitLdcInsn(constantPlaceholder(pclass)); 434 mv.visitTypeInsn(Opcodes.CHECKCAST, CLS); 435 mv.visitInsn(Opcodes.SWAP); 436 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, CLS, "cast", LL_SIG); 437 if (pclass.isArray()) 438 mv.visitTypeInsn(Opcodes.CHECKCAST, OBJARY); 439 } 440 return; 441 case 'I': 442 if (!VerifyType.isNullConversion(int.class, pclass)) 443 emitPrimCast(ptype, Wrapper.basicTypeChar(pclass)); 444 return; 445 case 'J': 446 assert(pclass == long.class); 447 return; 448 case 'F': 449 assert(pclass == float.class); 450 return; 451 case 'D': 452 assert(pclass == double.class); 453 return; 454 } 455 throw new InternalError("bad implicit conversion: tc="+ptype+": "+pclass); 456 } 457 458 /** 459 * Emits an actual return instruction conforming to the given return type. 460 */ 461 private void emitReturnInsn(Class<?> type) { 462 int opcode; 463 switch (Wrapper.basicTypeChar(type)) { 464 case 'I': opcode = Opcodes.IRETURN; break; 465 case 'J': opcode = Opcodes.LRETURN; break; 466 case 'F': opcode = Opcodes.FRETURN; break; 467 case 'D': opcode = Opcodes.DRETURN; break; 468 case 'L': opcode = Opcodes.ARETURN; break; 469 case 'V': opcode = Opcodes.RETURN; break; 470 default: 471 throw new InternalError("unknown return type: " + type); 472 } 473 mv.visitInsn(opcode); 474 } 475 476 private static String getInternalName(Class<?> c) { 477 assert(VerifyAccess.isTypeVisible(c, Object.class)); 478 return c.getName().replace('.', '/'); 479 } 480 481 /** 482 * Generate customized bytecode for a given LambdaForm. 483 */ 484 static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType) { 485 InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("MH", form, invokerType); 486 return g.loadMethod(g.generateCustomizedCodeBytes()); 487 } 488 489 /** 490 * Generate an invoker method for the passed {@link LambdaForm}. 491 */ 492 private byte[] generateCustomizedCodeBytes() { 493 classFilePrologue(); 494 495 // Suppress this method in backtraces displayed to the user. 496 mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true); 497 498 // Mark this method as a compiled LambdaForm 499 mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Compiled;", true); 500 501 // Force inlining of this invoker method. 502 mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true); 503 504 // iterate over the form's names, generating bytecode instructions for each 505 // start iterating at the first name following the arguments 506 for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) { 507 Name name = lambdaForm.names[i]; 508 MemberName member = name.function.member(); 509 510 if (isSelectAlternative(member)) { 511 // selectAlternative idiom 512 // FIXME: make sure this idiom is really present! 513 emitSelectAlternative(name, lambdaForm.names[i + 1]); 514 i++; // skip MH.invokeBasic of the selectAlternative result 515 } else if (isGuardWithCatch(i)) { 516 emitGuardWithCatch(i); 517 i = i+2; // Jump to the end of GWC idiom 518 } else if (isStaticallyInvocable(member)) { 519 emitStaticInvoke(member, name); 520 } else { 521 emitInvoke(name); 522 } 523 524 // Update cached form name's info in case an intrinsic spanning multiple names was encountered. 525 name = lambdaForm.names[i]; 526 member = name.function.member(); 527 528 // store the result from evaluating to the target name in a local if required 529 // (if this is the last value, i.e., the one that is going to be returned, 530 // avoid store/load/return and just return) 531 if (i == lambdaForm.names.length - 1 && i == lambdaForm.result) { 532 // return value - do nothing 533 } else if (name.type != 'V') { 534 // non-void: actually assign 535 emitStoreInsn(name.type, name.index()); 536 } 537 } 538 539 // return statement 540 emitReturn(); 541 542 classFileEpilogue(); 543 bogusMethod(lambdaForm); 544 545 final byte[] classFile = cw.toByteArray(); 546 maybeDump(className, classFile); 547 return classFile; 548 } 549 550 /** 551 * Emit an invoke for the given name. 552 */ 553 void emitInvoke(Name name) { 554 if (true) { 555 // push receiver 556 MethodHandle target = name.function.resolvedHandle; 557 assert(target != null) : name.exprString(); 558 mv.visitLdcInsn(constantPlaceholder(target)); 559 mv.visitTypeInsn(Opcodes.CHECKCAST, MH); 560 } else { 561 // load receiver 562 emitAloadInsn(0); 563 mv.visitTypeInsn(Opcodes.CHECKCAST, MH); 564 mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG); 565 mv.visitFieldInsn(Opcodes.GETFIELD, LF, "names", LFN_SIG); 566 // TODO more to come 567 } 568 569 // push arguments 570 for (int i = 0; i < name.arguments.length; i++) { 571 emitPushArgument(name, i); 572 } 573 574 // invocation 575 MethodType type = name.function.methodType(); 576 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString()); 577 } 578 579 static private Class<?>[] STATICALLY_INVOCABLE_PACKAGES = { 580 // Sample classes from each package we are willing to bind to statically: 581 java.lang.Object.class, 582 java.util.Arrays.class, 583 sun.misc.Unsafe.class 584 //MethodHandle.class already covered 585 }; 586 587 static boolean isStaticallyInvocable(MemberName member) { 588 if (member == null) return false; 589 if (member.isConstructor()) return false; 590 Class<?> cls = member.getDeclaringClass(); 591 if (cls.isArray() || cls.isPrimitive()) 592 return false; // FIXME 593 if (cls.isAnonymousClass() || cls.isLocalClass()) 594 return false; // inner class of some sort 595 if (cls.getClassLoader() != MethodHandle.class.getClassLoader()) 596 return false; // not on BCP 597 MethodType mtype = member.getMethodOrFieldType(); 598 if (!isStaticallyNameable(mtype.returnType())) 599 return false; 600 for (Class<?> ptype : mtype.parameterArray()) 601 if (!isStaticallyNameable(ptype)) 602 return false; 603 if (!member.isPrivate() && VerifyAccess.isSamePackage(MethodHandle.class, cls)) 604 return true; // in java.lang.invoke package 605 if (member.isPublic() && isStaticallyNameable(cls)) 606 return true; 607 return false; 608 } 609 610 static boolean isStaticallyNameable(Class<?> cls) { 611 while (cls.isArray()) 612 cls = cls.getComponentType(); 613 if (cls.isPrimitive()) 614 return true; // int[].class, for example 615 // could use VerifyAccess.isClassAccessible but the following is a safe approximation 616 if (cls.getClassLoader() != Object.class.getClassLoader()) 617 return false; 618 if (VerifyAccess.isSamePackage(MethodHandle.class, cls)) 619 return true; 620 if (!Modifier.isPublic(cls.getModifiers())) 621 return false; 622 for (Class<?> pkgcls : STATICALLY_INVOCABLE_PACKAGES) { 623 if (VerifyAccess.isSamePackage(pkgcls, cls)) 624 return true; 625 } 626 return false; 627 } 628 629 /** 630 * Emit an invoke for the given name, using the MemberName directly. 631 */ 632 void emitStaticInvoke(MemberName member, Name name) { 633 assert(member.equals(name.function.member())); 634 String cname = getInternalName(member.getDeclaringClass()); 635 String mname = member.getName(); 636 String mtype; 637 byte refKind = member.getReferenceKind(); 638 if (refKind == REF_invokeSpecial) { 639 // in order to pass the verifier, we need to convert this to invokevirtual in all cases 640 assert(member.canBeStaticallyBound()) : member; 641 refKind = REF_invokeVirtual; 642 } 643 644 if (member.getDeclaringClass().isInterface() && refKind == REF_invokeVirtual) { 645 // Methods from Object declared in an interface can be resolved by JVM to invokevirtual kind. 646 // Need to convert it back to invokeinterface to pass verification and make the invocation works as expected. 647 refKind = REF_invokeInterface; 648 } 649 650 // push arguments 651 for (int i = 0; i < name.arguments.length; i++) { 652 emitPushArgument(name, i); 653 } 654 655 // invocation 656 if (member.isMethod()) { 657 mtype = member.getMethodType().toMethodDescriptorString(); 658 mv.visitMethodInsn(refKindOpcode(refKind), cname, mname, mtype, 659 member.getDeclaringClass().isInterface()); 660 } else { 661 mtype = MethodType.toFieldDescriptorString(member.getFieldType()); 662 mv.visitFieldInsn(refKindOpcode(refKind), cname, mname, mtype); 663 } 664 } 665 int refKindOpcode(byte refKind) { 666 switch (refKind) { 667 case REF_invokeVirtual: return Opcodes.INVOKEVIRTUAL; 668 case REF_invokeStatic: return Opcodes.INVOKESTATIC; 669 case REF_invokeSpecial: return Opcodes.INVOKESPECIAL; 670 case REF_invokeInterface: return Opcodes.INVOKEINTERFACE; 671 case REF_getField: return Opcodes.GETFIELD; 672 case REF_putField: return Opcodes.PUTFIELD; 673 case REF_getStatic: return Opcodes.GETSTATIC; 674 case REF_putStatic: return Opcodes.PUTSTATIC; 675 } 676 throw new InternalError("refKind="+refKind); 677 } 678 679 /** 680 * Check if MemberName is a call to a method named {@code name} in class {@code declaredClass}. 681 */ 682 private boolean memberNameRefersTo(MemberName member, Class<?> declaringClass, String name) { 683 return member != null && 684 member.getDeclaringClass() == declaringClass && 685 member.getName().equals(name); 686 } 687 688 /** 689 * Check if MemberName is a call to MethodHandleImpl.selectAlternative. 690 */ 691 private boolean isSelectAlternative(MemberName member) { 692 return memberNameRefersTo(member, MethodHandleImpl.class, "selectAlternative"); 693 } 694 695 /** 696 * Emit bytecode for the selectAlternative idiom. 697 * 698 * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest): 699 * <blockquote><pre>{@code 700 * Lambda(a0:L,a1:I)=>{ 701 * t2:I=foo.test(a1:I); 702 * t3:L=MethodHandleImpl.selectAlternative(t2:I,(MethodHandle(int)int),(MethodHandle(int)int)); 703 * t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I} 704 * }</pre></blockquote> 705 */ 706 private void emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) { 707 MethodType type = selectAlternativeName.function.methodType(); 708 709 Name receiver = (Name) invokeBasicName.arguments[0]; 710 711 Label L_fallback = new Label(); 712 Label L_done = new Label(); 713 714 // load test result 715 emitPushArgument(selectAlternativeName, 0); 716 mv.visitInsn(Opcodes.ICONST_1); 717 718 // if_icmpne L_fallback 719 mv.visitJumpInsn(Opcodes.IF_ICMPNE, L_fallback); 720 721 // invoke selectAlternativeName.arguments[1] 722 MethodHandle target = (MethodHandle) selectAlternativeName.arguments[1]; 723 emitPushArgument(selectAlternativeName, 1); // get 2nd argument of selectAlternative 724 emitAstoreInsn(receiver.index()); // store the MH in the receiver slot 725 emitInvoke(invokeBasicName); 726 727 // goto L_done 728 mv.visitJumpInsn(Opcodes.GOTO, L_done); 729 730 // L_fallback: 731 mv.visitLabel(L_fallback); 732 733 // invoke selectAlternativeName.arguments[2] 734 MethodHandle fallback = (MethodHandle) selectAlternativeName.arguments[2]; 735 emitPushArgument(selectAlternativeName, 2); // get 3rd argument of selectAlternative 736 emitAstoreInsn(receiver.index()); // store the MH in the receiver slot 737 emitInvoke(invokeBasicName); 738 739 // L_done: 740 mv.visitLabel(L_done); 741 } 742 743 /** 744 * Check if i-th name is a start of GuardWithCatch idiom. 745 */ 746 private boolean isGuardWithCatch(int pos) { 747 // GuardWithCatch idiom: 748 // t_{n}:L=MethodHandle.invokeBasic(...) 749 // t_{n+1}:L=MethodHandleImpl.guardWithCatch(...); 750 // t_{n+2}:I=MethodHandle.invokeBasic(...) 751 return lambdaForm.names.length-1 >= pos+2 && 752 memberNameRefersTo(lambdaForm.names[pos+1].function.member(), 753 MethodHandleImpl.class, "guardWithCatch"); 754 } 755 756 /** 757 * Emit bytecode for the guardWithCatch idiom. 758 * 759 * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithCatch): 760 * <blockquote><pre>{@code 761 * guardWithCatch=Lambda(a0:L,a1:L,a2:L,a3:L,a4:L,a5:L,a6:L,a7:L)=>{ 762 * t8:L=MethodHandle.invokeBasic(a4:L,a6:L,a7:L); 763 * t9:L=MethodHandleImpl.guardWithCatch(a1:L,a2:L,a3:L,t8:L); 764 * t10:I=MethodHandle.invokeBasic(a5:L,t9:L);t10:I} 765 * }</pre></blockquote> 766 * 767 * It is compiled into bytecode equivalent of the following code: 768 * <blockquote><pre>{@code 769 * try { 770 * return a1.invokeBasic(a6, a7); 771 * } catch (Throwable e) { 772 * if (!a2.isInstance(e)) throw e; 773 * return a3.invokeBasic(ex, a6, a7); 774 * }} 775 */ 776 private void emitGuardWithCatch(int pos) { 777 Name args = lambdaForm.names[pos]; 778 Name invoker = lambdaForm.names[pos+1]; 779 Name result = lambdaForm.names[pos+2]; 780 781 Label L_startBlock = new Label(); 782 Label L_endBlock = new Label(); 783 Label L_handler = new Label(); 784 Label L_done = new Label(); 785 786 Class<?> returnType = result.function.resolvedHandle.type().returnType(); 787 MethodType type = args.function.resolvedHandle.type() 788 .dropParameterTypes(0,1) 789 .changeReturnType(returnType); 790 791 mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_handler, "java/lang/Throwable"); 792 793 // Normal case 794 mv.visitLabel(L_startBlock); 795 // load target 796 emitPushArgument(invoker, 0); 797 emitPushArguments(args, 1); // skip 1st argument: method handle 798 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString()); 799 mv.visitLabel(L_endBlock); 800 mv.visitJumpInsn(Opcodes.GOTO, L_done); 801 802 // Exceptional case 803 mv.visitLabel(L_handler); 804 805 // Check exception's type 806 mv.visitInsn(Opcodes.DUP); 807 // load exception class 808 emitPushArgument(invoker, 1); 809 mv.visitInsn(Opcodes.SWAP); 810 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "isInstance", "(Ljava/lang/Object;)Z"); 811 Label L_rethrow = new Label(); 812 mv.visitJumpInsn(Opcodes.IFEQ, L_rethrow); 813 814 // Invoke catcher 815 // load catcher 816 emitPushArgument(invoker, 2); 817 mv.visitInsn(Opcodes.SWAP); 818 emitPushArguments(args, 1); // skip 1st argument: method handle 819 MethodType catcherType = type.insertParameterTypes(0, Throwable.class); 820 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", catcherType.basicType().toMethodDescriptorString()); 821 mv.visitJumpInsn(Opcodes.GOTO, L_done); 822 823 mv.visitLabel(L_rethrow); 824 mv.visitInsn(Opcodes.ATHROW); 825 826 mv.visitLabel(L_done); 827 } 828 829 private void emitPushArguments(Name args, int start) { 830 for (int i = start; i < args.arguments.length; i++) { 831 emitPushArgument(args, i); 832 } 833 } 834 835 private void emitPushArgument(Name name, int paramIndex) { 836 Object arg = name.arguments[paramIndex]; 837 char ptype = name.function.parameterType(paramIndex); 838 MethodType mtype = name.function.methodType(); 839 if (arg instanceof Name) { 840 Name n = (Name) arg; 841 emitLoadInsn(n.type, n.index()); 842 emitImplicitConversion(n.type, mtype.parameterType(paramIndex)); 843 } else if ((arg == null || arg instanceof String) && ptype == 'L') { 844 emitConst(arg); 845 } else { 846 if (Wrapper.isWrapperType(arg.getClass()) && ptype != 'L') { 847 emitConst(arg); 848 } else { 849 mv.visitLdcInsn(constantPlaceholder(arg)); 850 emitImplicitConversion('L', mtype.parameterType(paramIndex)); 851 } 852 } 853 } 854 855 /** 856 * Emits a return statement from a LF invoker. If required, the result type is cast to the correct return type. 857 */ 858 private void emitReturn() { 859 // return statement 860 if (lambdaForm.result == -1) { 861 // void 862 mv.visitInsn(Opcodes.RETURN); 863 } else { 864 LambdaForm.Name rn = lambdaForm.names[lambdaForm.result]; 865 char rtype = Wrapper.basicTypeChar(invokerType.returnType()); 866 867 // put return value on the stack if it is not already there 868 if (lambdaForm.result != lambdaForm.names.length - 1) { 869 emitLoadInsn(rn.type, lambdaForm.result); 870 } 871 872 // potentially generate cast 873 // rtype is the return type of the invoker - generated code must conform to this 874 // rn.type is the type of the result Name in the LF 875 if (rtype != rn.type) { 876 // need cast 877 if (rtype == 'L') { 878 // possibly cast the primitive to the correct type for boxing 879 char boxedType = Wrapper.forWrapperType(invokerType.returnType()).basicTypeChar(); 880 if (boxedType != rn.type) { 881 emitPrimCast(rn.type, boxedType); 882 } 883 // cast primitive to reference ("boxing") 884 emitBoxing(invokerType.returnType()); 885 } else { 886 // to-primitive cast 887 if (rn.type != 'L') { 888 // prim-to-prim cast 889 emitPrimCast(rn.type, rtype); 890 } else { 891 // ref-to-prim cast ("unboxing") 892 throw new InternalError("no ref-to-prim (unboxing) casts supported right now"); 893 } 894 } 895 } 896 897 // generate actual return statement 898 emitReturnInsn(invokerType.returnType()); 899 } 900 } 901 902 /** 903 * Emit a type conversion bytecode casting from "from" to "to". 904 */ 905 private void emitPrimCast(char from, char to) { 906 // Here's how. 907 // - indicates forbidden 908 // <-> indicates implicit 909 // to ----> boolean byte short char int long float double 910 // from boolean <-> - - - - - - - 911 // byte - <-> i2s i2c <-> i2l i2f i2d 912 // short - i2b <-> i2c <-> i2l i2f i2d 913 // char - i2b i2s <-> <-> i2l i2f i2d 914 // int - i2b i2s i2c <-> i2l i2f i2d 915 // long - l2i,i2b l2i,i2s l2i,i2c l2i <-> l2f l2d 916 // float - f2i,i2b f2i,i2s f2i,i2c f2i f2l <-> f2d 917 // double - d2i,i2b d2i,i2s d2i,i2c d2i d2l d2f <-> 918 if (from == to) { 919 // no cast required, should be dead code anyway 920 return; 921 } 922 Wrapper wfrom = Wrapper.forBasicType(from); 923 Wrapper wto = Wrapper.forBasicType(to); 924 if (wfrom.isSubwordOrInt()) { 925 // cast from {byte,short,char,int} to anything 926 emitI2X(to); 927 } else { 928 // cast from {long,float,double} to anything 929 if (wto.isSubwordOrInt()) { 930 // cast to {byte,short,char,int} 931 emitX2I(from); 932 if (wto.bitWidth() < 32) { 933 // targets other than int require another conversion 934 emitI2X(to); 935 } 936 } else { 937 // cast to {long,float,double} - this is verbose 938 boolean error = false; 939 switch (from) { 940 case 'J': 941 if (to == 'F') { mv.visitInsn(Opcodes.L2F); } 942 else if (to == 'D') { mv.visitInsn(Opcodes.L2D); } 943 else error = true; 944 break; 945 case 'F': 946 if (to == 'J') { mv.visitInsn(Opcodes.F2L); } 947 else if (to == 'D') { mv.visitInsn(Opcodes.F2D); } 948 else error = true; 949 break; 950 case 'D': 951 if (to == 'J') { mv.visitInsn(Opcodes.D2L); } 952 else if (to == 'F') { mv.visitInsn(Opcodes.D2F); } 953 else error = true; 954 break; 955 default: 956 error = true; 957 break; 958 } 959 if (error) { 960 throw new IllegalStateException("unhandled prim cast: " + from + "2" + to); 961 } 962 } 963 } 964 } 965 966 private void emitI2X(char type) { 967 switch (type) { 968 case 'B': mv.visitInsn(Opcodes.I2B); break; 969 case 'S': mv.visitInsn(Opcodes.I2S); break; 970 case 'C': mv.visitInsn(Opcodes.I2C); break; 971 case 'I': /* naught */ break; 972 case 'J': mv.visitInsn(Opcodes.I2L); break; 973 case 'F': mv.visitInsn(Opcodes.I2F); break; 974 case 'D': mv.visitInsn(Opcodes.I2D); break; 975 case 'Z': 976 // For compatibility with ValueConversions and explicitCastArguments: 977 mv.visitInsn(Opcodes.ICONST_1); 978 mv.visitInsn(Opcodes.IAND); 979 break; 980 default: throw new InternalError("unknown type: " + type); 981 } 982 } 983 984 private void emitX2I(char type) { 985 switch (type) { 986 case 'J': mv.visitInsn(Opcodes.L2I); break; 987 case 'F': mv.visitInsn(Opcodes.F2I); break; 988 case 'D': mv.visitInsn(Opcodes.D2I); break; 989 default: throw new InternalError("unknown type: " + type); 990 } 991 } 992 993 private static String basicTypeCharSignature(String prefix, MethodType type) { 994 StringBuilder buf = new StringBuilder(prefix); 995 for (Class<?> ptype : type.parameterList()) 996 buf.append(Wrapper.forBasicType(ptype).basicTypeChar()); 997 buf.append('_').append(Wrapper.forBasicType(type.returnType()).basicTypeChar()); 998 return buf.toString(); 999 } 1000 1001 /** 1002 * Generate bytecode for a LambdaForm.vmentry which calls interpretWithArguments. 1003 */ 1004 static MemberName generateLambdaFormInterpreterEntryPoint(String sig) { 1005 assert(LambdaForm.isValidSignature(sig)); 1006 //System.out.println("generateExactInvoker "+sig); 1007 // compute method type 1008 // first parameter and return type 1009 char tret = LambdaForm.signatureReturn(sig); 1010 MethodType type = MethodType.methodType(LambdaForm.typeClass(tret), MethodHandle.class); 1011 // other parameter types 1012 int arity = LambdaForm.signatureArity(sig); 1013 for (int i = 1; i < arity; i++) { 1014 type = type.appendParameterTypes(LambdaForm.typeClass(sig.charAt(i))); 1015 } 1016 InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("LFI", "interpret_"+tret, type); 1017 return g.loadMethod(g.generateLambdaFormInterpreterEntryPointBytes()); 1018 } 1019 1020 private byte[] generateLambdaFormInterpreterEntryPointBytes() { 1021 classFilePrologue(); 1022 1023 // Suppress this method in backtraces displayed to the user. 1024 mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true); 1025 1026 // Don't inline the interpreter entry. 1027 mv.visitAnnotation("Ljava/lang/invoke/DontInline;", true); 1028 1029 // create parameter array 1030 emitIconstInsn(invokerType.parameterCount()); 1031 mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); 1032 1033 // fill parameter array 1034 for (int i = 0; i < invokerType.parameterCount(); i++) { 1035 Class<?> ptype = invokerType.parameterType(i); 1036 mv.visitInsn(Opcodes.DUP); 1037 emitIconstInsn(i); 1038 emitLoadInsn(Wrapper.basicTypeChar(ptype), i); 1039 // box if primitive type 1040 if (ptype.isPrimitive()) { 1041 emitBoxing(ptype); 1042 } 1043 mv.visitInsn(Opcodes.AASTORE); 1044 } 1045 // invoke 1046 emitAloadInsn(0); 1047 mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", "Ljava/lang/invoke/LambdaForm;"); 1048 mv.visitInsn(Opcodes.SWAP); // swap form and array; avoid local variable 1049 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, LF, "interpretWithArguments", "([Ljava/lang/Object;)Ljava/lang/Object;"); 1050 1051 // maybe unbox 1052 Class<?> rtype = invokerType.returnType(); 1053 if (rtype.isPrimitive() && rtype != void.class) { 1054 emitUnboxing(Wrapper.asWrapperType(rtype)); 1055 } 1056 1057 // return statement 1058 emitReturnInsn(rtype); 1059 1060 classFileEpilogue(); 1061 bogusMethod(invokerType); 1062 1063 final byte[] classFile = cw.toByteArray(); 1064 maybeDump(className, classFile); 1065 return classFile; 1066 } 1067 1068 /** 1069 * Generate bytecode for a NamedFunction invoker. 1070 */ 1071 static MemberName generateNamedFunctionInvoker(MethodTypeForm typeForm) { 1072 MethodType invokerType = LambdaForm.NamedFunction.INVOKER_METHOD_TYPE; 1073 String invokerName = basicTypeCharSignature("invoke_", typeForm.erasedType()); 1074 InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("NFI", invokerName, invokerType); 1075 return g.loadMethod(g.generateNamedFunctionInvokerImpl(typeForm)); 1076 } 1077 1078 static int nfi = 0; 1079 1080 private byte[] generateNamedFunctionInvokerImpl(MethodTypeForm typeForm) { 1081 MethodType dstType = typeForm.erasedType(); 1082 classFilePrologue(); 1083 1084 // Suppress this method in backtraces displayed to the user. 1085 mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true); 1086 1087 // Force inlining of this invoker method. 1088 mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true); 1089 1090 // Load receiver 1091 emitAloadInsn(0); 1092 1093 // Load arguments from array 1094 for (int i = 0; i < dstType.parameterCount(); i++) { 1095 emitAloadInsn(1); 1096 emitIconstInsn(i); 1097 mv.visitInsn(Opcodes.AALOAD); 1098 1099 // Maybe unbox 1100 Class<?> dptype = dstType.parameterType(i); 1101 if (dptype.isPrimitive()) { 1102 Class<?> sptype = dstType.basicType().wrap().parameterType(i); 1103 Wrapper dstWrapper = Wrapper.forBasicType(dptype); 1104 Wrapper srcWrapper = dstWrapper.isSubwordOrInt() ? Wrapper.INT : dstWrapper; // narrow subword from int 1105 emitUnboxing(srcWrapper.wrapperType()); 1106 emitPrimCast(srcWrapper.basicTypeChar(), dstWrapper.basicTypeChar()); 1107 } 1108 } 1109 1110 // Invoke 1111 String targetDesc = dstType.basicType().toMethodDescriptorString(); 1112 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", targetDesc); 1113 1114 // Box primitive types 1115 Class<?> rtype = dstType.returnType(); 1116 if (rtype != void.class && rtype.isPrimitive()) { 1117 Wrapper srcWrapper = Wrapper.forBasicType(rtype); 1118 Wrapper dstWrapper = srcWrapper.isSubwordOrInt() ? Wrapper.INT : srcWrapper; // widen subword to int 1119 // boolean casts not allowed 1120 emitPrimCast(srcWrapper.basicTypeChar(), dstWrapper.basicTypeChar()); 1121 emitBoxing(dstWrapper.primitiveType()); 1122 } 1123 1124 // If the return type is void we return a null reference. 1125 if (rtype == void.class) { 1126 mv.visitInsn(Opcodes.ACONST_NULL); 1127 } 1128 emitReturnInsn(Object.class); // NOTE: NamedFunction invokers always return a reference value. 1129 1130 classFileEpilogue(); 1131 bogusMethod(dstType); 1132 1133 final byte[] classFile = cw.toByteArray(); 1134 maybeDump(className, classFile); 1135 return classFile; 1136 } 1137 1138 /** 1139 * Emit a bogus method that just loads some string constants. This is to get the constants into the constant pool 1140 * for debugging purposes. 1141 */ 1142 private void bogusMethod(Object... os) { 1143 if (DUMP_CLASS_FILES) { 1144 mv = cw.visitMethod(Opcodes.ACC_STATIC, "dummy", "()V", null, null); 1145 for (Object o : os) { 1146 mv.visitLdcInsn(o.toString()); 1147 mv.visitInsn(Opcodes.POP); 1148 } 1149 mv.visitInsn(Opcodes.RETURN); 1150 mv.visitMaxs(0, 0); 1151 mv.visitEnd(); 1152 } 1153 } 1154 }