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