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