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