1 /* 2 * Copyright (c) 2010, 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 jdk.nashorn.internal.codegen; 27 28 import static jdk.internal.org.objectweb.asm.Opcodes.ATHROW; 29 import static jdk.internal.org.objectweb.asm.Opcodes.CHECKCAST; 30 import static jdk.internal.org.objectweb.asm.Opcodes.DUP2; 31 import static jdk.internal.org.objectweb.asm.Opcodes.GETFIELD; 32 import static jdk.internal.org.objectweb.asm.Opcodes.GETSTATIC; 33 import static jdk.internal.org.objectweb.asm.Opcodes.GOTO; 34 import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC; 35 import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKEINTERFACE; 36 import static jdk.internal.org.objectweb.asm.Opcodes.IFEQ; 37 import static jdk.internal.org.objectweb.asm.Opcodes.IFGE; 38 import static jdk.internal.org.objectweb.asm.Opcodes.IFGT; 39 import static jdk.internal.org.objectweb.asm.Opcodes.IFLE; 40 import static jdk.internal.org.objectweb.asm.Opcodes.IFLT; 41 import static jdk.internal.org.objectweb.asm.Opcodes.IFNE; 42 import static jdk.internal.org.objectweb.asm.Opcodes.IFNONNULL; 43 import static jdk.internal.org.objectweb.asm.Opcodes.IFNULL; 44 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ACMPEQ; 45 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ACMPNE; 46 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPEQ; 47 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPGE; 48 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPGT; 49 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPLE; 50 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPLT; 51 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPNE; 52 import static jdk.internal.org.objectweb.asm.Opcodes.INSTANCEOF; 53 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEINTERFACE; 54 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL; 55 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC; 56 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEVIRTUAL; 57 import static jdk.internal.org.objectweb.asm.Opcodes.NEW; 58 import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD; 59 import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC; 60 import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; 61 import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS; 62 import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS; 63 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; 64 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; 65 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS_DEBUGGER; 66 import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS; 67 import static jdk.nashorn.internal.codegen.CompilerConstants.className; 68 import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup; 69 import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor; 70 import static jdk.nashorn.internal.codegen.CompilerConstants.staticField; 71 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; 72 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.PRIMITIVE_FIELD_TYPE; 73 import static jdk.nashorn.internal.runtime.linker.NameCodec.EMPTY_NAME; 74 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC; 75 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROGRAM_POINT_SHIFT; 76 77 import java.io.PrintStream; 78 import java.lang.reflect.Array; 79 import java.util.Collection; 80 import java.util.EnumSet; 81 import java.util.IdentityHashMap; 82 import java.util.List; 83 import java.util.Map; 84 import jdk.internal.org.objectweb.asm.Handle; 85 import jdk.internal.org.objectweb.asm.MethodVisitor; 86 import jdk.nashorn.internal.codegen.ClassEmitter.Flag; 87 import jdk.nashorn.internal.codegen.CompilerConstants.Call; 88 import jdk.nashorn.internal.codegen.CompilerConstants.FieldAccess; 89 import jdk.nashorn.internal.codegen.types.ArrayType; 90 import jdk.nashorn.internal.codegen.types.BitwiseType; 91 import jdk.nashorn.internal.codegen.types.NumericType; 92 import jdk.nashorn.internal.codegen.types.Type; 93 import jdk.nashorn.internal.ir.FunctionNode; 94 import jdk.nashorn.internal.ir.IdentNode; 95 import jdk.nashorn.internal.ir.JoinPredecessor; 96 import jdk.nashorn.internal.ir.LiteralNode; 97 import jdk.nashorn.internal.ir.LocalVariableConversion; 98 import jdk.nashorn.internal.ir.Symbol; 99 import jdk.nashorn.internal.ir.TryNode; 100 import jdk.nashorn.internal.objects.NativeArray; 101 import jdk.nashorn.internal.runtime.ArgumentSetter; 102 import jdk.nashorn.internal.runtime.Context; 103 import jdk.nashorn.internal.runtime.Debug; 104 import jdk.nashorn.internal.runtime.JSType; 105 import jdk.nashorn.internal.runtime.RewriteException; 106 import jdk.nashorn.internal.runtime.Scope; 107 import jdk.nashorn.internal.runtime.ScriptObject; 108 import jdk.nashorn.internal.runtime.ScriptRuntime; 109 import jdk.nashorn.internal.runtime.UnwarrantedOptimismException; 110 import jdk.nashorn.internal.runtime.linker.Bootstrap; 111 import jdk.nashorn.internal.runtime.linker.NameCodec; 112 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; 113 import jdk.nashorn.internal.runtime.logging.DebugLogger; 114 import jdk.nashorn.internal.runtime.options.Options; 115 116 /** 117 * This is the main function responsible for emitting method code 118 * in a class. It maintains a type stack and keeps track of control 119 * flow to make sure that the registered instructions don't violate 120 * byte code verification. 121 * 122 * Running Nashorn with -ea will assert as soon as a type stack 123 * becomes corrupt, for easier debugging 124 * 125 * Running Nashorn with -Dnashorn.codegen.debug=true will print 126 * all generated bytecode and labels to stderr, for easier debugging, 127 * including bytecode stack contents 128 */ 129 public class MethodEmitter { 130 /** The ASM MethodVisitor we are plugged into */ 131 private final MethodVisitor method; 132 133 /** Parent classEmitter representing the class of this method */ 134 private final ClassEmitter classEmitter; 135 136 /** FunctionNode representing this method, or null if none exists */ 137 protected FunctionNode functionNode; 138 139 /** Current type stack for current evaluation */ 140 private Label.Stack stack; 141 142 private boolean preventUndefinedLoad; 143 144 /** 145 * Map of live local variable definitions. 146 */ 147 private final Map<Symbol, LocalVariableDef> localVariableDefs = new IdentityHashMap<>(); 148 149 /** The context */ 150 private final Context context; 151 152 /** Threshold in chars for when string constants should be split */ 153 static final int LARGE_STRING_THRESHOLD = 32 * 1024; 154 155 /** Debug flag, should we dump all generated bytecode along with stacks? */ 156 private final DebugLogger log; 157 private final boolean debug; 158 159 /** dump stack on a particular line, or -1 if disabled */ 160 private static final int DEBUG_TRACE_LINE; 161 162 static { 163 final String tl = Options.getStringProperty("nashorn.codegen.debug.trace", "-1"); 164 int line = -1; 165 try { 166 line = Integer.parseInt(tl); 167 } catch (final NumberFormatException e) { 168 //fallthru 169 } 170 DEBUG_TRACE_LINE = line; 171 } 172 173 /** Bootstrap for normal indy:s */ 174 private static final Handle LINKERBOOTSTRAP = new Handle(H_INVOKESTATIC, Bootstrap.BOOTSTRAP.className(), Bootstrap.BOOTSTRAP.name(), Bootstrap.BOOTSTRAP.descriptor(), false); 175 176 /** Bootstrap for array populators */ 177 private static final Handle POPULATE_ARRAY_BOOTSTRAP = new Handle(H_INVOKESTATIC, RewriteException.BOOTSTRAP.className(), RewriteException.BOOTSTRAP.name(), RewriteException.BOOTSTRAP.descriptor(), false); 178 179 /** 180 * Constructor - internal use from ClassEmitter only 181 * @see ClassEmitter#method 182 * 183 * @param classEmitter the class emitter weaving the class this method is in 184 * @param method a method visitor 185 */ 186 MethodEmitter(final ClassEmitter classEmitter, final MethodVisitor method) { 187 this(classEmitter, method, null); 188 } 189 190 /** 191 * Constructor - internal use from ClassEmitter only 192 * @see ClassEmitter#method 193 * 194 * @param classEmitter the class emitter weaving the class this method is in 195 * @param method a method visitor 196 * @param functionNode a function node representing this method 197 */ 198 MethodEmitter(final ClassEmitter classEmitter, final MethodVisitor method, final FunctionNode functionNode) { 199 this.context = classEmitter.getContext(); 200 this.classEmitter = classEmitter; 201 this.method = method; 202 this.functionNode = functionNode; 203 this.stack = null; 204 this.log = context.getLogger(CodeGenerator.class); 205 this.debug = log.isEnabled(); 206 } 207 208 /** 209 * Begin a method 210 */ 211 public void begin() { 212 classEmitter.beginMethod(this); 213 newStack(); 214 method.visitCode(); 215 } 216 217 /** 218 * End a method 219 */ 220 public void end() { 221 method.visitMaxs(0, 0); 222 method.visitEnd(); 223 224 classEmitter.endMethod(this); 225 } 226 227 boolean isReachable() { 228 return stack != null; 229 } 230 231 private void doesNotContinueSequentially() { 232 stack = null; 233 } 234 235 private void newStack() { 236 stack = new Label.Stack(); 237 } 238 239 @Override 240 public String toString() { 241 return "methodEmitter: " + (functionNode == null ? method : functionNode.getName()).toString() + ' ' + Debug.id(this); 242 } 243 244 /** 245 * Push a type to the existing stack 246 * @param type the type 247 */ 248 void pushType(final Type type) { 249 if (type != null) { 250 stack.push(type); 251 } 252 } 253 254 /** 255 * Pop a type from the existing stack 256 * 257 * @param expected expected type - will assert if wrong 258 * 259 * @return the type that was retrieved 260 */ 261 private Type popType(final Type expected) { 262 final Type type = popType(); 263 assert type.isEquivalentTo(expected) : type + " is not compatible with " + expected; 264 return type; 265 } 266 267 /** 268 * Pop a type from the existing stack, no matter what it is. 269 * 270 * @return the type 271 */ 272 private Type popType() { 273 return stack.pop(); 274 } 275 276 /** 277 * Pop a type from the existing stack, ensuring that it is numeric. Boolean type is popped as int type. 278 * 279 * @return the type 280 */ 281 private NumericType popNumeric() { 282 final Type type = popType(); 283 if(type.isBoolean()) { 284 // Booleans are treated as int for purposes of arithmetic operations 285 return Type.INT; 286 } 287 assert type.isNumeric(); 288 return (NumericType)type; 289 } 290 291 /** 292 * Pop a type from the existing stack, ensuring that it is an integer type 293 * (integer or long). Boolean type is popped as int type. 294 * 295 * @return the type 296 */ 297 private BitwiseType popBitwise() { 298 final Type type = popType(); 299 if(type == Type.BOOLEAN) { 300 return Type.INT; 301 } 302 return (BitwiseType)type; 303 } 304 305 private BitwiseType popInteger() { 306 final Type type = popType(); 307 if(type == Type.BOOLEAN) { 308 return Type.INT; 309 } 310 assert type == Type.INT; 311 return (BitwiseType)type; 312 } 313 314 /** 315 * Pop a type from the existing stack, ensuring that it is an array type, 316 * assert if not 317 * 318 * @return the type 319 */ 320 private ArrayType popArray() { 321 final Type type = popType(); 322 assert type.isArray() : type; 323 return (ArrayType)type; 324 } 325 326 /** 327 * Peek a given number of slots from the top of the stack and return the 328 * type in that slot 329 * 330 * @param pos the number of positions from the top, 0 is the top element 331 * 332 * @return the type at position "pos" on the stack 333 */ 334 final Type peekType(final int pos) { 335 return stack.peek(pos); 336 } 337 338 /** 339 * Peek at the type at the top of the stack 340 * 341 * @return the type at the top of the stack 342 */ 343 final Type peekType() { 344 return stack.peek(); 345 } 346 347 /** 348 * Generate code a for instantiating a new object and push the 349 * object type on the stack 350 * 351 * @param classDescriptor class descriptor for the object type 352 * @param type the type of the new object 353 * 354 * @return the method emitter 355 */ 356 MethodEmitter _new(final String classDescriptor, final Type type) { 357 debug("new", classDescriptor); 358 method.visitTypeInsn(NEW, classDescriptor); 359 pushType(type); 360 return this; 361 } 362 363 /** 364 * Generate code a for instantiating a new object and push the 365 * object type on the stack 366 * 367 * @param clazz class type to instatiate 368 * 369 * @return the method emitter 370 */ 371 MethodEmitter _new(final Class<?> clazz) { 372 return _new(className(clazz), Type.typeFor(clazz)); 373 } 374 375 /** 376 * Generate code to call the empty constructor for a class 377 * 378 * @param clazz class type to instatiate 379 * 380 * @return the method emitter 381 */ 382 MethodEmitter newInstance(final Class<?> clazz) { 383 return invoke(constructorNoLookup(clazz)); 384 } 385 386 /** 387 * Perform a dup, that is, duplicate the top element and 388 * push the duplicate down a given number of positions 389 * on the stack. This is totally type agnostic. 390 * 391 * @param depth the depth on which to put the copy 392 * 393 * @return the method emitter, or null if depth is illegal and 394 * has no instruction equivalent. 395 */ 396 MethodEmitter dup(final int depth) { 397 if (peekType().dup(method, depth) == null) { 398 return null; 399 } 400 401 debug("dup", depth); 402 403 switch (depth) { 404 case 0: { 405 final int l0 = stack.getTopLocalLoad(); 406 pushType(peekType()); 407 stack.markLocalLoad(l0); 408 break; 409 } 410 case 1: { 411 final int l0 = stack.getTopLocalLoad(); 412 final Type p0 = popType(); 413 final int l1 = stack.getTopLocalLoad(); 414 final Type p1 = popType(); 415 pushType(p0); 416 stack.markLocalLoad(l0); 417 pushType(p1); 418 stack.markLocalLoad(l1); 419 pushType(p0); 420 stack.markLocalLoad(l0); 421 break; 422 } 423 case 2: { 424 final int l0 = stack.getTopLocalLoad(); 425 final Type p0 = popType(); 426 final int l1 = stack.getTopLocalLoad(); 427 final Type p1 = popType(); 428 final int l2 = stack.getTopLocalLoad(); 429 final Type p2 = popType(); 430 pushType(p0); 431 stack.markLocalLoad(l0); 432 pushType(p2); 433 stack.markLocalLoad(l2); 434 pushType(p1); 435 stack.markLocalLoad(l1); 436 pushType(p0); 437 stack.markLocalLoad(l0); 438 break; 439 } 440 default: 441 assert false : "illegal dup depth = " + depth; 442 return null; 443 } 444 445 return this; 446 } 447 448 /** 449 * Perform a dup2, that is, duplicate the top element if it 450 * is a category 2 type, or two top elements if they are category 451 * 1 types, and push them on top of the stack 452 * 453 * @return the method emitter 454 */ 455 MethodEmitter dup2() { 456 debug("dup2"); 457 458 if (peekType().isCategory2()) { 459 final int l0 = stack.getTopLocalLoad(); 460 pushType(peekType()); 461 stack.markLocalLoad(l0); 462 } else { 463 final int l0 = stack.getTopLocalLoad(); 464 final Type p0 = popType(); 465 final int l1 = stack.getTopLocalLoad(); 466 final Type p1 = popType(); 467 pushType(p0); 468 stack.markLocalLoad(l0); 469 pushType(p1); 470 stack.markLocalLoad(l1); 471 pushType(p0); 472 stack.markLocalLoad(l0); 473 pushType(p1); 474 stack.markLocalLoad(l1); 475 } 476 method.visitInsn(DUP2); 477 return this; 478 } 479 480 /** 481 * Duplicate the top element on the stack and push it 482 * 483 * @return the method emitter 484 */ 485 MethodEmitter dup() { 486 return dup(0); 487 } 488 489 /** 490 * Pop the top element of the stack and throw it away 491 * 492 * @return the method emitter 493 */ 494 MethodEmitter pop() { 495 debug("pop", peekType()); 496 popType().pop(method); 497 return this; 498 } 499 500 /** 501 * Pop the top element of the stack if category 2 type, or the two 502 * top elements of the stack if category 1 types 503 * 504 * @return the method emitter 505 */ 506 MethodEmitter pop2() { 507 if (peekType().isCategory2()) { 508 popType(); 509 } else { 510 get2n(); 511 } 512 return this; 513 } 514 515 /** 516 * Swap the top two elements of the stack. This is totally 517 * type agnostic and works for all types 518 * 519 * @return the method emitter 520 */ 521 MethodEmitter swap() { 522 debug("swap"); 523 524 final int l0 = stack.getTopLocalLoad(); 525 final Type p0 = popType(); 526 final int l1 = stack.getTopLocalLoad(); 527 final Type p1 = popType(); 528 p0.swap(method, p1); 529 530 pushType(p0); 531 stack.markLocalLoad(l0); 532 pushType(p1); 533 stack.markLocalLoad(l1); 534 return this; 535 } 536 537 void pack() { 538 final Type type = peekType(); 539 if (type.isInteger()) { 540 convert(PRIMITIVE_FIELD_TYPE); 541 } else if (type.isLong()) { 542 //nop 543 } else if (type.isNumber()) { 544 invokestatic("java/lang/Double", "doubleToRawLongBits", "(D)J"); 545 } else { 546 assert false : type + " cannot be packed!"; 547 } 548 } 549 550 /** 551 * Initializes a bytecode method parameter 552 * @param symbol the symbol for the parameter 553 * @param type the type of the parameter 554 * @param start the label for the start of the method 555 */ 556 void initializeMethodParameter(final Symbol symbol, final Type type, final Label start) { 557 assert symbol.isBytecodeLocal(); 558 localVariableDefs.put(symbol, new LocalVariableDef(start.getLabel(), type)); 559 } 560 561 /** 562 * Create a new string builder, call the constructor and push the instance to the stack. 563 * 564 * @return the method emitter 565 */ 566 MethodEmitter newStringBuilder() { 567 return invoke(constructorNoLookup(StringBuilder.class)).dup(); 568 } 569 570 /** 571 * Pop a string and a StringBuilder from the top of the stack and call the append 572 * function of the StringBuilder, appending the string. Pushes the StringBuilder to 573 * the stack when finished. 574 * 575 * @return the method emitter 576 */ 577 MethodEmitter stringBuilderAppend() { 578 convert(Type.STRING); 579 return invoke(virtualCallNoLookup(StringBuilder.class, "append", StringBuilder.class, String.class)); 580 } 581 582 /** 583 * Pops two integer types from the stack, performs a bitwise and and pushes 584 * the result 585 * 586 * @return the method emitter 587 */ 588 MethodEmitter and() { 589 debug("and"); 590 pushType(get2i().and(method)); 591 return this; 592 } 593 594 /** 595 * Pops two integer types from the stack, performs a bitwise or and pushes 596 * the result 597 * 598 * @return the method emitter 599 */ 600 MethodEmitter or() { 601 debug("or"); 602 pushType(get2i().or(method)); 603 return this; 604 } 605 606 /** 607 * Pops two integer types from the stack, performs a bitwise xor and pushes 608 * the result 609 * 610 * @return the method emitter 611 */ 612 MethodEmitter xor() { 613 debug("xor"); 614 pushType(get2i().xor(method)); 615 return this; 616 } 617 618 /** 619 * Pops two integer types from the stack, performs a bitwise logic shift right and pushes 620 * the result. The shift count, the first element, must be INT. 621 * 622 * @return the method emitter 623 */ 624 MethodEmitter shr() { 625 debug("shr"); 626 popInteger(); 627 pushType(popBitwise().shr(method)); 628 return this; 629 } 630 631 /** 632 * Pops two integer types from the stack, performs a bitwise shift left and and pushes 633 * the result. The shift count, the first element, must be INT. 634 * 635 * @return the method emitter 636 */ 637 MethodEmitter shl() { 638 debug("shl"); 639 popInteger(); 640 pushType(popBitwise().shl(method)); 641 return this; 642 } 643 644 /** 645 * Pops two integer types from the stack, performs a bitwise arithmetic shift right and pushes 646 * the result. The shift count, the first element, must be INT. 647 * 648 * @return the method emitter 649 */ 650 MethodEmitter sar() { 651 debug("sar"); 652 popInteger(); 653 pushType(popBitwise().sar(method)); 654 return this; 655 } 656 657 /** 658 * Pops a numeric type from the stack, negates it and pushes the result 659 * 660 * @return the method emitter 661 */ 662 MethodEmitter neg(final int programPoint) { 663 debug("neg"); 664 pushType(popNumeric().neg(method, programPoint)); 665 return this; 666 } 667 668 /** 669 * Add label for the start of a catch block and push the exception to the 670 * stack 671 * 672 * @param recovery label pointing to start of catch block 673 */ 674 void _catch(final Label recovery) { 675 // While in JVM a catch block can be reached through normal control flow, our code generator never does this, 676 // so we might as well presume there's no stack on entry. 677 assert stack == null; 678 recovery.onCatch(); 679 label(recovery); 680 beginCatchBlock(); 681 } 682 683 /** 684 * Add any number of labels for the start of a catch block and push the exception to the 685 * stack 686 * 687 * @param recoveries labels pointing to start of catch block 688 */ 689 void _catch(final Collection<Label> recoveries) { 690 assert stack == null; 691 for(final Label l: recoveries) { 692 label(l); 693 } 694 beginCatchBlock(); 695 } 696 697 private void beginCatchBlock() { 698 // It can happen that the catch label wasn't marked as reachable. They are marked as reachable if there's an 699 // assignment in the try block, but it's possible that there was none. 700 if(!isReachable()) { 701 newStack(); 702 } 703 pushType(Type.typeFor(Throwable.class)); 704 } 705 706 /** 707 * Start a try/catch block. 708 * 709 * @param entry start label for try 710 * @param exit end label for try 711 * @param recovery start label for catch 712 * @param clazz exception class or null for any Throwable 713 * @param isOptimismHandler true if this is a hander for {@code UnwarrantedOptimismException}. Normally joining on a 714 * catch handler kills temporary variables, but optimism handlers are an exception, as they need to capture 715 * temporaries as well, so they must remain live. 716 */ 717 void _try(final Label entry, final Label exit, final Label recovery, final Class<?> clazz, final boolean isOptimismHandler) { 718 recovery.joinFromTry(entry.getStack(), isOptimismHandler); 719 final String typeDescriptor = clazz == null ? null : CompilerConstants.className(clazz); 720 method.visitTryCatchBlock(entry.getLabel(), exit.getLabel(), recovery.getLabel(), typeDescriptor); 721 } 722 723 /** 724 * Start a try/catch block. 725 * 726 * @param entry start label for try 727 * @param exit end label for try 728 * @param recovery start label for catch 729 * @param clazz exception class 730 */ 731 void _try(final Label entry, final Label exit, final Label recovery, final Class<?> clazz) { 732 _try(entry, exit, recovery, clazz, clazz == UnwarrantedOptimismException.class); 733 } 734 735 /** 736 * Start a try/catch block. The catch is "Throwable" - i.e. catch-all 737 * 738 * @param entry start label for try 739 * @param exit end label for try 740 * @param recovery start label for catch 741 */ 742 void _try(final Label entry, final Label exit, final Label recovery) { 743 _try(entry, exit, recovery, null, false); 744 } 745 746 void markLabelAsOptimisticCatchHandler(final Label label, final int liveLocalCount) { 747 label.markAsOptimisticCatchHandler(stack, liveLocalCount); 748 } 749 750 /** 751 * Load the constants array 752 * @return this method emitter 753 */ 754 MethodEmitter loadConstants() { 755 getStatic(classEmitter.getUnitClassName(), CONSTANTS.symbolName(), CONSTANTS.descriptor()); 756 assert peekType().isArray() : peekType(); 757 return this; 758 } 759 760 /** 761 * Push the undefined value for the given type, i.e. 762 * UNDEFINED or UNDEFINEDNUMBER. Currently we have no way of 763 * representing UNDEFINED for INTs and LONGs, so they are not 764 * allowed to be local variables (yet) 765 * 766 * @param type the type for which to push UNDEFINED 767 * @return the method emitter 768 */ 769 MethodEmitter loadUndefined(final Type type) { 770 debug("load undefined ", type); 771 pushType(type.loadUndefined(method)); 772 return this; 773 } 774 775 MethodEmitter loadForcedInitializer(final Type type) { 776 debug("load forced initializer ", type); 777 pushType(type.loadForcedInitializer(method)); 778 return this; 779 } 780 781 /** 782 * Push the empty value for the given type, i.e. EMPTY. 783 * 784 * @param type the type 785 * @return the method emitter 786 */ 787 MethodEmitter loadEmpty(final Type type) { 788 debug("load empty ", type); 789 pushType(type.loadEmpty(method)); 790 return this; 791 } 792 793 /** 794 * Push null to stack 795 * 796 * @return the method emitter 797 */ 798 MethodEmitter loadNull() { 799 debug("aconst_null"); 800 pushType(Type.OBJECT.ldc(method, null)); 801 return this; 802 } 803 804 /** 805 * Push a handle representing this class top stack 806 * 807 * @param className name of the class 808 * 809 * @return the method emitter 810 */ 811 MethodEmitter loadType(final String className) { 812 debug("load type", className); 813 method.visitLdcInsn(jdk.internal.org.objectweb.asm.Type.getObjectType(className)); 814 pushType(Type.OBJECT); 815 return this; 816 } 817 818 /** 819 * Push a boolean constant to the stack. 820 * 821 * @param b value of boolean 822 * 823 * @return the method emitter 824 */ 825 MethodEmitter load(final boolean b) { 826 debug("load boolean", b); 827 pushType(Type.BOOLEAN.ldc(method, b)); 828 return this; 829 } 830 831 /** 832 * Push an int constant to the stack 833 * 834 * @param i value of the int 835 * 836 * @return the method emitter 837 */ 838 MethodEmitter load(final int i) { 839 debug("load int", i); 840 pushType(Type.INT.ldc(method, i)); 841 return this; 842 } 843 844 /** 845 * Push a double constant to the stack 846 * 847 * @param d value of the double 848 * 849 * @return the method emitter 850 */ 851 MethodEmitter load(final double d) { 852 debug("load double", d); 853 pushType(Type.NUMBER.ldc(method, d)); 854 return this; 855 } 856 857 /** 858 * Push an long constant to the stack 859 * 860 * @param l value of the long 861 * 862 * @return the method emitter 863 */ 864 MethodEmitter load(final long l) { 865 debug("load long", l); 866 pushType(Type.LONG.ldc(method, l)); 867 return this; 868 } 869 870 /** 871 * Fetch the length of an array. 872 * @return Array length. 873 */ 874 MethodEmitter arraylength() { 875 debug("arraylength"); 876 popType(Type.OBJECT); 877 pushType(Type.OBJECT_ARRAY.arraylength(method)); 878 return this; 879 } 880 881 /** 882 * Push a String constant to the stack 883 * 884 * @param s value of the String 885 * 886 * @return the method emitter 887 */ 888 MethodEmitter load(final String s) { 889 debug("load string", s); 890 891 if (s == null) { 892 loadNull(); 893 return this; 894 } 895 896 //NASHORN-142 - split too large string 897 final int length = s.length(); 898 if (length > LARGE_STRING_THRESHOLD) { 899 900 _new(StringBuilder.class); 901 dup(); 902 load(length); 903 invoke(constructorNoLookup(StringBuilder.class, int.class)); 904 905 for (int n = 0; n < length; n += LARGE_STRING_THRESHOLD) { 906 final String part = s.substring(n, Math.min(n + LARGE_STRING_THRESHOLD, length)); 907 load(part); 908 stringBuilderAppend(); 909 } 910 911 invoke(virtualCallNoLookup(StringBuilder.class, "toString", String.class)); 912 913 return this; 914 } 915 916 pushType(Type.OBJECT.ldc(method, s)); 917 return this; 918 } 919 920 /** 921 * Pushes the value of an identifier to the stack. If the identifier does not represent a local variable or a 922 * parameter, this will be a no-op. 923 * 924 * @param ident the identifier for the variable being loaded. 925 * 926 * @return the method emitter 927 */ 928 MethodEmitter load(final IdentNode ident) { 929 return load(ident.getSymbol(), ident.getType()); 930 } 931 932 /** 933 * Pushes the value of the symbol to the stack with the specified type. No type conversion is being performed, and 934 * the type is only being used if the symbol addresses a local variable slot. The value of the symbol is loaded if 935 * it addresses a local variable slot, or it is a parameter (in which case it can also be loaded from a vararg array 936 * or the arguments object). If it is neither, the operation is a no-op. 937 * 938 * @param symbol the symbol addressing the value being loaded 939 * @param type the presumed type of the value when it is loaded from a local variable slot 940 * @return the method emitter 941 */ 942 MethodEmitter load(final Symbol symbol, final Type type) { 943 assert symbol != null; 944 if (symbol.hasSlot()) { 945 final int slot = symbol.getSlot(type); 946 debug("load symbol", symbol.getName(), " slot=", slot, "type=", type); 947 load(type, slot); 948 // _try(new Label("dummy"), new Label("dummy2"), recovery); 949 // method.visitTryCatchBlock(new Label(), arg1, arg2, arg3); 950 } else if (symbol.isParam()) { 951 assert functionNode.isVarArg() : "Non-vararg functions have slotted parameters"; 952 final int index = symbol.getFieldIndex(); 953 if (functionNode.needsArguments()) { 954 // ScriptObject.getArgument(int) on arguments 955 debug("load symbol", symbol.getName(), " arguments index=", index); 956 loadCompilerConstant(ARGUMENTS); 957 load(index); 958 ScriptObject.GET_ARGUMENT.invoke(this); 959 } else { 960 // array load from __varargs__ 961 debug("load symbol", symbol.getName(), " array index=", index); 962 loadCompilerConstant(VARARGS); 963 load(symbol.getFieldIndex()); 964 arrayload(); 965 } 966 } 967 return this; 968 } 969 970 /** 971 * Push a local variable to the stack, given an explicit bytecode slot. 972 * This is used e.g. for stub generation where we know where items like 973 * "this" and "scope" reside. 974 * 975 * @param type the type of the variable 976 * @param slot the slot the variable is in 977 * 978 * @return the method emitter 979 */ 980 MethodEmitter load(final Type type, final int slot) { 981 debug("explicit load", type, slot); 982 final Type loadType = type.load(method, slot); 983 assert loadType != null; 984 pushType(loadType == Type.OBJECT && isThisSlot(slot) ? Type.THIS : loadType); 985 assert !preventUndefinedLoad || (slot < stack.localVariableTypes.size() && stack.localVariableTypes.get(slot) != Type.UNKNOWN) 986 : "Attempted load of uninitialized slot " + slot + " (as type " + type + ")"; 987 stack.markLocalLoad(slot); 988 return this; 989 } 990 991 private boolean isThisSlot(final int slot) { 992 if (functionNode == null) { 993 return slot == CompilerConstants.JAVA_THIS.slot(); 994 } 995 final int thisSlot = getCompilerConstantSymbol(THIS).getSlot(Type.OBJECT); 996 assert !functionNode.needsCallee() || thisSlot == 1; // needsCallee -> thisSlot == 1 997 assert functionNode.needsCallee() || thisSlot == 0; // !needsCallee -> thisSlot == 0 998 return slot == thisSlot; 999 } 1000 1001 /** 1002 * Push a method handle to the stack 1003 * 1004 * @param className class name 1005 * @param methodName method name 1006 * @param descName descriptor 1007 * @param flags flags that describe this handle, e.g. invokespecial new, or invoke virtual 1008 * 1009 * @return the method emitter 1010 */ 1011 MethodEmitter loadHandle(final String className, final String methodName, final String descName, final EnumSet<Flag> flags) { 1012 final int flag = Flag.getValue(flags); 1013 debug("load handle "); 1014 pushType(Type.OBJECT.ldc(method, new Handle(flag, className, methodName, descName, flag == H_INVOKEINTERFACE))); 1015 return this; 1016 } 1017 1018 private Symbol getCompilerConstantSymbol(final CompilerConstants cc) { 1019 return functionNode.getBody().getExistingSymbol(cc.symbolName()); 1020 } 1021 1022 /** 1023 * True if this method has a slot allocated for the scope variable (meaning, something in the method actually needs 1024 * the scope). 1025 * @return if this method has a slot allocated for the scope variable. 1026 */ 1027 boolean hasScope() { 1028 return getCompilerConstantSymbol(SCOPE).hasSlot(); 1029 } 1030 1031 MethodEmitter loadCompilerConstant(final CompilerConstants cc) { 1032 return loadCompilerConstant(cc, null); 1033 } 1034 1035 MethodEmitter loadCompilerConstant(final CompilerConstants cc, final Type type) { 1036 if (cc == SCOPE && peekType() == Type.SCOPE) { 1037 dup(); 1038 return this; 1039 } 1040 return load(getCompilerConstantSymbol(cc), type != null ? type : getCompilerConstantType(cc)); 1041 } 1042 1043 MethodEmitter loadScope() { 1044 return loadCompilerConstant(SCOPE).checkcast(Scope.class); 1045 } 1046 1047 MethodEmitter setSplitState(final int state) { 1048 return loadScope().load(state).invoke(Scope.SET_SPLIT_STATE); 1049 } 1050 1051 void storeCompilerConstant(final CompilerConstants cc) { 1052 storeCompilerConstant(cc, null); 1053 } 1054 1055 void storeCompilerConstant(final CompilerConstants cc, final Type type) { 1056 final Symbol symbol = getCompilerConstantSymbol(cc); 1057 if(!symbol.hasSlot()) { 1058 return; 1059 } 1060 debug("store compiler constant ", symbol); 1061 store(symbol, type != null ? type : getCompilerConstantType(cc)); 1062 } 1063 1064 private static Type getCompilerConstantType(final CompilerConstants cc) { 1065 final Class<?> constantType = cc.type(); 1066 assert constantType != null; 1067 return Type.typeFor(constantType); 1068 } 1069 1070 /** 1071 * Load an element from an array, determining type automatically 1072 * @return the method emitter 1073 */ 1074 MethodEmitter arrayload() { 1075 debug("Xaload"); 1076 popType(Type.INT); 1077 pushType(popArray().aload(method)); 1078 return this; 1079 } 1080 1081 /** 1082 * Pop a value, an index and an array from the stack and store 1083 * the value at the given index in the array. 1084 */ 1085 void arraystore() { 1086 debug("Xastore"); 1087 final Type value = popType(); 1088 final Type index = popType(Type.INT); 1089 assert index.isInteger() : "array index is not integer, but " + index; 1090 final ArrayType array = popArray(); 1091 1092 assert value.isEquivalentTo(array.getElementType()) : "Storing "+value+" into "+array; 1093 assert array.isObject(); 1094 array.astore(method); 1095 } 1096 1097 /** 1098 * Pop a value from the stack and store it in a local variable represented 1099 * by the given identifier. If the symbol has no slot, this is a NOP 1100 * 1101 * @param ident identifier to store stack to 1102 */ 1103 void store(final IdentNode ident) { 1104 final Type type = ident.getType(); 1105 final Symbol symbol = ident.getSymbol(); 1106 if(type == Type.UNDEFINED) { 1107 assert peekType() == Type.UNDEFINED; 1108 store(symbol, Type.OBJECT); 1109 } else { 1110 store(symbol, type); 1111 } 1112 } 1113 1114 /** 1115 * Represents a definition of a local variable with a type. Used for local variable table building. 1116 */ 1117 private static class LocalVariableDef { 1118 // The start label from where this definition lives. 1119 private final jdk.internal.org.objectweb.asm.Label label; 1120 // The currently live type of the local variable. 1121 private final Type type; 1122 1123 LocalVariableDef(final jdk.internal.org.objectweb.asm.Label label, final Type type) { 1124 this.label = label; 1125 this.type = type; 1126 } 1127 1128 } 1129 1130 void closeLocalVariable(final Symbol symbol, final Label label) { 1131 final LocalVariableDef def = localVariableDefs.get(symbol); 1132 if(def != null) { 1133 endLocalValueDef(symbol, def, label.getLabel()); 1134 } 1135 if(isReachable()) { 1136 markDeadLocalVariable(symbol); 1137 } 1138 } 1139 1140 void markDeadLocalVariable(final Symbol symbol) { 1141 if(!symbol.isDead()) { 1142 markDeadSlots(symbol.getFirstSlot(), symbol.slotCount()); 1143 } 1144 } 1145 1146 void markDeadSlots(final int firstSlot, final int slotCount) { 1147 stack.markDeadLocalVariables(firstSlot, slotCount); 1148 } 1149 1150 private void endLocalValueDef(final Symbol symbol, final LocalVariableDef def, final jdk.internal.org.objectweb.asm.Label label) { 1151 String name = symbol.getName(); 1152 if (name.equals(THIS.symbolName())) { 1153 name = THIS_DEBUGGER.symbolName(); 1154 } 1155 method.visitLocalVariable(name, def.type.getDescriptor(), null, def.label, label, symbol.getSlot(def.type)); 1156 } 1157 1158 void store(final Symbol symbol, final Type type) { 1159 store(symbol, type, true); 1160 } 1161 1162 /** 1163 * Pop a value from the stack and store it in a variable denoted by the given symbol. The variable should be either 1164 * a local variable, or a function parameter (and not a scoped variable). For local variables, this method will also 1165 * do the bookkeeping of the local variable table as well as mark values in all alternative slots for the symbol as 1166 * dead. In this regard it differs from {@link #storeHidden(Type, int)}. 1167 * 1168 * @param symbol the symbol to store into. 1169 * @param type the type to store 1170 * @param onlySymbolLiveValue if true, this is the sole live value for the symbol. If false, currently live values should 1171 * be kept live. 1172 */ 1173 void store(final Symbol symbol, final Type type, final boolean onlySymbolLiveValue) { 1174 assert symbol != null : "No symbol to store"; 1175 if (symbol.hasSlot()) { 1176 final boolean isLiveType = symbol.hasSlotFor(type); 1177 final LocalVariableDef existingDef = localVariableDefs.get(symbol); 1178 if(existingDef == null || existingDef.type != type) { 1179 final jdk.internal.org.objectweb.asm.Label here = new jdk.internal.org.objectweb.asm.Label(); 1180 if(isLiveType) { 1181 final LocalVariableDef newDef = new LocalVariableDef(here, type); 1182 localVariableDefs.put(symbol, newDef); 1183 } 1184 method.visitLabel(here); 1185 if(existingDef != null) { 1186 endLocalValueDef(symbol, existingDef, here); 1187 } 1188 } 1189 if(isLiveType) { 1190 final int slot = symbol.getSlot(type); 1191 debug("store symbol", symbol.getName(), " type=", type, " slot=", slot); 1192 storeHidden(type, slot, onlySymbolLiveValue); 1193 } else { 1194 if(onlySymbolLiveValue) { 1195 markDeadLocalVariable(symbol); 1196 } 1197 debug("dead store symbol ", symbol.getName(), " type=", type); 1198 pop(); 1199 } 1200 } else if (symbol.isParam()) { 1201 assert !symbol.isScope(); 1202 assert functionNode.isVarArg() : "Non-vararg functions have slotted parameters"; 1203 final int index = symbol.getFieldIndex(); 1204 if (functionNode.needsArguments()) { 1205 convert(Type.OBJECT); 1206 debug("store symbol", symbol.getName(), " arguments index=", index); 1207 loadCompilerConstant(ARGUMENTS); 1208 load(index); 1209 ArgumentSetter.SET_ARGUMENT.invoke(this); 1210 } else { 1211 convert(Type.OBJECT); 1212 // varargs without arguments object - just do array store to __varargs__ 1213 debug("store symbol", symbol.getName(), " array index=", index); 1214 loadCompilerConstant(VARARGS); 1215 load(index); 1216 ArgumentSetter.SET_ARRAY_ELEMENT.invoke(this); 1217 } 1218 } else { 1219 debug("dead store symbol ", symbol.getName(), " type=", type); 1220 pop(); 1221 } 1222 } 1223 1224 /** 1225 * Pop a value from the stack and store it in a local variable slot. Note that in contrast with 1226 * {@link #store(Symbol, Type)}, this method does not adjust the local variable table, nor marks slots for 1227 * alternative value types for the symbol as being dead. For that reason, this method is usually not called 1228 * directly. Notable exceptions are temporary internal locals (e.g. quick store, last-catch-condition, etc.) that 1229 * are not desired to show up in the local variable table. 1230 * 1231 * @param type the type to pop 1232 * @param slot the slot 1233 */ 1234 void storeHidden(final Type type, final int slot) { 1235 storeHidden(type, slot, true); 1236 } 1237 1238 void storeHidden(final Type type, final int slot, final boolean onlyLiveSymbolValue) { 1239 explicitStore(type, slot); 1240 stack.onLocalStore(type, slot, onlyLiveSymbolValue); 1241 } 1242 1243 void storeTemp(final Type type, final int slot) { 1244 explicitStore(type, slot); 1245 defineTemporaryLocalVariable(slot, slot + type.getSlots()); 1246 onLocalStore(type, slot); 1247 } 1248 1249 void onLocalStore(final Type type, final int slot) { 1250 stack.onLocalStore(type, slot, true); 1251 } 1252 1253 private void explicitStore(final Type type, final int slot) { 1254 assert slot != -1; 1255 debug("explicit store", type, slot); 1256 popType(type); 1257 type.store(method, slot); 1258 } 1259 1260 /** 1261 * Marks a range of slots as belonging to a defined local variable. The slots will start out with no live value 1262 * in them. 1263 * @param fromSlot first slot, inclusive. 1264 * @param toSlot last slot, exclusive. 1265 */ 1266 void defineBlockLocalVariable(final int fromSlot, final int toSlot) { 1267 stack.defineBlockLocalVariable(fromSlot, toSlot); 1268 } 1269 1270 /** 1271 * Marks a range of slots as belonging to a defined temporary local variable. The slots will start out with no 1272 * live value in them. 1273 * @param fromSlot first slot, inclusive. 1274 * @param toSlot last slot, exclusive. 1275 */ 1276 void defineTemporaryLocalVariable(final int fromSlot, final int toSlot) { 1277 stack.defineTemporaryLocalVariable(fromSlot, toSlot); 1278 } 1279 1280 /** 1281 * Defines a new temporary local variable and returns its allocated index. 1282 * @param width the required width (in slots) for the new variable. 1283 * @return the bytecode slot index where the newly allocated local begins. 1284 */ 1285 int defineTemporaryLocalVariable(final int width) { 1286 return stack.defineTemporaryLocalVariable(width); 1287 } 1288 1289 void undefineLocalVariables(final int fromSlot, final boolean canTruncateSymbol) { 1290 if(isReachable()) { 1291 stack.undefineLocalVariables(fromSlot, canTruncateSymbol); 1292 } 1293 } 1294 1295 List<Type> getLocalVariableTypes() { 1296 return stack.localVariableTypes; 1297 } 1298 1299 List<Type> getWidestLiveLocals(final List<Type> localTypes) { 1300 return stack.getWidestLiveLocals(localTypes); 1301 } 1302 1303 String markSymbolBoundariesInLvarTypesDescriptor(final String lvarDescriptor) { 1304 return stack.markSymbolBoundariesInLvarTypesDescriptor(lvarDescriptor); 1305 } 1306 1307 /** 1308 * Increment/Decrement a local integer by the given value. 1309 * 1310 * @param slot the int slot 1311 * @param increment the amount to increment 1312 */ 1313 void iinc(final int slot, final int increment) { 1314 debug("iinc"); 1315 method.visitIincInsn(slot, increment); 1316 } 1317 1318 /** 1319 * Pop an exception object from the stack and generate code 1320 * for throwing it 1321 */ 1322 public void athrow() { 1323 debug("athrow"); 1324 final Type receiver = popType(Type.OBJECT); 1325 assert Throwable.class.isAssignableFrom(receiver.getTypeClass()) : receiver.getTypeClass(); 1326 method.visitInsn(ATHROW); 1327 doesNotContinueSequentially(); 1328 } 1329 1330 /** 1331 * Pop an object from the stack and perform an instanceof 1332 * operation, given a classDescriptor to compare it to. 1333 * Push the boolean result 1/0 as an int to the stack 1334 * 1335 * @param classDescriptor descriptor of the class to type check against 1336 * 1337 * @return the method emitter 1338 */ 1339 MethodEmitter _instanceof(final String classDescriptor) { 1340 debug("instanceof", classDescriptor); 1341 popType(Type.OBJECT); 1342 method.visitTypeInsn(INSTANCEOF, classDescriptor); 1343 pushType(Type.INT); 1344 return this; 1345 } 1346 1347 /** 1348 * Pop an object from the stack and perform an instanceof 1349 * operation, given a classDescriptor to compare it to. 1350 * Push the boolean result 1/0 as an int to the stack 1351 * 1352 * @param clazz the type to check instanceof against 1353 * 1354 * @return the method emitter 1355 */ 1356 MethodEmitter _instanceof(final Class<?> clazz) { 1357 return _instanceof(CompilerConstants.className(clazz)); 1358 } 1359 1360 /** 1361 * Perform a checkcast operation on the object at the top of the 1362 * stack. 1363 * 1364 * @param classDescriptor descriptor of the class to type check against 1365 * 1366 * @return the method emitter 1367 */ 1368 MethodEmitter checkcast(final String classDescriptor) { 1369 debug("checkcast", classDescriptor); 1370 assert peekType().isObject(); 1371 method.visitTypeInsn(CHECKCAST, classDescriptor); 1372 return this; 1373 } 1374 1375 /** 1376 * Perform a checkcast operation on the object at the top of the 1377 * stack. 1378 * 1379 * @param clazz class to checkcast against 1380 * 1381 * @return the method emitter 1382 */ 1383 MethodEmitter checkcast(final Class<?> clazz) { 1384 return checkcast(CompilerConstants.className(clazz)); 1385 } 1386 1387 /** 1388 * Instantiate a new array given a length that is popped 1389 * from the stack and the array type 1390 * 1391 * @param arrayType the type of the array 1392 * 1393 * @return the method emitter 1394 */ 1395 MethodEmitter newarray(final ArrayType arrayType) { 1396 debug("newarray ", "arrayType=", arrayType); 1397 popType(Type.INT); //LENGTH 1398 pushType(arrayType.newarray(method)); 1399 return this; 1400 } 1401 1402 /** 1403 * Instantiate a multidimensional array with a given number of dimensions. 1404 * On the stack are dim lengths of the sub arrays. 1405 * 1406 * @param arrayType type of the array 1407 * @param dims number of dimensions 1408 * 1409 * @return the method emitter 1410 */ 1411 MethodEmitter multinewarray(final ArrayType arrayType, final int dims) { 1412 debug("multianewarray ", arrayType, dims); 1413 for (int i = 0; i < dims; i++) { 1414 popType(Type.INT); //LENGTH 1415 } 1416 pushType(arrayType.newarray(method, dims)); 1417 return this; 1418 } 1419 1420 /** 1421 * Helper function to pop and type check the appropriate arguments 1422 * from the stack given a method signature 1423 * 1424 * @param signature method signature 1425 * 1426 * @return return type of method 1427 */ 1428 private Type fixParamStack(final String signature) { 1429 final Type[] params = Type.getMethodArguments(signature); 1430 for (int i = params.length - 1; i >= 0; i--) { 1431 popType(params[i]); 1432 } 1433 final Type returnType = Type.getMethodReturnType(signature); 1434 return returnType; 1435 } 1436 1437 /** 1438 * Generate an invocation to a Call structure 1439 * @see CompilerConstants 1440 * 1441 * @param call the call object 1442 * 1443 * @return the method emitter 1444 */ 1445 MethodEmitter invoke(final Call call) { 1446 return call.invoke(this); 1447 } 1448 1449 private MethodEmitter invoke(final int opcode, final String className, final String methodName, final String methodDescriptor, final boolean hasReceiver) { 1450 final Type returnType = fixParamStack(methodDescriptor); 1451 1452 if (hasReceiver) { 1453 popType(Type.OBJECT); 1454 } 1455 1456 method.visitMethodInsn(opcode, className, methodName, methodDescriptor, opcode == INVOKEINTERFACE); 1457 1458 if (returnType != null) { 1459 pushType(returnType); 1460 } 1461 1462 return this; 1463 } 1464 1465 /** 1466 * Pop receiver from stack, perform an invoke special 1467 * 1468 * @param className class name 1469 * @param methodName method name 1470 * @param methodDescriptor descriptor 1471 * 1472 * @return the method emitter 1473 */ 1474 MethodEmitter invokespecial(final String className, final String methodName, final String methodDescriptor) { 1475 debug("invokespecial", className, ".", methodName, methodDescriptor); 1476 return invoke(INVOKESPECIAL, className, methodName, methodDescriptor, true); 1477 } 1478 1479 /** 1480 * Pop receiver from stack, perform an invoke virtual, push return value if any 1481 * 1482 * @param className class name 1483 * @param methodName method name 1484 * @param methodDescriptor descriptor 1485 * 1486 * @return the method emitter 1487 */ 1488 MethodEmitter invokevirtual(final String className, final String methodName, final String methodDescriptor) { 1489 debug("invokevirtual", className, ".", methodName, methodDescriptor, " ", stack); 1490 return invoke(INVOKEVIRTUAL, className, methodName, methodDescriptor, true); 1491 } 1492 1493 /** 1494 * Perform an invoke static and push the return value if any 1495 * 1496 * @param className class name 1497 * @param methodName method name 1498 * @param methodDescriptor descriptor 1499 * 1500 * @return the method emitter 1501 */ 1502 MethodEmitter invokestatic(final String className, final String methodName, final String methodDescriptor) { 1503 debug("invokestatic", className, ".", methodName, methodDescriptor); 1504 invoke(INVOKESTATIC, className, methodName, methodDescriptor, false); 1505 return this; 1506 } 1507 1508 /** 1509 * Perform an invoke static and replace the return type if we know better, e.g. Global.allocate 1510 * that allocates an array should return an ObjectArray type as a NativeArray counts as that 1511 * 1512 * @param className class name 1513 * @param methodName method name 1514 * @param methodDescriptor descriptor 1515 * @param returnType return type override 1516 * 1517 * @return the method emitter 1518 */ 1519 MethodEmitter invokestatic(final String className, final String methodName, final String methodDescriptor, final Type returnType) { 1520 invokestatic(className, methodName, methodDescriptor); 1521 popType(); 1522 pushType(returnType); 1523 return this; 1524 } 1525 1526 /** 1527 * Pop receiver from stack, perform an invoke interface and push return value if any 1528 * 1529 * @param className class name 1530 * @param methodName method name 1531 * @param methodDescriptor descriptor 1532 * 1533 * @return the method emitter 1534 */ 1535 MethodEmitter invokeinterface(final String className, final String methodName, final String methodDescriptor) { 1536 debug("invokeinterface", className, ".", methodName, methodDescriptor); 1537 return invoke(INVOKEINTERFACE, className, methodName, methodDescriptor, true); 1538 } 1539 1540 static jdk.internal.org.objectweb.asm.Label[] getLabels(final Label... table) { 1541 final jdk.internal.org.objectweb.asm.Label[] internalLabels = new jdk.internal.org.objectweb.asm.Label[table.length]; 1542 for (int i = 0; i < table.length; i++) { 1543 internalLabels[i] = table[i].getLabel(); 1544 } 1545 return internalLabels; 1546 } 1547 1548 /** 1549 * Generate a lookup switch, popping the switch value from the stack 1550 * 1551 * @param defaultLabel default label 1552 * @param values case values for the table 1553 * @param table default label 1554 */ 1555 void lookupswitch(final Label defaultLabel, final int[] values, final Label... table) {//Collection<Label> table) { 1556 debug("lookupswitch", peekType()); 1557 adjustStackForSwitch(defaultLabel, table); 1558 method.visitLookupSwitchInsn(defaultLabel.getLabel(), values, getLabels(table)); 1559 doesNotContinueSequentially(); 1560 } 1561 1562 /** 1563 * Generate a table switch 1564 * @param lo low value 1565 * @param hi high value 1566 * @param defaultLabel default label 1567 * @param table label table 1568 */ 1569 void tableswitch(final int lo, final int hi, final Label defaultLabel, final Label... table) { 1570 debug("tableswitch", peekType()); 1571 adjustStackForSwitch(defaultLabel, table); 1572 method.visitTableSwitchInsn(lo, hi, defaultLabel.getLabel(), getLabels(table)); 1573 doesNotContinueSequentially(); 1574 } 1575 1576 private void adjustStackForSwitch(final Label defaultLabel, final Label... table) { 1577 popType(Type.INT); 1578 joinTo(defaultLabel); 1579 for(final Label label: table) { 1580 joinTo(label); 1581 } 1582 } 1583 1584 /** 1585 * Abstraction for performing a conditional jump of any type 1586 * 1587 * @see Condition 1588 * 1589 * @param cond the condition to test 1590 * @param trueLabel the destination label is condition is true 1591 */ 1592 void conditionalJump(final Condition cond, final Label trueLabel) { 1593 conditionalJump(cond, cond != Condition.GT && cond != Condition.GE, trueLabel); 1594 } 1595 1596 /** 1597 * Abstraction for performing a conditional jump of any type, 1598 * including a dcmpg/dcmpl semantic for doubles. 1599 * 1600 * @param cond the condition to test 1601 * @param isCmpG is this a dcmpg for numbers, false if it's a dcmpl 1602 * @param trueLabel the destination label if condition is true 1603 */ 1604 void conditionalJump(final Condition cond, final boolean isCmpG, final Label trueLabel) { 1605 if (peekType().isCategory2()) { 1606 debug("[ld]cmp isCmpG=", isCmpG); 1607 pushType(get2n().cmp(method, isCmpG)); 1608 jump(Condition.toUnary(cond), trueLabel, 1); 1609 } else { 1610 debug("if", cond); 1611 jump(Condition.toBinary(cond, peekType().isObject()), trueLabel, 2); 1612 } 1613 } 1614 1615 /** 1616 * Perform a non void return, popping the type from the stack 1617 * 1618 * @param type the type for the return 1619 */ 1620 void _return(final Type type) { 1621 debug("return", type); 1622 assert stack.size() == 1 : "Only return value on stack allowed at return point - depth=" + stack.size() + " stack = " + stack; 1623 final Type stackType = peekType(); 1624 if (!Type.areEquivalent(type, stackType)) { 1625 convert(type); 1626 } 1627 popType(type)._return(method); 1628 doesNotContinueSequentially(); 1629 } 1630 1631 /** 1632 * Perform a return using the stack top value as the guide for the type 1633 */ 1634 void _return() { 1635 _return(peekType()); 1636 } 1637 1638 /** 1639 * Perform a void return. 1640 */ 1641 void returnVoid() { 1642 debug("return [void]"); 1643 assert stack.isEmpty() : stack; 1644 method.visitInsn(RETURN); 1645 doesNotContinueSequentially(); 1646 } 1647 1648 /** 1649 * Perform a comparison of two number types that are popped from the stack 1650 * 1651 * @param isCmpG is this a dcmpg semantic, false if it's a dcmpl semantic 1652 * 1653 * @return the method emitter 1654 */ 1655 MethodEmitter cmp(final boolean isCmpG) { 1656 pushType(get2n().cmp(method, isCmpG)); 1657 return this; 1658 } 1659 1660 /** 1661 * Helper function for jumps, conditional or not 1662 * @param opcode opcode for jump 1663 * @param label destination 1664 * @param n elements on stack to compare, 0-2 1665 */ 1666 private void jump(final int opcode, final Label label, final int n) { 1667 for (int i = 0; i < n; i++) { 1668 assert peekType().isInteger() || peekType().isBoolean() || peekType().isObject() : "expecting integer type or object for jump, but found " + peekType(); 1669 popType(); 1670 } 1671 joinTo(label); 1672 method.visitJumpInsn(opcode, label.getLabel()); 1673 } 1674 1675 /** 1676 * Generate an if_acmpeq 1677 * 1678 * @param label label to true case 1679 */ 1680 void if_acmpeq(final Label label) { 1681 debug("if_acmpeq", label); 1682 jump(IF_ACMPEQ, label, 2); 1683 } 1684 1685 /** 1686 * Generate an if_acmpne 1687 * 1688 * @param label label to true case 1689 */ 1690 void if_acmpne(final Label label) { 1691 debug("if_acmpne", label); 1692 jump(IF_ACMPNE, label, 2); 1693 } 1694 1695 /** 1696 * Generate an ifnull 1697 * 1698 * @param label label to true case 1699 */ 1700 void ifnull(final Label label) { 1701 debug("ifnull", label); 1702 jump(IFNULL, label, 1); 1703 } 1704 1705 /** 1706 * Generate an ifnonnull 1707 * 1708 * @param label label to true case 1709 */ 1710 void ifnonnull(final Label label) { 1711 debug("ifnonnull", label); 1712 jump(IFNONNULL, label, 1); 1713 } 1714 1715 /** 1716 * Generate an ifeq 1717 * 1718 * @param label label to true case 1719 */ 1720 void ifeq(final Label label) { 1721 debug("ifeq ", label); 1722 jump(IFEQ, label, 1); 1723 } 1724 1725 /** 1726 * Generate an if_icmpeq 1727 * 1728 * @param label label to true case 1729 */ 1730 void if_icmpeq(final Label label) { 1731 debug("if_icmpeq", label); 1732 jump(IF_ICMPEQ, label, 2); 1733 } 1734 1735 /** 1736 * Generate an if_ne 1737 * 1738 * @param label label to true case 1739 */ 1740 void ifne(final Label label) { 1741 debug("ifne", label); 1742 jump(IFNE, label, 1); 1743 } 1744 1745 /** 1746 * Generate an if_icmpne 1747 * 1748 * @param label label to true case 1749 */ 1750 void if_icmpne(final Label label) { 1751 debug("if_icmpne", label); 1752 jump(IF_ICMPNE, label, 2); 1753 } 1754 1755 /** 1756 * Generate an iflt 1757 * 1758 * @param label label to true case 1759 */ 1760 void iflt(final Label label) { 1761 debug("iflt", label); 1762 jump(IFLT, label, 1); 1763 } 1764 1765 /** 1766 * Generate an if_icmplt 1767 * 1768 * @param label label to true case 1769 */ 1770 void if_icmplt(final Label label) { 1771 debug("if_icmplt", label); 1772 jump(IF_ICMPLT, label, 2); 1773 } 1774 1775 /** 1776 * Generate an ifle 1777 * 1778 * @param label label to true case 1779 */ 1780 void ifle(final Label label) { 1781 debug("ifle", label); 1782 jump(IFLE, label, 1); 1783 } 1784 1785 /** 1786 * Generate an if_icmple 1787 * 1788 * @param label label to true case 1789 */ 1790 void if_icmple(final Label label) { 1791 debug("if_icmple", label); 1792 jump(IF_ICMPLE, label, 2); 1793 } 1794 1795 /** 1796 * Generate an ifgt 1797 * 1798 * @param label label to true case 1799 */ 1800 void ifgt(final Label label) { 1801 debug("ifgt", label); 1802 jump(IFGT, label, 1); 1803 } 1804 1805 /** 1806 * Generate an if_icmpgt 1807 * 1808 * @param label label to true case 1809 */ 1810 void if_icmpgt(final Label label) { 1811 debug("if_icmpgt", label); 1812 jump(IF_ICMPGT, label, 2); 1813 } 1814 1815 /** 1816 * Generate an ifge 1817 * 1818 * @param label label to true case 1819 */ 1820 void ifge(final Label label) { 1821 debug("ifge", label); 1822 jump(IFGE, label, 1); 1823 } 1824 1825 /** 1826 * Generate an if_icmpge 1827 * 1828 * @param label label to true case 1829 */ 1830 void if_icmpge(final Label label) { 1831 debug("if_icmpge", label); 1832 jump(IF_ICMPGE, label, 2); 1833 } 1834 1835 /** 1836 * Unconditional jump to a label 1837 * 1838 * @param label destination label 1839 */ 1840 void _goto(final Label label) { 1841 debug("goto", label); 1842 jump(GOTO, label, 0); 1843 doesNotContinueSequentially(); //whoever reaches the point after us provides the stack, because we don't 1844 } 1845 1846 /** 1847 * Unconditional jump to the start label of a loop. It differs from ordinary {@link #_goto(Label)} in that it will 1848 * preserve the current label stack, as the next instruction after the goto is loop body that the loop will come 1849 * back to. Also used to jump at the start label of the continuation handler, as it behaves much like a loop test in 1850 * the sense that after it is evaluated, it also jumps backwards. 1851 * 1852 * @param loopStart start label of a loop 1853 */ 1854 void gotoLoopStart(final Label loopStart) { 1855 debug("goto (loop)", loopStart); 1856 jump(GOTO, loopStart, 0); 1857 } 1858 1859 /** 1860 * Unconditional jump without any control flow and data flow testing. You should not normally use this method when 1861 * generating code, except if you're very sure that you know what you're doing. Normally only used for the 1862 * admittedly torturous control flow of continuation handler plumbing. 1863 * @param target the target of the jump 1864 */ 1865 void uncheckedGoto(final Label target) { 1866 method.visitJumpInsn(GOTO, target.getLabel()); 1867 } 1868 1869 /** 1870 * Potential transfer of control to a catch block. 1871 * 1872 * @param catchLabel destination catch label 1873 */ 1874 void canThrow(final Label catchLabel) { 1875 catchLabel.joinFromTry(stack, false); 1876 } 1877 1878 /** 1879 * A join in control flow - helper function that makes sure all entry stacks 1880 * discovered for the join point so far are equivalent 1881 * 1882 * MergeStack: we are about to enter a label. If its stack, label.getStack() is null 1883 * we have never been here before. Then we are expected to carry a stack with us. 1884 * 1885 * @param label label 1886 */ 1887 private void joinTo(final Label label) { 1888 assert isReachable(); 1889 label.joinFrom(stack); 1890 } 1891 1892 /** 1893 * Register a new label, enter it here. 1894 * @param label 1895 */ 1896 void label(final Label label) { 1897 breakLabel(label, -1); 1898 } 1899 1900 /** 1901 * Register a new break target label, enter it here. 1902 * 1903 * @param label the label 1904 * @param liveLocals the number of live locals at this label 1905 */ 1906 void breakLabel(final Label label, final int liveLocals) { 1907 if (!isReachable()) { 1908 // If we emit a label, and the label's stack is null, it must not be reachable. 1909 assert (label.getStack() == null) != label.isReachable(); 1910 } else { 1911 joinTo(label); 1912 } 1913 // Use label's stack as we might have no stack. 1914 final Label.Stack labelStack = label.getStack(); 1915 stack = labelStack == null ? null : labelStack.clone(); 1916 if(stack != null && label.isBreakTarget() && liveLocals != -1) { 1917 // This has to be done because we might not have another frame to provide us with its firstTemp if the label 1918 // is only reachable through a break or continue statement; also in this case, the frame can actually 1919 // give us a higher number of live locals, e.g. if it comes from a catch. Typical example: 1920 // for(;;) { try{ throw 0; } catch(e) { break; } }. 1921 // Since the for loop can only be exited through the break in the catch block, it'll bring with it the 1922 // "e" as a live local, and we need to trim it off here. 1923 assert stack.firstTemp >= liveLocals; 1924 stack.firstTemp = liveLocals; 1925 } 1926 debug_label(label); 1927 method.visitLabel(label.getLabel()); 1928 } 1929 1930 /** 1931 * Pop element from stack, convert to given type 1932 * 1933 * @param to type to convert to 1934 * 1935 * @return the method emitter 1936 */ 1937 MethodEmitter convert(final Type to) { 1938 final Type from = peekType(); 1939 final Type type = from.convert(method, to); 1940 if (type != null) { 1941 if (!from.isEquivalentTo(to)) { 1942 debug("convert", from, "->", to); 1943 } 1944 if (type != from) { 1945 final int l0 = stack.getTopLocalLoad(); 1946 popType(); 1947 pushType(type); 1948 // NOTE: conversions from a primitive type are considered to preserve the "load" property of the value 1949 // on the stack. Otherwise we could introduce temporary locals in a deoptimized rest-of (e.g. doing an 1950 // "i < x.length" where "i" is int and ".length" gets deoptimized to long would end up converting i to 1951 // long with "ILOAD i; I2L; LSTORE tmp; LLOAD tmp;"). Such additional temporary would cause an error 1952 // when restoring the state of the function for rest-of execution, as the not-yet deoptimized variant 1953 // would have the (now invalidated) assumption that "x.length" is an int, so it wouldn't have the I2L, 1954 // and therefore neither the subsequent LSTORE tmp; LLOAD tmp;. By making sure conversions from a 1955 // primitive type don't erase the "load" information, we don't introduce temporaries in the deoptimized 1956 // rest-of that didn't exist in the more optimistic version that triggered the deoptimization. 1957 // NOTE: as a more general observation, we could theoretically track the operations required to 1958 // reproduce any stack value as long as they are all local loads, constant loads, and stack operations. 1959 // We won't go there in the current system 1960 if(!from.isObject()) { 1961 stack.markLocalLoad(l0); 1962 } 1963 } 1964 } 1965 return this; 1966 } 1967 1968 /** 1969 * Helper function - expect two types that are equivalent 1970 * 1971 * @return common type 1972 */ 1973 private Type get2() { 1974 final Type p0 = popType(); 1975 final Type p1 = popType(); 1976 assert p0.isEquivalentTo(p1) : "expecting equivalent types on stack but got " + p0 + " and " + p1; 1977 return p0; 1978 } 1979 1980 /** 1981 * Helper function - expect two types that are integer types and equivalent 1982 * 1983 * @return common type 1984 */ 1985 private BitwiseType get2i() { 1986 final BitwiseType p0 = popBitwise(); 1987 final BitwiseType p1 = popBitwise(); 1988 assert p0.isEquivalentTo(p1) : "expecting equivalent types on stack but got " + p0 + " and " + p1; 1989 return p0; 1990 } 1991 1992 /** 1993 * Helper function - expect two types that are numbers and equivalent 1994 * 1995 * @return common type 1996 */ 1997 private NumericType get2n() { 1998 final NumericType p0 = popNumeric(); 1999 final NumericType p1 = popNumeric(); 2000 assert p0.isEquivalentTo(p1) : "expecting equivalent types on stack but got " + p0 + " and " + p1; 2001 return p0; 2002 } 2003 2004 /** 2005 * Pop two numbers, perform addition and push result 2006 * 2007 * @return the method emitter 2008 */ 2009 MethodEmitter add(final int programPoint) { 2010 debug("add"); 2011 pushType(get2().add(method, programPoint)); 2012 return this; 2013 } 2014 2015 /** 2016 * Pop two numbers, perform subtraction and push result 2017 * 2018 * @return the method emitter 2019 */ 2020 MethodEmitter sub(final int programPoint) { 2021 debug("sub"); 2022 pushType(get2n().sub(method, programPoint)); 2023 return this; 2024 } 2025 2026 /** 2027 * Pop two numbers, perform multiplication and push result 2028 * 2029 * @return the method emitter 2030 */ 2031 MethodEmitter mul(final int programPoint) { 2032 debug("mul "); 2033 pushType(get2n().mul(method, programPoint)); 2034 return this; 2035 } 2036 2037 /** 2038 * Pop two numbers, perform division and push result 2039 * 2040 * @return the method emitter 2041 */ 2042 MethodEmitter div(final int programPoint) { 2043 debug("div"); 2044 pushType(get2n().div(method, programPoint)); 2045 return this; 2046 } 2047 2048 /** 2049 * Pop two numbers, calculate remainder and push result 2050 * 2051 * @return the method emitter 2052 */ 2053 MethodEmitter rem(final int programPoint) { 2054 debug("rem"); 2055 pushType(get2n().rem(method, programPoint)); 2056 return this; 2057 } 2058 2059 /** 2060 * Retrieve the top <code>count</code> types on the stack without modifying it. 2061 * 2062 * @param count number of types to return 2063 * @return array of Types 2064 */ 2065 protected Type[] getTypesFromStack(final int count) { 2066 return stack.getTopTypes(count); 2067 } 2068 2069 int[] getLocalLoadsOnStack(final int from, final int to) { 2070 return stack.getLocalLoads(from, to); 2071 } 2072 2073 int getStackSize() { 2074 return stack.size(); 2075 } 2076 2077 int getFirstTemp() { 2078 return stack.firstTemp; 2079 } 2080 2081 int getUsedSlotsWithLiveTemporaries() { 2082 return stack.getUsedSlotsWithLiveTemporaries(); 2083 } 2084 2085 /** 2086 * Helper function to generate a function signature based on stack contents 2087 * and argument count and return type 2088 * 2089 * @param returnType return type 2090 * @param argCount argument count 2091 * 2092 * @return function signature for stack contents 2093 */ 2094 private String getDynamicSignature(final Type returnType, final int argCount) { 2095 final Type[] paramTypes = new Type[argCount]; 2096 2097 int pos = 0; 2098 for (int i = argCount - 1; i >= 0; i--) { 2099 Type pt = stack.peek(pos++); 2100 // "erase" specific ScriptObject subtype info - except for NativeArray. 2101 // NativeArray is used for array/List/Deque conversion for Java calls. 2102 if (ScriptObject.class.isAssignableFrom(pt.getTypeClass()) && 2103 !NativeArray.class.isAssignableFrom(pt.getTypeClass())) { 2104 pt = Type.SCRIPT_OBJECT; 2105 } 2106 paramTypes[i] = pt; 2107 } 2108 final String descriptor = Type.getMethodDescriptor(returnType, paramTypes); 2109 for (int i = 0; i < argCount; i++) { 2110 popType(paramTypes[argCount - i - 1]); 2111 } 2112 2113 return descriptor; 2114 } 2115 2116 MethodEmitter invalidateSpecialName(final String name) { 2117 switch (name) { 2118 case "apply": 2119 case "call": 2120 debug("invalidate_name", "name=", name); 2121 load("Function"); 2122 invoke(ScriptRuntime.INVALIDATE_RESERVED_BUILTIN_NAME); 2123 break; 2124 default: 2125 break; 2126 } 2127 return this; 2128 } 2129 2130 /** 2131 * Generate a dynamic new 2132 * 2133 * @param argCount number of arguments 2134 * @param flags callsite flags 2135 * 2136 * @return the method emitter 2137 */ 2138 MethodEmitter dynamicNew(final int argCount, final int flags) { 2139 return dynamicNew(argCount, flags, null); 2140 } 2141 2142 /** 2143 * Generate a dynamic new 2144 * 2145 * @param argCount number of arguments 2146 * @param flags callsite flags 2147 * @param msg additional message to be used when reporting error 2148 * 2149 * @return the method emitter 2150 */ 2151 MethodEmitter dynamicNew(final int argCount, final int flags, final String msg) { 2152 assert !isOptimistic(flags); 2153 debug("dynamic_new", "argcount=", argCount); 2154 final String signature = getDynamicSignature(Type.OBJECT, argCount); 2155 method.visitInvokeDynamicInsn( 2156 msg != null && msg.length() < LARGE_STRING_THRESHOLD? NameCodec.encode(msg) : EMPTY_NAME, 2157 signature, LINKERBOOTSTRAP, flags | NashornCallSiteDescriptor.NEW); 2158 pushType(Type.OBJECT); //TODO fix result type 2159 return this; 2160 } 2161 2162 /** 2163 * Generate a dynamic call 2164 * 2165 * @param returnType return type 2166 * @param argCount number of arguments 2167 * @param flags callsite flags 2168 * 2169 * @return the method emitter 2170 */ 2171 MethodEmitter dynamicCall(final Type returnType, final int argCount, final int flags) { 2172 return dynamicCall(returnType, argCount, flags, null); 2173 } 2174 2175 /** 2176 * Generate a dynamic call 2177 * 2178 * @param returnType return type 2179 * @param argCount number of arguments 2180 * @param flags callsite flags 2181 * @param msg additional message to be used when reporting error 2182 * 2183 * @return the method emitter 2184 */ 2185 MethodEmitter dynamicCall(final Type returnType, final int argCount, final int flags, final String msg) { 2186 debug("dynamic_call", "args=", argCount, "returnType=", returnType); 2187 final String signature = getDynamicSignature(returnType, argCount); // +1 because the function itself is the 1st parameter for dynamic calls (what you call - call target) 2188 debug(" signature", signature); 2189 method.visitInvokeDynamicInsn( 2190 msg != null && msg.length() < LARGE_STRING_THRESHOLD? NameCodec.encode(msg) : EMPTY_NAME, 2191 signature, LINKERBOOTSTRAP, flags | NashornCallSiteDescriptor.CALL); 2192 pushType(returnType); 2193 2194 return this; 2195 } 2196 2197 MethodEmitter dynamicArrayPopulatorCall(final int argCount, final int startIndex) { 2198 debug("populate_array", "args=", argCount, "startIndex=", startIndex); 2199 final String signature = getDynamicSignature(Type.OBJECT_ARRAY, argCount); 2200 method.visitInvokeDynamicInsn("populateArray", signature, POPULATE_ARRAY_BOOTSTRAP, startIndex); 2201 pushType(Type.OBJECT_ARRAY); 2202 return this; 2203 } 2204 2205 /** 2206 * Generate dynamic getter. Pop object from stack. Push result. 2207 * 2208 * @param valueType type of the value to set 2209 * @param name name of property 2210 * @param flags call site flags 2211 * @param isMethod should it prefer retrieving methods 2212 * @param isIndex is this an index operation? 2213 * @return the method emitter 2214 */ 2215 MethodEmitter dynamicGet(final Type valueType, final String name, final int flags, final boolean isMethod, final boolean isIndex) { 2216 if (name.length() > LARGE_STRING_THRESHOLD) { // use getIndex for extremely long names 2217 return load(name).dynamicGetIndex(valueType, flags, isMethod); 2218 } 2219 2220 debug("dynamic_get", name, valueType, getProgramPoint(flags)); 2221 2222 Type type = valueType; 2223 if (type.isObject() || type.isBoolean()) { 2224 type = Type.OBJECT; //promote e.g strings to object generic setter 2225 } 2226 2227 popType(Type.OBJECT); 2228 method.visitInvokeDynamicInsn(NameCodec.encode(name), 2229 Type.getMethodDescriptor(type, Type.OBJECT), LINKERBOOTSTRAP, flags | dynGetOperation(isMethod, isIndex)); 2230 2231 pushType(type); 2232 convert(valueType); //most probably a nop 2233 2234 return this; 2235 } 2236 2237 /** 2238 * Generate dynamic setter. Pop receiver and property from stack. 2239 * 2240 * @param name name of property 2241 * @param flags call site flags 2242 * @param isIndex is this an index operation? 2243 */ 2244 void dynamicSet(final String name, final int flags, final boolean isIndex) { 2245 if (name.length() > LARGE_STRING_THRESHOLD) { // use setIndex for extremely long names 2246 load(name).swap().dynamicSetIndex(flags); 2247 return; 2248 } 2249 2250 assert !isOptimistic(flags); 2251 debug("dynamic_set", name, peekType()); 2252 2253 Type type = peekType(); 2254 if (type.isObject() || type.isBoolean()) { //promote strings to objects etc 2255 type = Type.OBJECT; 2256 convert(Type.OBJECT); //TODO bad- until we specialize boolean setters, 2257 } 2258 popType(type); 2259 popType(Type.OBJECT); 2260 2261 method.visitInvokeDynamicInsn(NameCodec.encode(name), 2262 methodDescriptor(void.class, Object.class, type.getTypeClass()), LINKERBOOTSTRAP, flags | dynSetOperation(isIndex)); 2263 } 2264 2265 /** 2266 * Generate dynamic remover. Pop object from stack. Push result. 2267 * 2268 * @param name name of property 2269 * @param flags call site flags 2270 * @return the method emitter 2271 */ 2272 MethodEmitter dynamicRemove(final String name, final int flags, final boolean isIndex) { 2273 if (name.length() > LARGE_STRING_THRESHOLD) { // use removeIndex for extremely long names 2274 return load(name).dynamicRemoveIndex(flags); 2275 } 2276 2277 debug("dynamic_remove", name, Type.BOOLEAN, getProgramPoint(flags)); 2278 2279 popType(Type.OBJECT); 2280 // Type is widened to OBJECT then coerced back to BOOLEAN 2281 method.visitInvokeDynamicInsn(NameCodec.encode(name), 2282 Type.getMethodDescriptor(Type.OBJECT, Type.OBJECT), LINKERBOOTSTRAP, flags | dynRemoveOperation(isIndex)); 2283 2284 pushType(Type.OBJECT); 2285 convert(Type.BOOLEAN); //most probably a nop 2286 2287 return this; 2288 } 2289 2290 /** 2291 * Dynamic getter for indexed structures. Pop index and receiver from stack, 2292 * generate appropriate signatures based on types 2293 * 2294 * @param result result type for getter 2295 * @param flags call site flags for getter 2296 * @param isMethod should it prefer retrieving methods 2297 * 2298 * @return the method emitter 2299 */ 2300 MethodEmitter dynamicGetIndex(final Type result, final int flags, final boolean isMethod) { 2301 assert result.getTypeClass().isPrimitive() || result.getTypeClass() == Object.class; 2302 debug("dynamic_get_index", peekType(1), "[", peekType(), "]", getProgramPoint(flags)); 2303 2304 Type resultType = result; 2305 if (result.isBoolean()) { 2306 resultType = Type.OBJECT; // INT->OBJECT to avoid another dimension of cross products in the getters. TODO 2307 } 2308 2309 Type index = peekType(); 2310 if (index.isObject() || index.isBoolean()) { 2311 index = Type.OBJECT; //e.g. string->object 2312 convert(Type.OBJECT); 2313 } 2314 popType(); 2315 2316 popType(Type.OBJECT); 2317 2318 final String signature = Type.getMethodDescriptor(resultType, Type.OBJECT /*e.g STRING->OBJECT*/, index); 2319 2320 method.visitInvokeDynamicInsn(EMPTY_NAME, signature, LINKERBOOTSTRAP, flags | dynGetOperation(isMethod, true)); 2321 pushType(resultType); 2322 2323 if (result.isBoolean()) { 2324 convert(Type.BOOLEAN); 2325 } 2326 2327 return this; 2328 } 2329 2330 private static String getProgramPoint(final int flags) { 2331 if((flags & CALLSITE_OPTIMISTIC) == 0) { 2332 return ""; 2333 } 2334 return "pp=" + String.valueOf((flags & (-1 << CALLSITE_PROGRAM_POINT_SHIFT)) >> CALLSITE_PROGRAM_POINT_SHIFT); 2335 } 2336 2337 /** 2338 * Dynamic setter for indexed structures. Pop value, index and receiver from 2339 * stack, generate appropriate signature based on types 2340 * 2341 * @param flags call site flags for setter 2342 */ 2343 void dynamicSetIndex(final int flags) { 2344 assert !isOptimistic(flags); 2345 debug("dynamic_set_index", peekType(2), "[", peekType(1), "] =", peekType()); 2346 2347 Type value = peekType(); 2348 if (value.isObject() || value.isBoolean()) { 2349 value = Type.OBJECT; //e.g. STRING->OBJECT - one descriptor for all object types 2350 convert(Type.OBJECT); 2351 } 2352 popType(); 2353 2354 Type index = peekType(); 2355 if (index.isObject() || index.isBoolean()) { 2356 index = Type.OBJECT; //e.g. string->object 2357 convert(Type.OBJECT); 2358 } 2359 popType(index); 2360 2361 final Type receiver = popType(Type.OBJECT); 2362 assert receiver.isObject(); 2363 2364 method.visitInvokeDynamicInsn(EMPTY_NAME, 2365 methodDescriptor(void.class, receiver.getTypeClass(), index.getTypeClass(), value.getTypeClass()), 2366 LINKERBOOTSTRAP, flags | NashornCallSiteDescriptor.SET_ELEMENT); 2367 } 2368 2369 /** 2370 * Dynamic remover for indexed structures. Pop index and receiver from stack, 2371 * generate appropriate signatures based on types 2372 * 2373 * @param flags call site flags for getter 2374 * 2375 * @return the method emitter 2376 */ 2377 MethodEmitter dynamicRemoveIndex(final int flags) { 2378 debug("dynamic_remove_index", peekType(1), "[", peekType(), "]", getProgramPoint(flags)); 2379 2380 Type index = peekType(); 2381 if (index.isObject() || index.isBoolean()) { 2382 index = Type.OBJECT; //e.g. string->object 2383 convert(Type.OBJECT); 2384 } 2385 popType(); 2386 2387 popType(Type.OBJECT); 2388 2389 final String signature = Type.getMethodDescriptor(Type.OBJECT, Type.OBJECT /*e.g STRING->OBJECT*/, index); 2390 2391 method.visitInvokeDynamicInsn(EMPTY_NAME, signature, LINKERBOOTSTRAP, flags | dynRemoveOperation(true)); 2392 pushType(Type.OBJECT); 2393 convert(Type.BOOLEAN); 2394 2395 return this; 2396 } 2397 2398 /** 2399 * Load a key value in the proper form. 2400 * 2401 * @param key 2402 */ 2403 //TODO move this and break it apart 2404 MethodEmitter loadKey(final Object key) { 2405 if (key instanceof IdentNode) { 2406 method.visitLdcInsn(((IdentNode) key).getName()); 2407 } else if (key instanceof LiteralNode) { 2408 method.visitLdcInsn(((LiteralNode<?>)key).getString()); 2409 } else { 2410 method.visitLdcInsn(JSType.toString(key)); 2411 } 2412 pushType(Type.OBJECT); //STRING 2413 return this; 2414 } 2415 2416 @SuppressWarnings("fallthrough") 2417 private static Type fieldType(final String desc) { 2418 switch (desc) { 2419 case "Z": 2420 case "B": 2421 case "C": 2422 case "S": 2423 case "I": 2424 return Type.INT; 2425 case "F": 2426 assert false; 2427 case "D": 2428 return Type.NUMBER; 2429 case "J": 2430 return Type.LONG; 2431 default: 2432 assert desc.startsWith("[") || desc.startsWith("L") : desc + " is not an object type"; 2433 switch (desc.charAt(0)) { 2434 case 'L': 2435 return Type.OBJECT; 2436 case '[': 2437 return Type.typeFor(Array.newInstance(fieldType(desc.substring(1)).getTypeClass(), 0).getClass()); 2438 default: 2439 assert false; 2440 } 2441 return Type.OBJECT; 2442 } 2443 } 2444 2445 /** 2446 * Generate get for a field access 2447 * 2448 * @param fa the field access 2449 * 2450 * @return the method emitter 2451 */ 2452 MethodEmitter getField(final FieldAccess fa) { 2453 return fa.get(this); 2454 } 2455 2456 /** 2457 * Generate set for a field access 2458 * 2459 * @param fa the field access 2460 */ 2461 void putField(final FieldAccess fa) { 2462 fa.put(this); 2463 } 2464 2465 /** 2466 * Get the value of a non-static field, pop the receiver from the stack, 2467 * push value to the stack 2468 * 2469 * @param className class 2470 * @param fieldName field name 2471 * @param fieldDescriptor field descriptor 2472 * 2473 * @return the method emitter 2474 */ 2475 MethodEmitter getField(final String className, final String fieldName, final String fieldDescriptor) { 2476 debug("getfield", "receiver=", peekType(), className, ".", fieldName, fieldDescriptor); 2477 final Type receiver = popType(); 2478 assert receiver.isObject(); 2479 method.visitFieldInsn(GETFIELD, className, fieldName, fieldDescriptor); 2480 pushType(fieldType(fieldDescriptor)); 2481 return this; 2482 } 2483 2484 /** 2485 * Get the value of a static field, push it to the stack 2486 * 2487 * @param className class 2488 * @param fieldName field name 2489 * @param fieldDescriptor field descriptor 2490 * 2491 * @return the method emitter 2492 */ 2493 MethodEmitter getStatic(final String className, final String fieldName, final String fieldDescriptor) { 2494 debug("getstatic", className, ".", fieldName, ".", fieldDescriptor); 2495 method.visitFieldInsn(GETSTATIC, className, fieldName, fieldDescriptor); 2496 pushType(fieldType(fieldDescriptor)); 2497 return this; 2498 } 2499 2500 /** 2501 * Pop value and field from stack and write to a non-static field 2502 * 2503 * @param className class 2504 * @param fieldName field name 2505 * @param fieldDescriptor field descriptor 2506 */ 2507 void putField(final String className, final String fieldName, final String fieldDescriptor) { 2508 debug("putfield", "receiver=", peekType(1), "value=", peekType()); 2509 popType(fieldType(fieldDescriptor)); 2510 popType(Type.OBJECT); 2511 method.visitFieldInsn(PUTFIELD, className, fieldName, fieldDescriptor); 2512 } 2513 2514 /** 2515 * Pop value from stack and write to a static field 2516 * 2517 * @param className class 2518 * @param fieldName field name 2519 * @param fieldDescriptor field descriptor 2520 */ 2521 void putStatic(final String className, final String fieldName, final String fieldDescriptor) { 2522 debug("putfield", "value=", peekType()); 2523 popType(fieldType(fieldDescriptor)); 2524 method.visitFieldInsn(PUTSTATIC, className, fieldName, fieldDescriptor); 2525 } 2526 2527 /** 2528 * Register line number at a label 2529 * 2530 * @param line line number 2531 */ 2532 void lineNumber(final int line) { 2533 if (context.getEnv()._debug_lines) { 2534 debug_label("[LINE]", line); 2535 final jdk.internal.org.objectweb.asm.Label l = new jdk.internal.org.objectweb.asm.Label(); 2536 method.visitLabel(l); 2537 method.visitLineNumber(line, l); 2538 } 2539 } 2540 2541 void beforeJoinPoint(final JoinPredecessor joinPredecessor) { 2542 LocalVariableConversion next = joinPredecessor.getLocalVariableConversion(); 2543 while(next != null) { 2544 final Symbol symbol = next.getSymbol(); 2545 if(next.isLive()) { 2546 emitLocalVariableConversion(next, true); 2547 } else { 2548 markDeadLocalVariable(symbol); 2549 } 2550 next = next.getNext(); 2551 } 2552 } 2553 2554 void beforeTry(final TryNode tryNode, final Label recovery) { 2555 LocalVariableConversion next = tryNode.getLocalVariableConversion(); 2556 while(next != null) { 2557 if(next.isLive()) { 2558 final Type to = emitLocalVariableConversion(next, false); 2559 recovery.getStack().onLocalStore(to, next.getSymbol().getSlot(to), true); 2560 } 2561 next = next.getNext(); 2562 } 2563 } 2564 2565 private static int dynGetOperation(final boolean isMethod, final boolean isIndex) { 2566 if (isMethod) { 2567 return isIndex ? NashornCallSiteDescriptor.GET_METHOD_ELEMENT : NashornCallSiteDescriptor.GET_METHOD_PROPERTY; 2568 } else { 2569 return isIndex ? NashornCallSiteDescriptor.GET_ELEMENT : NashornCallSiteDescriptor.GET_PROPERTY; 2570 } 2571 } 2572 2573 private static int dynSetOperation(final boolean isIndex) { 2574 return isIndex ? NashornCallSiteDescriptor.SET_ELEMENT : NashornCallSiteDescriptor.SET_PROPERTY; 2575 } 2576 2577 private static int dynRemoveOperation(final boolean isIndex) { 2578 return isIndex ? NashornCallSiteDescriptor.REMOVE_ELEMENT : NashornCallSiteDescriptor.REMOVE_PROPERTY; 2579 } 2580 2581 private Type emitLocalVariableConversion(final LocalVariableConversion conversion, final boolean onlySymbolLiveValue) { 2582 final Type from = conversion.getFrom(); 2583 final Type to = conversion.getTo(); 2584 final Symbol symbol = conversion.getSymbol(); 2585 assert symbol.isBytecodeLocal(); 2586 if(from == Type.UNDEFINED) { 2587 loadUndefined(to); 2588 } else { 2589 load(symbol, from).convert(to); 2590 } 2591 store(symbol, to, onlySymbolLiveValue); 2592 return to; 2593 } 2594 2595 /* 2596 * Debugging below 2597 */ 2598 2599 private final FieldAccess ERR_STREAM = staticField(System.class, "err", PrintStream.class); 2600 private final Call PRINT = virtualCallNoLookup(PrintStream.class, "print", void.class, Object.class); 2601 private final Call PRINTLN = virtualCallNoLookup(PrintStream.class, "println", void.class, Object.class); 2602 private final Call PRINT_STACKTRACE = virtualCallNoLookup(Throwable.class, "printStackTrace", void.class); 2603 2604 /** 2605 * Emit a System.err.print statement of whatever is on top of the bytecode stack 2606 */ 2607 void print() { 2608 getField(ERR_STREAM); 2609 swap(); 2610 convert(Type.OBJECT); 2611 invoke(PRINT); 2612 } 2613 2614 /** 2615 * Emit a System.err.println statement of whatever is on top of the bytecode stack 2616 */ 2617 void println() { 2618 getField(ERR_STREAM); 2619 swap(); 2620 convert(Type.OBJECT); 2621 invoke(PRINTLN); 2622 } 2623 2624 /** 2625 * Emit a System.err.print statement 2626 * @param string string to print 2627 */ 2628 void print(final String string) { 2629 getField(ERR_STREAM); 2630 load(string); 2631 invoke(PRINT); 2632 } 2633 2634 /** 2635 * Emit a System.err.println statement 2636 * @param string string to print 2637 */ 2638 void println(final String string) { 2639 getField(ERR_STREAM); 2640 load(string); 2641 invoke(PRINTLN); 2642 } 2643 2644 /** 2645 * Print a stacktrace to S 2646 */ 2647 void stacktrace() { 2648 _new(Throwable.class); 2649 dup(); 2650 invoke(constructorNoLookup(Throwable.class)); 2651 invoke(PRINT_STACKTRACE); 2652 } 2653 2654 private static int linePrefix = 0; 2655 2656 /** 2657 * Debug function that outputs generated bytecode and stack contents 2658 * 2659 * @param args debug information to print 2660 */ 2661 @SuppressWarnings("unused") 2662 private void debug(final Object... args) { 2663 if (debug) { 2664 debug(30, args); 2665 } 2666 } 2667 2668 private void debug(final String arg) { 2669 if (debug) { 2670 debug(30, arg); 2671 } 2672 } 2673 2674 private void debug(final Object arg0, final Object arg1) { 2675 if (debug) { 2676 debug(30, new Object[] { arg0, arg1 }); 2677 } 2678 } 2679 2680 private void debug(final Object arg0, final Object arg1, final Object arg2) { 2681 if (debug) { 2682 debug(30, new Object[] { arg0, arg1, arg2 }); 2683 } 2684 } 2685 2686 private void debug(final Object arg0, final Object arg1, final Object arg2, final Object arg3) { 2687 if (debug) { 2688 debug(30, new Object[] { arg0, arg1, arg2, arg3 }); 2689 } 2690 } 2691 2692 private void debug(final Object arg0, final Object arg1, final Object arg2, final Object arg3, final Object arg4) { 2693 if (debug) { 2694 debug(30, new Object[] { arg0, arg1, arg2, arg3, arg4 }); 2695 } 2696 } 2697 2698 private void debug(final Object arg0, final Object arg1, final Object arg2, final Object arg3, final Object arg4, final Object arg5) { 2699 if (debug) { 2700 debug(30, new Object[] { arg0, arg1, arg2, arg3, arg4, arg5 }); 2701 } 2702 } 2703 2704 private void debug(final Object arg0, final Object arg1, final Object arg2, final Object arg3, final Object arg4, final Object arg5, final Object arg6) { 2705 if (debug) { 2706 debug(30, new Object[] { arg0, arg1, arg2, arg3, arg4, arg5, arg6 }); 2707 } 2708 } 2709 2710 /** 2711 * Debug function that outputs generated bytecode and stack contents 2712 * for a label - indentation is currently the only thing that differs 2713 * 2714 * @param args debug information to print 2715 */ 2716 private void debug_label(final Object... args) { 2717 if (debug) { 2718 debug(22, args); 2719 } 2720 } 2721 2722 private void debug(final int padConstant, final Object... args) { 2723 if (debug) { 2724 final StringBuilder sb = new StringBuilder(); 2725 int pad; 2726 2727 sb.append('#'); 2728 sb.append(++linePrefix); 2729 2730 pad = 5 - sb.length(); 2731 while (pad > 0) { 2732 sb.append(' '); 2733 pad--; 2734 } 2735 2736 if (isReachable() && !stack.isEmpty()) { 2737 sb.append("{"); 2738 sb.append(stack.size()); 2739 sb.append(":"); 2740 for (int pos = 0; pos < stack.size(); pos++) { 2741 final Type t = stack.peek(pos); 2742 2743 if (t == Type.SCOPE) { 2744 sb.append("scope"); 2745 } else if (t == Type.THIS) { 2746 sb.append("this"); 2747 } else if (t.isObject()) { 2748 String desc = t.getDescriptor(); 2749 int i; 2750 for (i = 0; desc.charAt(i) == '[' && i < desc.length(); i++) { 2751 sb.append('['); 2752 } 2753 desc = desc.substring(i); 2754 final int slash = desc.lastIndexOf('/'); 2755 if (slash != -1) { 2756 desc = desc.substring(slash + 1, desc.length() - 1); 2757 } 2758 if ("Object".equals(desc)) { 2759 sb.append('O'); 2760 } else { 2761 sb.append(desc); 2762 } 2763 } else { 2764 sb.append(t.getDescriptor()); 2765 } 2766 final int loadIndex = stack.localLoads[stack.sp - 1 - pos]; 2767 if(loadIndex != Label.Stack.NON_LOAD) { 2768 sb.append('(').append(loadIndex).append(')'); 2769 } 2770 if (pos + 1 < stack.size()) { 2771 sb.append(' '); 2772 } 2773 } 2774 sb.append('}'); 2775 sb.append(' '); 2776 } 2777 2778 pad = padConstant - sb.length(); 2779 while (pad > 0) { 2780 sb.append(' '); 2781 pad--; 2782 } 2783 2784 for (final Object arg : args) { 2785 sb.append(arg); 2786 sb.append(' '); 2787 } 2788 2789 if (context.getEnv() != null) { //early bootstrap code doesn't have inited context yet 2790 log.info(sb); 2791 if (DEBUG_TRACE_LINE == linePrefix) { 2792 new Throwable().printStackTrace(log.getOutputStream()); 2793 } 2794 } 2795 } 2796 } 2797 2798 /** 2799 * Set the current function node being emitted 2800 * @param functionNode the function node 2801 */ 2802 void setFunctionNode(final FunctionNode functionNode) { 2803 this.functionNode = functionNode; 2804 } 2805 2806 /** 2807 * Invoke to enforce assertions preventing load from a local variable slot that's known to not have been written to. 2808 * Used by CodeGenerator, as it strictly enforces tracking of stores. Simpler uses of MethodEmitter, e.g. those 2809 * for creating initializers for structure classes, array getters, etc. don't have strict tracking of stores, 2810 * therefore they would fail if they had this assertion turned on. 2811 */ 2812 void setPreventUndefinedLoad() { 2813 this.preventUndefinedLoad = true; 2814 } 2815 2816 private static boolean isOptimistic(final int flags) { 2817 return (flags & CALLSITE_OPTIMISTIC) != 0; 2818 } 2819 }