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