MethodEmitter.java revision 1040:cc3000241e57
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; 74import java.io.PrintStream; 75import java.lang.reflect.Array; 76import java.util.Collection; 77import java.util.EnumSet; 78import java.util.IdentityHashMap; 79import java.util.List; 80import java.util.Map; 81import jdk.internal.dynalink.support.NameCodec; 82import jdk.internal.org.objectweb.asm.Handle; 83import jdk.internal.org.objectweb.asm.MethodVisitor; 84import jdk.nashorn.internal.codegen.ClassEmitter.Flag; 85import jdk.nashorn.internal.codegen.CompilerConstants.Call; 86import jdk.nashorn.internal.codegen.CompilerConstants.FieldAccess; 87import jdk.nashorn.internal.codegen.types.ArrayType; 88import jdk.nashorn.internal.codegen.types.BitwiseType; 89import jdk.nashorn.internal.codegen.types.NumericType; 90import jdk.nashorn.internal.codegen.types.Type; 91import jdk.nashorn.internal.ir.BreakableNode; 92import jdk.nashorn.internal.ir.FunctionNode; 93import jdk.nashorn.internal.ir.IdentNode; 94import jdk.nashorn.internal.ir.JoinPredecessor; 95import jdk.nashorn.internal.ir.LexicalContext; 96import jdk.nashorn.internal.ir.LiteralNode; 97import jdk.nashorn.internal.ir.LocalVariableConversion; 98import jdk.nashorn.internal.ir.RuntimeNode; 99import jdk.nashorn.internal.ir.Symbol; 100import jdk.nashorn.internal.ir.TryNode; 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.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 runtime node indy:s */ 177 private static final Handle RUNTIMEBOOTSTRAP = new Handle(H_INVOKESTATIC, RuntimeCallSite.BOOTSTRAP.className(), RuntimeCallSite.BOOTSTRAP.name(), RuntimeCallSite.BOOTSTRAP.descriptor()); 178 179 /** Bootstrap for array populators */ 180 private static final Handle POPULATE_ARRAY_BOOTSTRAP = new Handle(H_INVOKESTATIC, RewriteException.BOOTSTRAP.className(), RewriteException.BOOTSTRAP.name(), RewriteException.BOOTSTRAP.descriptor()); 181 182 /** 183 * Constructor - internal use from ClassEmitter only 184 * @see ClassEmitter#method 185 * 186 * @param classEmitter the class emitter weaving the class this method is in 187 * @param method a method visitor 188 */ 189 MethodEmitter(final ClassEmitter classEmitter, final MethodVisitor method) { 190 this(classEmitter, method, null); 191 } 192 193 /** 194 * Constructor - internal use from ClassEmitter only 195 * @see ClassEmitter#method 196 * 197 * @param classEmitter the class emitter weaving the class this method is in 198 * @param method a method visitor 199 * @param functionNode a function node representing this method 200 */ 201 MethodEmitter(final ClassEmitter classEmitter, final MethodVisitor method, final FunctionNode functionNode) { 202 this.context = classEmitter.getContext(); 203 this.classEmitter = classEmitter; 204 this.method = method; 205 this.functionNode = functionNode; 206 this.stack = null; 207 this.log = context.getLogger(CodeGenerator.class); 208 this.debug = log.isEnabled(); 209 } 210 211 /** 212 * Begin a method 213 * @see Emitter 214 */ 215 @Override 216 public void begin() { 217 classEmitter.beginMethod(this); 218 newStack(); 219 method.visitCode(); 220 } 221 222 /** 223 * End a method 224 * @see Emitter 225 */ 226 @Override 227 public void end() { 228 method.visitMaxs(0, 0); 229 method.visitEnd(); 230 231 classEmitter.endMethod(this); 232 } 233 234 boolean isReachable() { 235 return stack != null; 236 } 237 238 private void doesNotContinueSequentially() { 239 stack = null; 240 } 241 242 private void newStack() { 243 stack = new Label.Stack(); 244 } 245 246 @Override 247 public String toString() { 248 return "methodEmitter: " + (functionNode == null ? method : functionNode.getName()).toString() + ' ' + Debug.id(this); 249 } 250 251 /** 252 * Push a type to the existing stack 253 * @param type the type 254 */ 255 void pushType(final Type type) { 256 if (type != null) { 257 stack.push(type); 258 } 259 } 260 261 /** 262 * Pop a type from the existing stack 263 * 264 * @param expected expected type - will assert if wrong 265 * 266 * @return the type that was retrieved 267 */ 268 private Type popType(final Type expected) { 269 final Type type = popType(); 270 assert type.isObject() && expected.isObject() || 271 type.isEquivalentTo(expected) : type + " is not compatible with " + expected; 272 return type; 273 } 274 275 /** 276 * Pop a type from the existing stack, no matter what it is. 277 * 278 * @return the type 279 */ 280 private Type popType() { 281 return stack.pop(); 282 } 283 284 /** 285 * Pop a type from the existing stack, ensuring that it is numeric. Boolean type is popped as int type. 286 * 287 * @return the type 288 */ 289 private NumericType popNumeric() { 290 final Type type = popType(); 291 if(type.isBoolean()) { 292 // Booleans are treated as int for purposes of arithmetic operations 293 return Type.INT; 294 } 295 assert type.isNumeric(); 296 return (NumericType)type; 297 } 298 299 /** 300 * Pop a type from the existing stack, ensuring that it is an integer type 301 * (integer or long). Boolean type is popped as int type. 302 * 303 * @return the type 304 */ 305 private BitwiseType popBitwise() { 306 final Type type = popType(); 307 if(type == Type.BOOLEAN) { 308 return Type.INT; 309 } 310 return (BitwiseType)type; 311 } 312 313 private BitwiseType popInteger() { 314 final Type type = popType(); 315 if(type == Type.BOOLEAN) { 316 return Type.INT; 317 } 318 assert type == Type.INT; 319 return (BitwiseType)type; 320 } 321 322 /** 323 * Pop a type from the existing stack, ensuring that it is an array type, 324 * assert if not 325 * 326 * @return the type 327 */ 328 private ArrayType popArray() { 329 final Type type = popType(); 330 assert type.isArray() : type; 331 return (ArrayType)type; 332 } 333 334 /** 335 * Peek a given number of slots from the top of the stack and return the 336 * type in that slot 337 * 338 * @param pos the number of positions from the top, 0 is the top element 339 * 340 * @return the type at position "pos" on the stack 341 */ 342 final Type peekType(final int pos) { 343 return stack.peek(pos); 344 } 345 346 /** 347 * Peek at the type at the top of the stack 348 * 349 * @return the type at the top of the stack 350 */ 351 final Type peekType() { 352 return stack.peek(); 353 } 354 355 /** 356 * Generate code a for instantiating a new object and push the 357 * object type on the stack 358 * 359 * @param classDescriptor class descriptor for the object type 360 * @param type the type of the new object 361 * 362 * @return the method emitter 363 */ 364 MethodEmitter _new(final String classDescriptor, final Type type) { 365 debug("new", classDescriptor); 366 method.visitTypeInsn(NEW, classDescriptor); 367 pushType(type); 368 return this; 369 } 370 371 /** 372 * Generate code a for instantiating a new object and push the 373 * object type on the stack 374 * 375 * @param clazz class type to instatiate 376 * 377 * @return the method emitter 378 */ 379 MethodEmitter _new(final Class<?> clazz) { 380 return _new(className(clazz), Type.typeFor(clazz)); 381 } 382 383 /** 384 * Generate code to call the empty constructor for a class 385 * 386 * @param clazz class type to instatiate 387 * 388 * @return the method emitter 389 */ 390 MethodEmitter newInstance(final Class<?> clazz) { 391 return invoke(constructorNoLookup(clazz)); 392 } 393 394 /** 395 * Perform a dup, that is, duplicate the top element and 396 * push the duplicate down a given number of positions 397 * on the stack. This is totally type agnostic. 398 * 399 * @param depth the depth on which to put the copy 400 * 401 * @return the method emitter, or null if depth is illegal and 402 * has no instruction equivalent. 403 */ 404 MethodEmitter dup(final int depth) { 405 if (peekType().dup(method, depth) == null) { 406 return null; 407 } 408 409 debug("dup", depth); 410 411 switch (depth) { 412 case 0: { 413 final int l0 = stack.getTopLocalLoad(); 414 pushType(peekType()); 415 stack.markLocalLoad(l0); 416 break; 417 } 418 case 1: { 419 final int l0 = stack.getTopLocalLoad(); 420 final Type p0 = popType(); 421 final int l1 = stack.getTopLocalLoad(); 422 final Type p1 = popType(); 423 pushType(p0); 424 stack.markLocalLoad(l0); 425 pushType(p1); 426 stack.markLocalLoad(l1); 427 pushType(p0); 428 stack.markLocalLoad(l0); 429 break; 430 } 431 case 2: { 432 final int l0 = stack.getTopLocalLoad(); 433 final Type p0 = popType(); 434 final int l1 = stack.getTopLocalLoad(); 435 final Type p1 = popType(); 436 final int l2 = stack.getTopLocalLoad(); 437 final Type p2 = popType(); 438 pushType(p0); 439 stack.markLocalLoad(l0); 440 pushType(p2); 441 stack.markLocalLoad(l2); 442 pushType(p1); 443 stack.markLocalLoad(l1); 444 pushType(p0); 445 stack.markLocalLoad(l0); 446 break; 447 } 448 default: 449 assert false : "illegal dup depth = " + depth; 450 return null; 451 } 452 453 return this; 454 } 455 456 /** 457 * Perform a dup2, that is, duplicate the top element if it 458 * is a category 2 type, or two top elements if they are category 459 * 1 types, and push them on top of the stack 460 * 461 * @return the method emitter 462 */ 463 MethodEmitter dup2() { 464 debug("dup2"); 465 466 if (peekType().isCategory2()) { 467 final int l0 = stack.getTopLocalLoad(); 468 pushType(peekType()); 469 stack.markLocalLoad(l0); 470 } else { 471 final int l0 = stack.getTopLocalLoad(); 472 final Type p0 = popType(); 473 final int l1 = stack.getTopLocalLoad(); 474 final Type p1 = popType(); 475 pushType(p0); 476 stack.markLocalLoad(l0); 477 pushType(p1); 478 stack.markLocalLoad(l1); 479 pushType(p0); 480 stack.markLocalLoad(l0); 481 pushType(p1); 482 stack.markLocalLoad(l1); 483 } 484 method.visitInsn(DUP2); 485 return this; 486 } 487 488 /** 489 * Duplicate the top element on the stack and push it 490 * 491 * @return the method emitter 492 */ 493 MethodEmitter dup() { 494 return dup(0); 495 } 496 497 /** 498 * Pop the top element of the stack and throw it away 499 * 500 * @return the method emitter 501 */ 502 MethodEmitter pop() { 503 debug("pop", peekType()); 504 popType().pop(method); 505 return this; 506 } 507 508 /** 509 * Pop the top element of the stack if category 2 type, or the two 510 * top elements of the stack if category 1 types 511 * 512 * @return the method emitter 513 */ 514 MethodEmitter pop2() { 515 if (peekType().isCategory2()) { 516 popType(); 517 } else { 518 get2n(); 519 } 520 return this; 521 } 522 523 /** 524 * Swap the top two elements of the stack. This is totally 525 * type agnostic and works for all types 526 * 527 * @return the method emitter 528 */ 529 MethodEmitter swap() { 530 debug("swap"); 531 532 final int l0 = stack.getTopLocalLoad(); 533 final Type p0 = popType(); 534 final int l1 = stack.getTopLocalLoad(); 535 final Type p1 = popType(); 536 p0.swap(method, p1); 537 538 pushType(p0); 539 stack.markLocalLoad(l0); 540 pushType(p1); 541 stack.markLocalLoad(l1); 542 return this; 543 } 544 545 void pack() { 546 final Type type = peekType(); 547 if (type.isInteger()) { 548 convert(PRIMITIVE_FIELD_TYPE); 549 } else if (type.isLong()) { 550 //nop 551 } else if (type.isNumber()) { 552 invokestatic("java/lang/Double", "doubleToRawLongBits", "(D)J"); 553 } else { 554 assert false : type + " cannot be packed!"; 555 } 556 //all others are nops, objects aren't packed 557 } 558 559 /** 560 * Initializes a bytecode method parameter 561 * @param symbol the symbol for the parameter 562 * @param type the type of the parameter 563 * @param start the label for the start of the method 564 */ 565 void initializeMethodParameter(final Symbol symbol, final Type type, final Label start) { 566 assert symbol.isBytecodeLocal(); 567 localVariableDefs.put(symbol, new LocalVariableDef(start.getLabel(), type)); 568 } 569 570 /** 571 * Create a new string builder, call the constructor and push the instance to the stack. 572 * 573 * @return the method emitter 574 */ 575 MethodEmitter newStringBuilder() { 576 return invoke(constructorNoLookup(StringBuilder.class)).dup(); 577 } 578 579 /** 580 * Pop a string and a StringBuilder from the top of the stack and call the append 581 * function of the StringBuilder, appending the string. Pushes the StringBuilder to 582 * the stack when finished. 583 * 584 * @return the method emitter 585 */ 586 MethodEmitter stringBuilderAppend() { 587 convert(Type.STRING); 588 return invoke(virtualCallNoLookup(StringBuilder.class, "append", StringBuilder.class, String.class)); 589 } 590 591 /** 592 * Pops two integer types from the stack, performs a bitwise and and pushes 593 * the result 594 * 595 * @return the method emitter 596 */ 597 MethodEmitter and() { 598 debug("and"); 599 pushType(get2i().and(method)); 600 return this; 601 } 602 603 /** 604 * Pops two integer types from the stack, performs a bitwise or and pushes 605 * the result 606 * 607 * @return the method emitter 608 */ 609 MethodEmitter or() { 610 debug("or"); 611 pushType(get2i().or(method)); 612 return this; 613 } 614 615 /** 616 * Pops two integer types from the stack, performs a bitwise xor and pushes 617 * the result 618 * 619 * @return the method emitter 620 */ 621 MethodEmitter xor() { 622 debug("xor"); 623 pushType(get2i().xor(method)); 624 return this; 625 } 626 627 /** 628 * Pops two integer types from the stack, performs a bitwise logic shift right and pushes 629 * the result. The shift count, the first element, must be INT. 630 * 631 * @return the method emitter 632 */ 633 MethodEmitter shr() { 634 debug("shr"); 635 popInteger(); 636 pushType(popBitwise().shr(method)); 637 return this; 638 } 639 640 /** 641 * Pops two integer types from the stack, performs a bitwise shift left and and pushes 642 * the result. The shift count, the first element, must be INT. 643 * 644 * @return the method emitter 645 */ 646 MethodEmitter shl() { 647 debug("shl"); 648 popInteger(); 649 pushType(popBitwise().shl(method)); 650 return this; 651 } 652 653 /** 654 * Pops two integer types from the stack, performs a bitwise arithmetic shift right and pushes 655 * the result. The shift count, the first element, must be INT. 656 * 657 * @return the method emitter 658 */ 659 MethodEmitter sar() { 660 debug("sar"); 661 popInteger(); 662 pushType(popBitwise().sar(method)); 663 return this; 664 } 665 666 /** 667 * Pops a numeric type from the stack, negates it and pushes the result 668 * 669 * @return the method emitter 670 */ 671 MethodEmitter neg(final int programPoint) { 672 debug("neg"); 673 pushType(popNumeric().neg(method, programPoint)); 674 return this; 675 } 676 677 /** 678 * Add label for the start of a catch block and push the exception to the 679 * stack 680 * 681 * @param recovery label pointing to start of catch block 682 */ 683 void _catch(final Label recovery) { 684 // While in JVM a catch block can be reached through normal control flow, our code generator never does this, 685 // so we might as well presume there's no stack on entry. 686 assert stack == null; 687 recovery.onCatch(); 688 label(recovery); 689 beginCatchBlock(); 690 } 691 692 /** 693 * Add any number of labels for the start of a catch block and push the exception to the 694 * stack 695 * 696 * @param recoveries labels pointing to start of catch block 697 */ 698 void _catch(final Collection<Label> recoveries) { 699 assert stack == null; 700 for(final Label l: recoveries) { 701 label(l); 702 } 703 beginCatchBlock(); 704 } 705 706 private void beginCatchBlock() { 707 // It can happen that the catch label wasn't marked as reachable. They are marked as reachable if there's an 708 // assignment in the try block, but it's possible that there was none. 709 if(!isReachable()) { 710 newStack(); 711 } 712 pushType(Type.typeFor(Throwable.class)); 713 } 714 /** 715 * Start a try/catch block. 716 * 717 * @param entry start label for try 718 * @param exit end label for try 719 * @param recovery start label for catch 720 * @param typeDescriptor type descriptor for exception 721 * @param isOptimismHandler true if this is a hander for {@code UnwarrantedOptimismException}. Normally joining on a 722 * catch handler kills temporary variables, but optimism handlers are an exception, as they need to capture 723 * temporaries as well, so they must remain live. 724 */ 725 private void _try(final Label entry, final Label exit, final Label recovery, final String typeDescriptor, final boolean isOptimismHandler) { 726 recovery.joinFromTry(entry.getStack(), isOptimismHandler); 727 method.visitTryCatchBlock(entry.getLabel(), exit.getLabel(), recovery.getLabel(), typeDescriptor); 728 } 729 730 /** 731 * Start a try/catch block. 732 * 733 * @param entry start label for try 734 * @param exit end label for try 735 * @param recovery start label for catch 736 * @param clazz exception class 737 */ 738 void _try(final Label entry, final Label exit, final Label recovery, final Class<?> clazz) { 739 _try(entry, exit, recovery, CompilerConstants.className(clazz), clazz == UnwarrantedOptimismException.class); 740 } 741 742 /** 743 * Start a try/catch block. The catch is "Throwable" - i.e. catch-all 744 * 745 * @param entry start label for try 746 * @param exit end label for try 747 * @param recovery start label for catch 748 */ 749 void _try(final Label entry, final Label exit, final Label recovery) { 750 _try(entry, exit, recovery, (String)null, false); 751 } 752 753 void markLabelAsOptimisticCatchHandler(final Label label, final int liveLocalCount) { 754 label.markAsOptimisticCatchHandler(stack, liveLocalCount); 755 } 756 757 /** 758 * Load the constants array 759 * @return this method emitter 760 */ 761 MethodEmitter loadConstants() { 762 getStatic(classEmitter.getUnitClassName(), CONSTANTS.symbolName(), CONSTANTS.descriptor()); 763 assert peekType().isArray() : peekType(); 764 return this; 765 } 766 767 /** 768 * Push the undefined value for the given type, i.e. 769 * UNDEFINED or UNDEFINEDNUMBER. Currently we have no way of 770 * representing UNDEFINED for INTs and LONGs, so they are not 771 * allowed to be local variables (yet) 772 * 773 * @param type the type for which to push UNDEFINED 774 * @return the method emitter 775 */ 776 MethodEmitter loadUndefined(final Type type) { 777 debug("load undefined ", type); 778 pushType(type.loadUndefined(method)); 779 return this; 780 } 781 782 MethodEmitter loadForcedInitializer(final Type type) { 783 debug("load forced initializer ", type); 784 pushType(type.loadForcedInitializer(method)); 785 return this; 786 } 787 788 /** 789 * Push the empty value for the given type, i.e. EMPTY. 790 * 791 * @param type the type 792 * @return the method emitter 793 */ 794 MethodEmitter loadEmpty(final Type type) { 795 debug("load empty ", type); 796 pushType(type.loadEmpty(method)); 797 return this; 798 } 799 800 /** 801 * Push null to stack 802 * 803 * @return the method emitter 804 */ 805 MethodEmitter loadNull() { 806 debug("aconst_null"); 807 pushType(Type.OBJECT.ldc(method, null)); 808 return this; 809 } 810 811 /** 812 * Push a handle representing this class top stack 813 * 814 * @param className name of the class 815 * 816 * @return the method emitter 817 */ 818 MethodEmitter loadType(final String className) { 819 debug("load type", className); 820 method.visitLdcInsn(jdk.internal.org.objectweb.asm.Type.getObjectType(className)); 821 pushType(Type.OBJECT); 822 return this; 823 } 824 825 /** 826 * Push a boolean constant to the stack. 827 * 828 * @param b value of boolean 829 * 830 * @return the method emitter 831 */ 832 MethodEmitter load(final boolean b) { 833 debug("load boolean", b); 834 pushType(Type.BOOLEAN.ldc(method, b)); 835 return this; 836 } 837 838 /** 839 * Push an int constant to the stack 840 * 841 * @param i value of the int 842 * 843 * @return the method emitter 844 */ 845 MethodEmitter load(final int i) { 846 debug("load int", i); 847 pushType(Type.INT.ldc(method, i)); 848 return this; 849 } 850 851 /** 852 * Push a double constant to the stack 853 * 854 * @param d value of the double 855 * 856 * @return the method emitter 857 */ 858 MethodEmitter load(final double d) { 859 debug("load double", d); 860 pushType(Type.NUMBER.ldc(method, d)); 861 return this; 862 } 863 864 /** 865 * Push an long constant to the stack 866 * 867 * @param l value of the long 868 * 869 * @return the method emitter 870 */ 871 MethodEmitter load(final long l) { 872 debug("load long", l); 873 pushType(Type.LONG.ldc(method, l)); 874 return this; 875 } 876 877 /** 878 * Fetch the length of an array. 879 * @return Array length. 880 */ 881 MethodEmitter arraylength() { 882 debug("arraylength"); 883 popType(Type.OBJECT); 884 pushType(Type.OBJECT_ARRAY.arraylength(method)); 885 return this; 886 } 887 888 /** 889 * Push a String constant to the stack 890 * 891 * @param s value of the String 892 * 893 * @return the method emitter 894 */ 895 MethodEmitter load(final String s) { 896 debug("load string", s); 897 898 if (s == null) { 899 loadNull(); 900 return this; 901 } 902 903 //NASHORN-142 - split too large string 904 final int length = s.length(); 905 if (length > LARGE_STRING_THRESHOLD) { 906 907 _new(StringBuilder.class); 908 dup(); 909 load(length); 910 invoke(constructorNoLookup(StringBuilder.class, int.class)); 911 912 for (int n = 0; n < length; n += LARGE_STRING_THRESHOLD) { 913 final String part = s.substring(n, Math.min(n + LARGE_STRING_THRESHOLD, length)); 914 load(part); 915 stringBuilderAppend(); 916 } 917 918 invoke(virtualCallNoLookup(StringBuilder.class, "toString", String.class)); 919 920 return this; 921 } 922 923 pushType(Type.OBJECT.ldc(method, s)); 924 return this; 925 } 926 927 /** 928 * Pushes the value of an identifier to the stack. If the identifier does not represent a local variable or a 929 * parameter, this will be a no-op. 930 * 931 * @param ident the identifier for the variable being loaded. 932 * 933 * @return the method emitter 934 */ 935 MethodEmitter load(final IdentNode ident) { 936 return load(ident.getSymbol(), ident.getType()); 937 } 938 939 /** 940 * Pushes the value of the symbol to the stack with the specified type. No type conversion is being performed, and 941 * the type is only being used if the symbol addresses a local variable slot. The value of the symbol is loaded if 942 * it addresses a local variable slot, or it is a parameter (in which case it can also be loaded from a vararg array 943 * or the arguments object). If it is neither, the operation is a no-op. 944 * 945 * @param symbol the symbol addressing the value being loaded 946 * @param type the presumed type of the value when it is loaded from a local variable slot 947 * @return the method emitter 948 */ 949 MethodEmitter load(final Symbol symbol, final Type type) { 950 assert symbol != null; 951 if (symbol.hasSlot()) { 952 final int slot = symbol.getSlot(type); 953 debug("load symbol", symbol.getName(), " slot=", slot, "type=", type); 954 load(type, slot); 955 // _try(new Label("dummy"), new Label("dummy2"), recovery); 956 // method.visitTryCatchBlock(new Label(), arg1, arg2, arg3); 957 } else if (symbol.isParam()) { 958 assert functionNode.isVarArg() : "Non-vararg functions have slotted parameters"; 959 final int index = symbol.getFieldIndex(); 960 if (functionNode.needsArguments()) { 961 // ScriptObject.getArgument(int) on arguments 962 debug("load symbol", symbol.getName(), " arguments index=", index); 963 loadCompilerConstant(ARGUMENTS); 964 load(index); 965 ScriptObject.GET_ARGUMENT.invoke(this); 966 } else { 967 // array load from __varargs__ 968 debug("load symbol", symbol.getName(), " array index=", index); 969 loadCompilerConstant(VARARGS); 970 load(symbol.getFieldIndex()); 971 arrayload(); 972 } 973 } 974 return this; 975 } 976 977 /** 978 * Push a local variable to the stack, given an explicit bytecode slot. 979 * This is used e.g. for stub generation where we know where items like 980 * "this" and "scope" reside. 981 * 982 * @param type the type of the variable 983 * @param slot the slot the variable is in 984 * 985 * @return the method emitter 986 */ 987 MethodEmitter load(final Type type, final int slot) { 988 debug("explicit load", type, slot); 989 final Type loadType = type.load(method, slot); 990 assert loadType != null; 991 pushType(loadType == Type.OBJECT && isThisSlot(slot) ? Type.THIS : loadType); 992 assert !preventUndefinedLoad || (slot < stack.localVariableTypes.size() && stack.localVariableTypes.get(slot) != Type.UNKNOWN) 993 : "Attempted load of uninitialized slot " + slot + " (as type " + type + ")"; 994 stack.markLocalLoad(slot); 995 return this; 996 } 997 998 private boolean isThisSlot(final int slot) { 999 if (functionNode == null) { 1000 return slot == CompilerConstants.JAVA_THIS.slot(); 1001 } 1002 final int thisSlot = getCompilerConstantSymbol(THIS).getSlot(Type.OBJECT); 1003 assert !functionNode.needsCallee() || thisSlot == 1; // needsCallee -> thisSlot == 1 1004 assert functionNode.needsCallee() || thisSlot == 0; // !needsCallee -> thisSlot == 0 1005 return slot == thisSlot; 1006 } 1007 1008 /** 1009 * Push a method handle to the stack 1010 * 1011 * @param className class name 1012 * @param methodName method name 1013 * @param descName descriptor 1014 * @param flags flags that describe this handle, e.g. invokespecial new, or invoke virtual 1015 * 1016 * @return the method emitter 1017 */ 1018 MethodEmitter loadHandle(final String className, final String methodName, final String descName, final EnumSet<Flag> flags) { 1019 debug("load handle "); 1020 pushType(Type.OBJECT.ldc(method, new Handle(Flag.getValue(flags), className, methodName, descName))); 1021 return this; 1022 } 1023 1024 private Symbol getCompilerConstantSymbol(final CompilerConstants cc) { 1025 return functionNode.getBody().getExistingSymbol(cc.symbolName()); 1026 } 1027 1028 /** 1029 * True if this method has a slot allocated for the scope variable (meaning, something in the method actually needs 1030 * the scope). 1031 * @return if this method has a slot allocated for the scope variable. 1032 */ 1033 boolean hasScope() { 1034 return getCompilerConstantSymbol(SCOPE).hasSlot(); 1035 } 1036 1037 MethodEmitter loadCompilerConstant(final CompilerConstants cc) { 1038 return loadCompilerConstant(cc, null); 1039 } 1040 1041 MethodEmitter loadCompilerConstant(final CompilerConstants cc, final Type type) { 1042 if (cc == SCOPE && peekType() == Type.SCOPE) { 1043 dup(); 1044 return this; 1045 } 1046 return load(getCompilerConstantSymbol(cc), type != null ? type : getCompilerConstantType(cc)); 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 bookeeping 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 MethodEmitter.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 MethodEmitter registerReturn() { 1614 setHasReturn(); 1615 return this; 1616 } 1617 1618 void setHasReturn() { 1619 this.hasReturn = true; 1620 } 1621 1622 /** 1623 * Perform a non void return, popping the type from the stack 1624 * 1625 * @param type the type for the return 1626 */ 1627 void _return(final Type type) { 1628 debug("return", type); 1629 assert stack.size() == 1 : "Only return value on stack allowed at return point - depth=" + stack.size() + " stack = " + stack; 1630 final Type stackType = peekType(); 1631 if (!Type.areEquivalent(type, stackType)) { 1632 convert(type); 1633 } 1634 popType(type)._return(method); 1635 doesNotContinueSequentially(); 1636 } 1637 1638 /** 1639 * Perform a return using the stack top value as the guide for the type 1640 */ 1641 void _return() { 1642 _return(peekType()); 1643 } 1644 1645 /** 1646 * Perform a void return. 1647 */ 1648 void returnVoid() { 1649 debug("return [void]"); 1650 assert stack.isEmpty() : stack; 1651 method.visitInsn(RETURN); 1652 doesNotContinueSequentially(); 1653 } 1654 1655 /** 1656 * Goto, possibly when splitting is taking place. If 1657 * a splitNode exists, we need to handle the case that the 1658 * jump target is another method 1659 * 1660 * @param label destination label 1661 * @param targetNode the node to which the destination label belongs (the label is normally a break or continue 1662 * label) 1663 */ 1664 void splitAwareGoto(final LexicalContext lc, final Label label, final BreakableNode targetNode) { 1665 _goto(label); 1666 } 1667 1668 /** 1669 * Perform a comparison of two number types that are popped from the stack 1670 * 1671 * @param isCmpG is this a dcmpg semantic, false if it's a dcmpl semantic 1672 * 1673 * @return the method emitter 1674 */ 1675 MethodEmitter cmp(final boolean isCmpG) { 1676 pushType(get2n().cmp(method, isCmpG)); 1677 return this; 1678 } 1679 1680 /** 1681 * Helper function for jumps, conditional or not 1682 * @param opcode opcode for jump 1683 * @param label destination 1684 * @param n elements on stack to compare, 0-2 1685 */ 1686 private void jump(final int opcode, final Label label, final int n) { 1687 for (int i = 0; i < n; i++) { 1688 assert peekType().isInteger() || peekType().isBoolean() || peekType().isObject() : "expecting integer type or object for jump, but found " + peekType(); 1689 popType(); 1690 } 1691 joinTo(label); 1692 method.visitJumpInsn(opcode, label.getLabel()); 1693 } 1694 1695 /** 1696 * Generate an if_acmpeq 1697 * 1698 * @param label label to true case 1699 */ 1700 void if_acmpeq(final Label label) { 1701 debug("if_acmpeq", label); 1702 jump(IF_ACMPEQ, label, 2); 1703 } 1704 1705 /** 1706 * Generate an if_acmpne 1707 * 1708 * @param label label to true case 1709 */ 1710 void if_acmpne(final Label label) { 1711 debug("if_acmpne", label); 1712 jump(IF_ACMPNE, label, 2); 1713 } 1714 1715 /** 1716 * Generate an ifnull 1717 * 1718 * @param label label to true case 1719 */ 1720 void ifnull(final Label label) { 1721 debug("ifnull", label); 1722 jump(IFNULL, label, 1); 1723 } 1724 1725 /** 1726 * Generate an ifnonnull 1727 * 1728 * @param label label to true case 1729 */ 1730 void ifnonnull(final Label label) { 1731 debug("ifnonnull", label); 1732 jump(IFNONNULL, label, 1); 1733 } 1734 1735 /** 1736 * Generate an ifeq 1737 * 1738 * @param label label to true case 1739 */ 1740 void ifeq(final Label label) { 1741 debug("ifeq ", label); 1742 jump(IFEQ, label, 1); 1743 } 1744 1745 /** 1746 * Generate an if_icmpeq 1747 * 1748 * @param label label to true case 1749 */ 1750 void if_icmpeq(final Label label) { 1751 debug("if_icmpeq", label); 1752 jump(IF_ICMPEQ, label, 2); 1753 } 1754 1755 /** 1756 * Generate an if_ne 1757 * 1758 * @param label label to true case 1759 */ 1760 void ifne(final Label label) { 1761 debug("ifne", label); 1762 jump(IFNE, label, 1); 1763 } 1764 1765 /** 1766 * Generate an if_icmpne 1767 * 1768 * @param label label to true case 1769 */ 1770 void if_icmpne(final Label label) { 1771 debug("if_icmpne", label); 1772 jump(IF_ICMPNE, label, 2); 1773 } 1774 1775 /** 1776 * Generate an iflt 1777 * 1778 * @param label label to true case 1779 */ 1780 void iflt(final Label label) { 1781 debug("iflt", label); 1782 jump(IFLT, label, 1); 1783 } 1784 1785 /** 1786 * Generate an if_icmplt 1787 * 1788 * @param label label to true case 1789 */ 1790 void if_icmplt(final Label label) { 1791 debug("if_icmplt", label); 1792 jump(IF_ICMPLT, label, 2); 1793 } 1794 1795 /** 1796 * Generate an ifle 1797 * 1798 * @param label label to true case 1799 */ 1800 void ifle(final Label label) { 1801 debug("ifle", label); 1802 jump(IFLE, label, 1); 1803 } 1804 1805 /** 1806 * Generate an if_icmple 1807 * 1808 * @param label label to true case 1809 */ 1810 void if_icmple(final Label label) { 1811 debug("if_icmple", label); 1812 jump(IF_ICMPLE, label, 2); 1813 } 1814 1815 /** 1816 * Generate an ifgt 1817 * 1818 * @param label label to true case 1819 */ 1820 void ifgt(final Label label) { 1821 debug("ifgt", label); 1822 jump(IFGT, label, 1); 1823 } 1824 1825 /** 1826 * Generate an if_icmpgt 1827 * 1828 * @param label label to true case 1829 */ 1830 void if_icmpgt(final Label label) { 1831 debug("if_icmpgt", label); 1832 jump(IF_ICMPGT, label, 2); 1833 } 1834 1835 /** 1836 * Generate an ifge 1837 * 1838 * @param label label to true case 1839 */ 1840 void ifge(final Label label) { 1841 debug("ifge", label); 1842 jump(IFGE, label, 1); 1843 } 1844 1845 /** 1846 * Generate an if_icmpge 1847 * 1848 * @param label label to true case 1849 */ 1850 void if_icmpge(final Label label) { 1851 debug("if_icmpge", label); 1852 jump(IF_ICMPGE, label, 2); 1853 } 1854 1855 /** 1856 * Unconditional jump to a label 1857 * 1858 * @param label destination label 1859 */ 1860 void _goto(final Label label) { 1861 debug("goto", label); 1862 jump(GOTO, label, 0); 1863 doesNotContinueSequentially(); //whoever reaches the point after us provides the stack, because we don't 1864 } 1865 1866 /** 1867 * Unconditional jump to the start label of a loop. It differs from ordinary {@link #_goto(Label)} in that it will 1868 * preserve the current label stack, as the next instruction after the goto is loop body that the loop will come 1869 * back to. Also used to jump at the start label of the continuation handler, as it behaves much like a loop test in 1870 * the sense that after it is evaluated, it also jumps backwards. 1871 * 1872 * @param loopStart start label of a loop 1873 */ 1874 void gotoLoopStart(final Label loopStart) { 1875 debug("goto (loop)", loopStart); 1876 jump(GOTO, loopStart, 0); 1877 } 1878 1879 /** 1880 * Unconditional jump without any control flow and data flow testing. You should not normally use this method when 1881 * generating code, except if you're very sure that you know what you're doing. Normally only used for the 1882 * admittedly torturous control flow of continuation handler plumbing. 1883 * @param target the target of the jump 1884 */ 1885 void uncheckedGoto(final Label target) { 1886 method.visitJumpInsn(GOTO, target.getLabel()); 1887 } 1888 1889 /** 1890 * Potential transfer of control to a catch block. 1891 * 1892 * @param catchLabel destination catch label 1893 */ 1894 void canThrow(final Label catchLabel) { 1895 catchLabel.joinFromTry(stack, false); 1896 } 1897 1898 /** 1899 * A join in control flow - helper function that makes sure all entry stacks 1900 * discovered for the join point so far are equivalent 1901 * 1902 * MergeStack: we are about to enter a label. If its stack, label.getStack() is null 1903 * we have never been here before. Then we are expected to carry a stack with us. 1904 * 1905 * @param label label 1906 */ 1907 private void joinTo(final Label label) { 1908 assert isReachable(); 1909 label.joinFrom(stack); 1910 } 1911 1912 /** 1913 * Register a new label, enter it here. 1914 * @param label 1915 */ 1916 void label(final Label label) { 1917 breakLabel(label, -1); 1918 } 1919 1920 /** 1921 * Register a new break target label, enter it here. 1922 * 1923 * @param label the label 1924 * @param liveLocals the number of live locals at this label 1925 */ 1926 void breakLabel(final Label label, final int liveLocals) { 1927 if (!isReachable()) { 1928 // If we emit a label, and the label's stack is null, it must not be reachable. 1929 assert (label.getStack() == null) != label.isReachable(); 1930 } else { 1931 joinTo(label); 1932 } 1933 // Use label's stack as we might have no stack. 1934 final Label.Stack labelStack = label.getStack(); 1935 stack = labelStack == null ? null : labelStack.clone(); 1936 if(stack != null && label.isBreakTarget() && liveLocals != -1) { 1937 // This has to be done because we might not have another frame to provide us with its firstTemp if the label 1938 // is only reachable through a break or continue statement; also in this case, the frame can actually 1939 // give us a higher number of live locals, e.g. if it comes from a catch. Typical example: 1940 // for(;;) { try{ throw 0; } catch(e) { break; } }. 1941 // Since the for loop can only be exited through the break in the catch block, it'll bring with it the 1942 // "e" as a live local, and we need to trim it off here. 1943 assert stack.firstTemp >= liveLocals; 1944 stack.firstTemp = liveLocals; 1945 } 1946 debug_label(label); 1947 method.visitLabel(label.getLabel()); 1948 } 1949 1950 /** 1951 * Pop element from stack, convert to given type 1952 * 1953 * @param to type to convert to 1954 * 1955 * @return the method emitter 1956 */ 1957 MethodEmitter convert(final Type to) { 1958 final Type from = peekType(); 1959 final Type type = from.convert(method, to); 1960 if (type != null) { 1961 if (!from.isEquivalentTo(to)) { 1962 debug("convert", from, "->", to); 1963 } 1964 if (type != from) { 1965 final int l0 = stack.getTopLocalLoad(); 1966 popType(); 1967 pushType(type); 1968 // NOTE: conversions from a primitive type are considered to preserve the "load" property of the value 1969 // on the stack. Otherwise we could introduce temporary locals in a deoptimized rest-of (e.g. doing an 1970 // "i < x.length" where "i" is int and ".length" gets deoptimized to long would end up converting i to 1971 // long with "ILOAD i; I2L; LSTORE tmp; LLOAD tmp;"). Such additional temporary would cause an error 1972 // when restoring the state of the function for rest-of execution, as the not-yet deoptimized variant 1973 // would have the (now invalidated) assumption that "x.length" is an int, so it wouldn't have the I2L, 1974 // and therefore neither the subsequent LSTORE tmp; LLOAD tmp;. By making sure conversions from a 1975 // primitive type don't erase the "load" information, we don't introduce temporaries in the deoptimized 1976 // rest-of that didn't exist in the more optimistic version that triggered the deoptimization. 1977 // NOTE: as a more general observation, we could theoretically track the operations required to 1978 // reproduce any stack value as long as they are all local loads, constant loads, and stack operations. 1979 // We won't go there in the current system 1980 if(!from.isObject()) { 1981 stack.markLocalLoad(l0); 1982 } 1983 } 1984 } 1985 return this; 1986 } 1987 1988 /** 1989 * Helper function - expect two types that are equivalent 1990 * 1991 * @return common type 1992 */ 1993 private Type get2() { 1994 final Type p0 = popType(); 1995 final Type p1 = popType(); 1996 assert p0.isEquivalentTo(p1) : "expecting equivalent types on stack but got " + p0 + " and " + p1; 1997 return p0; 1998 } 1999 2000 /** 2001 * Helper function - expect two types that are integer types and equivalent 2002 * 2003 * @return common type 2004 */ 2005 private BitwiseType get2i() { 2006 final BitwiseType p0 = popBitwise(); 2007 final BitwiseType p1 = popBitwise(); 2008 assert p0.isEquivalentTo(p1) : "expecting equivalent types on stack but got " + p0 + " and " + p1; 2009 return p0; 2010 } 2011 2012 /** 2013 * Helper function - expect two types that are numbers and equivalent 2014 * 2015 * @return common type 2016 */ 2017 private NumericType get2n() { 2018 final NumericType p0 = popNumeric(); 2019 final NumericType p1 = popNumeric(); 2020 assert p0.isEquivalentTo(p1) : "expecting equivalent types on stack but got " + p0 + " and " + p1; 2021 return p0; 2022 } 2023 2024 /** 2025 * Pop two numbers, perform addition and push result 2026 * 2027 * @return the method emitter 2028 */ 2029 MethodEmitter add(final int programPoint) { 2030 debug("add"); 2031 pushType(get2().add(method, programPoint)); 2032 return this; 2033 } 2034 2035 /** 2036 * Pop two numbers, perform subtraction and push result 2037 * 2038 * @return the method emitter 2039 */ 2040 MethodEmitter sub(final int programPoint) { 2041 debug("sub"); 2042 pushType(get2n().sub(method, programPoint)); 2043 return this; 2044 } 2045 2046 /** 2047 * Pop two numbers, perform multiplication and push result 2048 * 2049 * @return the method emitter 2050 */ 2051 MethodEmitter mul(final int programPoint) { 2052 debug("mul "); 2053 pushType(get2n().mul(method, programPoint)); 2054 return this; 2055 } 2056 2057 /** 2058 * Pop two numbers, perform division and push result 2059 * 2060 * @return the method emitter 2061 */ 2062 MethodEmitter div(final int programPoint) { 2063 debug("div"); 2064 pushType(get2n().div(method, programPoint)); 2065 return this; 2066 } 2067 2068 /** 2069 * Pop two numbers, calculate remainder and push result 2070 * 2071 * @return the method emitter 2072 */ 2073 MethodEmitter rem(final int programPoint) { 2074 debug("rem"); 2075 pushType(get2n().rem(method, programPoint)); 2076 return this; 2077 } 2078 2079 /** 2080 * Retrieve the top <tt>count</tt> types on the stack without modifying it. 2081 * 2082 * @param count number of types to return 2083 * @return array of Types 2084 */ 2085 protected Type[] getTypesFromStack(final int count) { 2086 return stack.getTopTypes(count); 2087 } 2088 2089 int[] getLocalLoadsOnStack(final int from, final int to) { 2090 return stack.getLocalLoads(from, to); 2091 } 2092 2093 int getStackSize() { 2094 return stack.size(); 2095 } 2096 2097 int getFirstTemp() { 2098 return stack.firstTemp; 2099 } 2100 2101 int getUsedSlotsWithLiveTemporaries() { 2102 return stack.getUsedSlotsWithLiveTemporaries(); 2103 } 2104 2105 /** 2106 * Helper function to generate a function signature based on stack contents 2107 * and argument count and return type 2108 * 2109 * @param returnType return type 2110 * @param argCount argument count 2111 * 2112 * @return function signature for stack contents 2113 */ 2114 private String getDynamicSignature(final Type returnType, final int argCount) { 2115 final Type[] paramTypes = new Type[argCount]; 2116 2117 int pos = 0; 2118 for (int i = argCount - 1; i >= 0; i--) { 2119 paramTypes[i] = stack.peek(pos++); 2120 } 2121 final String descriptor = Type.getMethodDescriptor(returnType, paramTypes); 2122 for (int i = 0; i < argCount; i++) { 2123 popType(paramTypes[argCount - i - 1]); 2124 } 2125 2126 return descriptor; 2127 } 2128 2129 MethodEmitter invalidateSpecialName(final String name) { 2130 switch (name) { 2131 case "apply": 2132 case "call": 2133 debug("invalidate_name", "name=", name); 2134 load("Function"); 2135 invoke(ScriptRuntime.INVALIDATE_RESERVED_BUILTIN_NAME); 2136 break; 2137 default: 2138 break; 2139 } 2140 return this; 2141 } 2142 2143 /** 2144 * Generate a dynamic new 2145 * 2146 * @param argCount number of arguments 2147 * @param flags callsite flags 2148 * 2149 * @return the method emitter 2150 */ 2151 MethodEmitter dynamicNew(final int argCount, final int flags) { 2152 assert !isOptimistic(flags); 2153 debug("dynamic_new", "argcount=", argCount); 2154 final String signature = getDynamicSignature(Type.OBJECT, argCount); 2155 method.visitInvokeDynamicInsn("dyn:new", signature, LINKERBOOTSTRAP, flags); 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 debug("dynamic_call", "args=", argCount, "returnType=", returnType); 2171 final String signature = getDynamicSignature(returnType, argCount); // +1 because the function itself is the 1st parameter for dynamic calls (what you call - call target) 2172 debug(" signature", signature); 2173 method.visitInvokeDynamicInsn("dyn:call", signature, LINKERBOOTSTRAP, flags); 2174 pushType(returnType); 2175 2176 return this; 2177 } 2178 2179 MethodEmitter dynamicArrayPopulatorCall(final int argCount, final int startIndex) { 2180 debug("populate_array", "args=", argCount, "startIndex=", startIndex); 2181 final String signature = getDynamicSignature(Type.OBJECT_ARRAY, argCount); 2182 method.visitInvokeDynamicInsn("populateArray", signature, POPULATE_ARRAY_BOOTSTRAP, startIndex); 2183 pushType(Type.OBJECT_ARRAY); 2184 return this; 2185 } 2186 2187 /** 2188 * Generate a dynamic call for a runtime node 2189 * 2190 * @param name tag for the invoke dynamic for this runtime node 2191 * @param returnType return type 2192 * @param request RuntimeNode request 2193 * 2194 * @return the method emitter 2195 */ 2196 MethodEmitter dynamicRuntimeCall(final String name, final Type returnType, final RuntimeNode.Request request) { 2197 debug("dynamic_runtime_call", name, "args=", request.getArity(), "returnType=", returnType); 2198 final String signature = getDynamicSignature(returnType, request.getArity()); 2199 debug(" signature", signature); 2200 method.visitInvokeDynamicInsn(name, signature, RUNTIMEBOOTSTRAP); 2201 pushType(returnType); 2202 2203 return this; 2204 } 2205 2206 /** 2207 * Generate dynamic getter. Pop scope from stack. Push result 2208 * 2209 * @param valueType type of the value to set 2210 * @param name name of property 2211 * @param flags call site flags 2212 * @param isMethod should it prefer retrieving methods 2213 * 2214 * @return the method emitter 2215 */ 2216 MethodEmitter dynamicGet(final Type valueType, final String name, final int flags, final boolean isMethod) { 2217 debug("dynamic_get", name, valueType, getProgramPoint(flags)); 2218 2219 Type type = valueType; 2220 if (type.isObject() || type.isBoolean()) { 2221 type = Type.OBJECT; //promote e.g strings to object generic setter 2222 } 2223 2224 popType(Type.SCOPE); 2225 method.visitInvokeDynamicInsn((isMethod ? "dyn:getMethod|getProp|getElem:" : "dyn:getProp|getElem|getMethod:") + 2226 NameCodec.encode(name), Type.getMethodDescriptor(type, Type.OBJECT), LINKERBOOTSTRAP, flags); 2227 2228 pushType(type); 2229 convert(valueType); //most probably a nop 2230 2231 return this; 2232 } 2233 2234 /** 2235 * Generate dynamic setter. Pop receiver and property from stack. 2236 * 2237 * @param name name of property 2238 * @param flags call site flags 2239 */ 2240 void dynamicSet(final String name, final int flags) { 2241 assert !isOptimistic(flags); 2242 debug("dynamic_set", name, peekType()); 2243 2244 Type type = peekType(); 2245 if (type.isObject() || type.isBoolean()) { //promote strings to objects etc 2246 type = Type.OBJECT; 2247 convert(Type.OBJECT); //TODO bad- until we specialize boolean setters, 2248 } 2249 popType(type); 2250 popType(Type.SCOPE); 2251 2252 method.visitInvokeDynamicInsn("dyn:setProp|setElem:" + NameCodec.encode(name), methodDescriptor(void.class, Object.class, type.getTypeClass()), LINKERBOOTSTRAP, flags); 2253 } 2254 2255 /** 2256 * Dynamic getter for indexed structures. Pop index and receiver from stack, 2257 * generate appropriate signatures based on types 2258 * 2259 * @param result result type for getter 2260 * @param flags call site flags for getter 2261 * @param isMethod should it prefer retrieving methods 2262 * 2263 * @return the method emitter 2264 */ 2265 MethodEmitter dynamicGetIndex(final Type result, final int flags, final boolean isMethod) { 2266 assert result.getTypeClass().isPrimitive() || result.getTypeClass() == Object.class; 2267 debug("dynamic_get_index", peekType(1), "[", peekType(), "]", getProgramPoint(flags)); 2268 2269 Type resultType = result; 2270 if (result.isBoolean()) { 2271 resultType = Type.OBJECT; // INT->OBJECT to avoid another dimension of cross products in the getters. TODO 2272 } 2273 2274 Type index = peekType(); 2275 if (index.isObject() || index.isBoolean()) { 2276 index = Type.OBJECT; //e.g. string->object 2277 convert(Type.OBJECT); 2278 } 2279 popType(); 2280 2281 popType(Type.OBJECT); 2282 2283 final String signature = Type.getMethodDescriptor(resultType, Type.OBJECT /*e.g STRING->OBJECT*/, index); 2284 2285 method.visitInvokeDynamicInsn(isMethod ? "dyn:getMethod|getElem|getProp" : "dyn:getElem|getProp|getMethod", signature, LINKERBOOTSTRAP, flags); 2286 pushType(resultType); 2287 2288 if (result.isBoolean()) { 2289 convert(Type.BOOLEAN); 2290 } 2291 2292 return this; 2293 } 2294 2295 private static String getProgramPoint(final int flags) { 2296 if((flags & CALLSITE_OPTIMISTIC) == 0) { 2297 return ""; 2298 } 2299 return "pp=" + String.valueOf((flags & (-1 << CALLSITE_PROGRAM_POINT_SHIFT)) >> CALLSITE_PROGRAM_POINT_SHIFT); 2300 } 2301 2302 /** 2303 * Dynamic setter for indexed structures. Pop value, index and receiver from 2304 * stack, generate appropriate signature based on types 2305 * 2306 * @param flags call site flags for setter 2307 */ 2308 void dynamicSetIndex(final int flags) { 2309 assert !isOptimistic(flags); 2310 debug("dynamic_set_index", peekType(2), "[", peekType(1), "] =", peekType()); 2311 2312 Type value = peekType(); 2313 if (value.isObject() || value.isBoolean()) { 2314 value = Type.OBJECT; //e.g. STRING->OBJECT - one descriptor for all object types 2315 convert(Type.OBJECT); 2316 } 2317 popType(); 2318 2319 Type index = peekType(); 2320 if (index.isObject() || index.isBoolean()) { 2321 index = Type.OBJECT; //e.g. string->object 2322 convert(Type.OBJECT); 2323 } 2324 popType(index); 2325 2326 final Type receiver = popType(Type.OBJECT); 2327 assert receiver.isObject(); 2328 2329 method.visitInvokeDynamicInsn("dyn:setElem|setProp", methodDescriptor(void.class, receiver.getTypeClass(), index.getTypeClass(), value.getTypeClass()), LINKERBOOTSTRAP, flags); 2330 } 2331 2332 /** 2333 * Load a key value in the proper form. 2334 * 2335 * @param key 2336 */ 2337 //TODO move this and break it apart 2338 MethodEmitter loadKey(final Object key) { 2339 if (key instanceof IdentNode) { 2340 method.visitLdcInsn(((IdentNode) key).getName()); 2341 } else if (key instanceof LiteralNode) { 2342 method.visitLdcInsn(((LiteralNode<?>)key).getString()); 2343 } else { 2344 method.visitLdcInsn(JSType.toString(key)); 2345 } 2346 pushType(Type.OBJECT); //STRING 2347 return this; 2348 } 2349 2350 @SuppressWarnings("fallthrough") 2351 private static Type fieldType(final String desc) { 2352 switch (desc) { 2353 case "Z": 2354 case "B": 2355 case "C": 2356 case "S": 2357 case "I": 2358 return Type.INT; 2359 case "F": 2360 assert false; 2361 case "D": 2362 return Type.NUMBER; 2363 case "J": 2364 return Type.LONG; 2365 default: 2366 assert desc.startsWith("[") || desc.startsWith("L") : desc + " is not an object type"; 2367 switch (desc.charAt(0)) { 2368 case 'L': 2369 return Type.OBJECT; 2370 case '[': 2371 return Type.typeFor(Array.newInstance(fieldType(desc.substring(1)).getTypeClass(), 0).getClass()); 2372 default: 2373 assert false; 2374 } 2375 return Type.OBJECT; 2376 } 2377 } 2378 2379 /** 2380 * Generate get for a field access 2381 * 2382 * @param fa the field access 2383 * 2384 * @return the method emitter 2385 */ 2386 MethodEmitter getField(final FieldAccess fa) { 2387 return fa.get(this); 2388 } 2389 2390 /** 2391 * Generate set for a field access 2392 * 2393 * @param fa the field access 2394 */ 2395 void putField(final FieldAccess fa) { 2396 fa.put(this); 2397 } 2398 2399 /** 2400 * Get the value of a non-static field, pop the receiver from the stack, 2401 * push value to the stack 2402 * 2403 * @param className class 2404 * @param fieldName field name 2405 * @param fieldDescriptor field descriptor 2406 * 2407 * @return the method emitter 2408 */ 2409 MethodEmitter getField(final String className, final String fieldName, final String fieldDescriptor) { 2410 debug("getfield", "receiver=", peekType(), className, ".", fieldName, fieldDescriptor); 2411 final Type receiver = popType(); 2412 assert receiver.isObject(); 2413 method.visitFieldInsn(GETFIELD, className, fieldName, fieldDescriptor); 2414 pushType(fieldType(fieldDescriptor)); 2415 return this; 2416 } 2417 2418 /** 2419 * Get the value of a static field, push it to the stack 2420 * 2421 * @param className class 2422 * @param fieldName field name 2423 * @param fieldDescriptor field descriptor 2424 * 2425 * @return the method emitter 2426 */ 2427 MethodEmitter getStatic(final String className, final String fieldName, final String fieldDescriptor) { 2428 debug("getstatic", className, ".", fieldName, ".", fieldDescriptor); 2429 method.visitFieldInsn(GETSTATIC, className, fieldName, fieldDescriptor); 2430 pushType(fieldType(fieldDescriptor)); 2431 return this; 2432 } 2433 2434 /** 2435 * Pop value and field from stack and write to a non-static field 2436 * 2437 * @param className class 2438 * @param fieldName field name 2439 * @param fieldDescriptor field descriptor 2440 */ 2441 void putField(final String className, final String fieldName, final String fieldDescriptor) { 2442 debug("putfield", "receiver=", peekType(1), "value=", peekType()); 2443 popType(fieldType(fieldDescriptor)); 2444 popType(Type.OBJECT); 2445 method.visitFieldInsn(PUTFIELD, className, fieldName, fieldDescriptor); 2446 } 2447 2448 /** 2449 * Pop value from stack and write to a static field 2450 * 2451 * @param className class 2452 * @param fieldName field name 2453 * @param fieldDescriptor field descriptor 2454 */ 2455 void putStatic(final String className, final String fieldName, final String fieldDescriptor) { 2456 debug("putfield", "value=", peekType()); 2457 popType(fieldType(fieldDescriptor)); 2458 method.visitFieldInsn(PUTSTATIC, className, fieldName, fieldDescriptor); 2459 } 2460 2461 /** 2462 * Register line number at a label 2463 * 2464 * @param line line number 2465 */ 2466 void lineNumber(final int line) { 2467 if (context.getEnv()._debug_lines) { 2468 debug_label("[LINE]", line); 2469 final jdk.internal.org.objectweb.asm.Label l = new jdk.internal.org.objectweb.asm.Label(); 2470 method.visitLabel(l); 2471 method.visitLineNumber(line, l); 2472 } 2473 } 2474 2475 void beforeJoinPoint(final JoinPredecessor joinPredecessor) { 2476 LocalVariableConversion next = joinPredecessor.getLocalVariableConversion(); 2477 while(next != null) { 2478 final Symbol symbol = next.getSymbol(); 2479 if(next.isLive()) { 2480 emitLocalVariableConversion(next, true); 2481 } else { 2482 markDeadLocalVariable(symbol); 2483 } 2484 next = next.getNext(); 2485 } 2486 } 2487 2488 void beforeTry(final TryNode tryNode, final Label recovery) { 2489 LocalVariableConversion next = tryNode.getLocalVariableConversion(); 2490 while(next != null) { 2491 if(next.isLive()) { 2492 final Type to = emitLocalVariableConversion(next, false); 2493 recovery.getStack().onLocalStore(to, next.getSymbol().getSlot(to), true); 2494 } 2495 next = next.getNext(); 2496 } 2497 } 2498 2499 private Type emitLocalVariableConversion(final LocalVariableConversion conversion, final boolean onlySymbolLiveValue) { 2500 final Type from = conversion.getFrom(); 2501 final Type to = conversion.getTo(); 2502 final Symbol symbol = conversion.getSymbol(); 2503 assert symbol.isBytecodeLocal(); 2504 if(from == Type.UNDEFINED) { 2505 loadUndefined(to); 2506 } else { 2507 load(symbol, from).convert(to); 2508 } 2509 store(symbol, to, onlySymbolLiveValue); 2510 return to; 2511 } 2512 2513 /* 2514 * Debugging below 2515 */ 2516 2517 private final FieldAccess ERR_STREAM = staticField(System.class, "err", PrintStream.class); 2518 private final Call PRINT = virtualCallNoLookup(PrintStream.class, "print", void.class, Object.class); 2519 private final Call PRINTLN = virtualCallNoLookup(PrintStream.class, "println", void.class, Object.class); 2520 private final Call PRINT_STACKTRACE = virtualCallNoLookup(Throwable.class, "printStackTrace", void.class); 2521 2522 /** 2523 * Emit a System.err.print statement of whatever is on top of the bytecode stack 2524 */ 2525 void print() { 2526 getField(ERR_STREAM); 2527 swap(); 2528 convert(Type.OBJECT); 2529 invoke(PRINT); 2530 } 2531 2532 /** 2533 * Emit a System.err.println statement of whatever is on top of the bytecode stack 2534 */ 2535 void println() { 2536 getField(ERR_STREAM); 2537 swap(); 2538 convert(Type.OBJECT); 2539 invoke(PRINTLN); 2540 } 2541 2542 /** 2543 * Emit a System.err.print statement 2544 * @param string string to print 2545 */ 2546 void print(final String string) { 2547 getField(ERR_STREAM); 2548 load(string); 2549 invoke(PRINT); 2550 } 2551 2552 /** 2553 * Emit a System.err.println statement 2554 * @param string string to print 2555 */ 2556 void println(final String string) { 2557 getField(ERR_STREAM); 2558 load(string); 2559 invoke(PRINTLN); 2560 } 2561 2562 /** 2563 * Print a stacktrace to S 2564 */ 2565 void stacktrace() { 2566 _new(Throwable.class); 2567 dup(); 2568 invoke(constructorNoLookup(Throwable.class)); 2569 invoke(PRINT_STACKTRACE); 2570 } 2571 2572 private static int linePrefix = 0; 2573 2574 /** 2575 * Debug function that outputs generated bytecode and stack contents 2576 * 2577 * @param args debug information to print 2578 */ 2579 private void debug(final Object... args) { 2580 if (debug) { 2581 debug(30, args); 2582 } 2583 } 2584 2585 /** 2586 * Debug function that outputs generated bytecode and stack contents 2587 * for a label - indentation is currently the only thing that differs 2588 * 2589 * @param args debug information to print 2590 */ 2591 private void debug_label(final Object... args) { 2592 if (debug) { 2593 debug(22, args); 2594 } 2595 } 2596 2597 private void debug(final int padConstant, final Object... args) { 2598 if (debug) { 2599 final StringBuilder sb = new StringBuilder(); 2600 int pad; 2601 2602 sb.append('#'); 2603 sb.append(++linePrefix); 2604 2605 pad = 5 - sb.length(); 2606 while (pad > 0) { 2607 sb.append(' '); 2608 pad--; 2609 } 2610 2611 if (isReachable() && !stack.isEmpty()) { 2612 sb.append("{"); 2613 sb.append(stack.size()); 2614 sb.append(":"); 2615 for (int pos = 0; pos < stack.size(); pos++) { 2616 final Type t = stack.peek(pos); 2617 2618 if (t == Type.SCOPE) { 2619 sb.append("scope"); 2620 } else if (t == Type.THIS) { 2621 sb.append("this"); 2622 } else if (t.isObject()) { 2623 String desc = t.getDescriptor(); 2624 int i; 2625 for (i = 0; desc.charAt(i) == '[' && i < desc.length(); i++) { 2626 sb.append('['); 2627 } 2628 desc = desc.substring(i); 2629 final int slash = desc.lastIndexOf('/'); 2630 if (slash != -1) { 2631 desc = desc.substring(slash + 1, desc.length() - 1); 2632 } 2633 if ("Object".equals(desc)) { 2634 sb.append('O'); 2635 } else { 2636 sb.append(desc); 2637 } 2638 } else { 2639 sb.append(t.getDescriptor()); 2640 } 2641 final int loadIndex = stack.localLoads[stack.sp - 1 - pos]; 2642 if(loadIndex != Label.Stack.NON_LOAD) { 2643 sb.append('(').append(loadIndex).append(')'); 2644 } 2645 if (pos + 1 < stack.size()) { 2646 sb.append(' '); 2647 } 2648 } 2649 sb.append('}'); 2650 sb.append(' '); 2651 } 2652 2653 pad = padConstant - sb.length(); 2654 while (pad > 0) { 2655 sb.append(' '); 2656 pad--; 2657 } 2658 2659 for (final Object arg : args) { 2660 sb.append(arg); 2661 sb.append(' '); 2662 } 2663 2664 if (context.getEnv() != null) { //early bootstrap code doesn't have inited context yet 2665 log.info(sb); 2666 if (DEBUG_TRACE_LINE == linePrefix) { 2667 new Throwable().printStackTrace(log.getOutputStream()); 2668 } 2669 } 2670 } 2671 } 2672 2673 /** 2674 * Set the current function node being emitted 2675 * @param functionNode the function node 2676 */ 2677 void setFunctionNode(final FunctionNode functionNode) { 2678 this.functionNode = functionNode; 2679 } 2680 2681 boolean hasReturn() { 2682 return hasReturn; 2683 } 2684 2685 /** 2686 * Invoke to enforce assertions preventing load from a local variable slot that's known to not have been written to. 2687 * Used by CodeGenerator, as it strictly enforces tracking of stores. Simpler uses of MethodEmitter, e.g. those 2688 * for creating initializers for structure classes, array getters, etc. don't have strict tracking of stores, 2689 * therefore they would fail if they had this assertion turned on. 2690 */ 2691 void setPreventUndefinedLoad() { 2692 this.preventUndefinedLoad = true; 2693 } 2694 2695 private static boolean isOptimistic(final int flags) { 2696 return (flags & CALLSITE_OPTIMISTIC) != 0; 2697 } 2698} 2699