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