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