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