1 /* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.nashorn.internal.codegen; 27 28 import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.PRIVATE; 29 import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.STATIC; 30 import static jdk.nashorn.internal.codegen.CompilerConstants.GET_MAP; 31 import static jdk.nashorn.internal.codegen.CompilerConstants.GET_STRING; 32 import static jdk.nashorn.internal.codegen.CompilerConstants.LEAF; 33 import static jdk.nashorn.internal.codegen.CompilerConstants.QUICK_PREFIX; 34 import static jdk.nashorn.internal.codegen.CompilerConstants.REGEX_PREFIX; 35 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; 36 import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_ARRAY_ARG; 37 import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_PREFIX; 38 import static jdk.nashorn.internal.codegen.CompilerConstants.interfaceCallNoLookup; 39 import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor; 40 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup; 41 import static jdk.nashorn.internal.codegen.CompilerConstants.staticField; 42 import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor; 43 import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL; 44 import static jdk.nashorn.internal.ir.Symbol.IS_TEMP; 45 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_FAST_SCOPE; 46 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_FUNCTION_DECLARATION; 47 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_SCOPE; 48 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT; 49 50 import java.io.PrintWriter; 51 import java.util.ArrayList; 52 import java.util.Arrays; 53 import java.util.EnumSet; 54 import java.util.HashMap; 55 import java.util.Iterator; 56 import java.util.LinkedList; 57 import java.util.List; 58 import java.util.Map; 59 import java.util.Map.Entry; 60 import java.util.TreeMap; 61 import jdk.nashorn.internal.codegen.ClassEmitter.Flag; 62 import jdk.nashorn.internal.codegen.CompilerConstants.Call; 63 import jdk.nashorn.internal.codegen.MethodEmitter.Condition; 64 import jdk.nashorn.internal.codegen.MethodEmitter.Label; 65 import jdk.nashorn.internal.codegen.RuntimeCallSite.SpecializedRuntimeNode; 66 import jdk.nashorn.internal.codegen.objects.FieldObjectCreator; 67 import jdk.nashorn.internal.codegen.objects.FunctionObjectCreator; 68 import jdk.nashorn.internal.codegen.objects.MapCreator; 69 import jdk.nashorn.internal.codegen.objects.ObjectMapCreator; 70 import jdk.nashorn.internal.codegen.types.ArrayType; 71 import jdk.nashorn.internal.codegen.types.Type; 72 import jdk.nashorn.internal.ir.AccessNode; 73 import jdk.nashorn.internal.ir.BaseNode; 74 import jdk.nashorn.internal.ir.BinaryNode; 75 import jdk.nashorn.internal.ir.Block; 76 import jdk.nashorn.internal.ir.BreakNode; 77 import jdk.nashorn.internal.ir.CallNode; 78 import jdk.nashorn.internal.ir.CaseNode; 79 import jdk.nashorn.internal.ir.CatchNode; 80 import jdk.nashorn.internal.ir.ContinueNode; 81 import jdk.nashorn.internal.ir.DoWhileNode; 82 import jdk.nashorn.internal.ir.EmptyNode; 83 import jdk.nashorn.internal.ir.ExecuteNode; 84 import jdk.nashorn.internal.ir.ForNode; 85 import jdk.nashorn.internal.ir.FunctionNode; 86 import jdk.nashorn.internal.ir.IdentNode; 87 import jdk.nashorn.internal.ir.IfNode; 88 import jdk.nashorn.internal.ir.IndexNode; 89 import jdk.nashorn.internal.ir.LineNumberNode; 90 import jdk.nashorn.internal.ir.LiteralNode; 91 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; 92 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; 93 import jdk.nashorn.internal.ir.Node; 94 import jdk.nashorn.internal.ir.ObjectNode; 95 import jdk.nashorn.internal.ir.PropertyNode; 96 import jdk.nashorn.internal.ir.ReferenceNode; 97 import jdk.nashorn.internal.ir.ReturnNode; 98 import jdk.nashorn.internal.ir.RuntimeNode; 99 import jdk.nashorn.internal.ir.RuntimeNode.Request; 100 import jdk.nashorn.internal.ir.SplitNode; 101 import jdk.nashorn.internal.ir.SwitchNode; 102 import jdk.nashorn.internal.ir.Symbol; 103 import jdk.nashorn.internal.ir.TernaryNode; 104 import jdk.nashorn.internal.ir.ThrowNode; 105 import jdk.nashorn.internal.ir.TryNode; 106 import jdk.nashorn.internal.ir.UnaryNode; 107 import jdk.nashorn.internal.ir.VarNode; 108 import jdk.nashorn.internal.ir.WhileNode; 109 import jdk.nashorn.internal.ir.WithNode; 110 import jdk.nashorn.internal.ir.debug.ASTWriter; 111 import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; 112 import jdk.nashorn.internal.ir.visitor.NodeVisitor; 113 import jdk.nashorn.internal.parser.Lexer.RegexToken; 114 import jdk.nashorn.internal.parser.TokenType; 115 import jdk.nashorn.internal.runtime.Context; 116 import jdk.nashorn.internal.runtime.ECMAException; 117 import jdk.nashorn.internal.runtime.PropertyMap; 118 import jdk.nashorn.internal.runtime.Scope; 119 import jdk.nashorn.internal.runtime.ScriptFunction; 120 import jdk.nashorn.internal.runtime.ScriptObject; 121 import jdk.nashorn.internal.runtime.ScriptRuntime; 122 import jdk.nashorn.internal.runtime.Source; 123 import jdk.nashorn.internal.runtime.Undefined; 124 import jdk.nashorn.internal.runtime.linker.LinkerCallSite; 125 126 /** 127 * This is the lowest tier of the code generator. It takes lowered ASTs emitted 128 * from Lower and emits Java byte code. The byte code emission logic is broken 129 * out into MethodEmitter. MethodEmitter works internally with a type stack, and 130 * keeps track of the contents of the byte code stack. This way we avoid a large 131 * number of special cases on the form 132 * <pre> 133 * if (type == INT) { 134 * visitInsn(ILOAD, slot); 135 * } else if (type == DOUBLE) { 136 * visitInsn(DOUBLE, slot); 137 * } 138 * </pre> 139 * This quickly became apparent when the code generator was generalized to work 140 * with all types, and not just numbers or objects. 141 * <p> 142 * The CodeGenerator visits nodes only once, tags them as resolved and emits 143 * bytecode for them. 144 */ 145 public final class CodeGenerator extends NodeOperatorVisitor { 146 147 /** Current compiler */ 148 private final Compiler compiler; 149 150 /** Compiler context */ 151 private final Context context; 152 153 /** Call site flags given to the code generator to be used for all generated call sites */ 154 private final int callSiteFlags; 155 156 /** How many regexp fields have been emitted */ 157 private int regexFieldCount; 158 159 /** Map of shared scope call sites */ 160 private final Map<SharedScopeCall, SharedScopeCall> scopeCalls = new HashMap<>(); 161 162 /** When should we stop caching regexp expressions in fields to limit bytecode size? */ 163 private static final int MAX_REGEX_FIELDS = 2 * 1024; 164 165 /** 166 * Constructor. 167 * 168 * @param compiler 169 */ 170 CodeGenerator(final Compiler compiler) { 171 this.compiler = compiler; 172 this.context = compiler.getContext(); 173 this.callSiteFlags = context._callsite_flags; 174 } 175 176 /** 177 * Get the compiler 178 * 179 * @return the compiler used 180 */ 181 public Compiler getCompiler() { 182 return compiler; 183 } 184 185 /** 186 * Gets the call site flags, adding the strict flag if the current function 187 * being generated is in strict mode 188 * 189 * @return the correct flags for a call site in the current function 190 */ 191 public int getCallSiteFlags() { 192 return getCurrentFunctionNode().isStrictMode() ? callSiteFlags | CALLSITE_STRICT : callSiteFlags; 193 } 194 195 /** 196 * Load an identity node 197 * 198 * @param identNode an identity node to load 199 * @return the method generator used 200 */ 201 private MethodEmitter loadIdent(final IdentNode identNode) { 202 final Symbol symbol = identNode.getSymbol(); 203 204 if (!symbol.isScope()) { 205 assert symbol.hasSlot() && symbol.getSlot() != 0 || symbol.isThis(); 206 return method.load(symbol); 207 } 208 209 final String name = symbol.getName(); 210 211 if (CompilerConstants.__FILE__.name().equals(name)) { 212 return method.load(identNode.getSource().getName()); 213 } else if (CompilerConstants.__DIR__.name().equals(name)) { 214 return method.load(identNode.getSource().getBase()); 215 } else if (CompilerConstants.__LINE__.name().equals(name)) { 216 return method.load(identNode.getSource().getLine(identNode.position())).convert(Type.OBJECT); 217 } else { 218 assert identNode.getSymbol().isScope() : identNode + " is not in scope!"; 219 220 final int flags = CALLSITE_SCOPE | getCallSiteFlags(); 221 method.loadScope(); 222 223 if (symbol.isFastScope(getCurrentFunctionNode())) { 224 // Only generate shared scope getter for fast-scope symbols so we know we can dial in correct scope. 225 if (symbol.getUseCount() > SharedScopeCall.FAST_SCOPE_GET_THRESHOLD) { 226 return loadSharedScopeVar(identNode.getType(), symbol, flags); 227 } 228 return loadFastScopeVar(identNode.getType(), symbol, flags, identNode.isFunction()); 229 } 230 return method.dynamicGet(identNode.getType(), identNode.getName(), flags, identNode.isFunction()); 231 } 232 } 233 234 private MethodEmitter loadSharedScopeVar(final Type valueType, final Symbol symbol, final int flags) { 235 method.load(symbol.isFastScope(getCurrentFunctionNode()) ? getScopeProtoDepth(getCurrentBlock(), symbol) : -1); 236 final SharedScopeCall scopeCall = getScopeGet(valueType, symbol, flags | CALLSITE_FAST_SCOPE); 237 scopeCall.generateInvoke(method); 238 return method; 239 } 240 241 private MethodEmitter loadFastScopeVar(final Type valueType, final Symbol symbol, final int flags, final boolean isMethod) { 242 loadFastScopeProto(symbol, false); 243 method.dynamicGet(valueType, symbol.getName(), flags | CALLSITE_FAST_SCOPE, isMethod); 244 return method; 245 } 246 247 private MethodEmitter storeFastScopeVar(final Type valueType, final Symbol symbol, final int flags) { 248 loadFastScopeProto(symbol, true); 249 method.dynamicSet(valueType, symbol.getName(), flags | CALLSITE_FAST_SCOPE); 250 return method; 251 } 252 253 private static int getScopeProtoDepth(final Block currentBlock, final Symbol symbol) { 254 if (currentBlock == symbol.getBlock()) { 255 return 0; 256 } 257 258 final int delta = currentBlock.needsScope() ? 1 : 0; 259 final Block parentBlock = currentBlock.getParent(); 260 261 if (parentBlock != null) { 262 final int result = getScopeProtoDepth(parentBlock, symbol); 263 if (result != -1) { 264 return delta + result; 265 } 266 } 267 268 if (currentBlock instanceof FunctionNode) { 269 for (final Block lookupBlock : ((FunctionNode)currentBlock).getReferencingParentBlocks()) { 270 final int result = getScopeProtoDepth(lookupBlock, symbol); 271 if (result != -1) { 272 return delta + result; 273 } 274 } 275 } 276 277 return -1; 278 } 279 280 private void loadFastScopeProto(final Symbol symbol, final boolean swap) { 281 final int depth = getScopeProtoDepth(getCurrentBlock(), symbol); 282 assert depth != -1; 283 if(depth > 0) { 284 if (swap) { 285 method.swap(); 286 } 287 for (int i = 0; i < depth; i++) { 288 method.invoke(ScriptObject.GET_PROTO); 289 } 290 if (swap) { 291 method.swap(); 292 } 293 } 294 } 295 296 /** 297 * Generate code that loads this node to the stack. This method is only 298 * public to be accessible from the maps sub package. Do not call externally 299 * 300 * @param node node to load 301 * 302 * @return the method emitter used 303 */ 304 public MethodEmitter load(final Node node) { 305 return load(node, false); 306 } 307 308 private MethodEmitter load(final Node node, final boolean baseAlreadyOnStack) { 309 final Symbol symbol = node.getSymbol(); 310 311 // If we lack symbols, we just generate what we see. 312 if (symbol == null) { 313 node.accept(this); 314 return method; 315 } 316 317 /* 318 * The load may be of type IdentNode, e.g. "x", AccessNode, e.g. "x.y" 319 * or IndexNode e.g. "x[y]". Both AccessNodes and IndexNodes are 320 * BaseNodes and the logic for loading the base object is reused 321 */ 322 final CodeGenerator codegen = this; 323 324 node.accept(new NodeVisitor(compileUnit, method) { 325 @Override 326 public Node enter(final IdentNode identNode) { 327 loadIdent(identNode); 328 return null; 329 } 330 331 @Override 332 public Node enter(final AccessNode accessNode) { 333 if (!baseAlreadyOnStack) { 334 load(accessNode.getBase()).convert(Type.OBJECT); 335 } 336 assert method.peekType().isObject(); 337 method.dynamicGet(node.getType(), accessNode.getProperty().getName(), getCallSiteFlags(), accessNode.isFunction()); 338 return null; 339 } 340 341 @Override 342 public Node enter(final IndexNode indexNode) { 343 if (!baseAlreadyOnStack) { 344 load(indexNode.getBase()); 345 load(indexNode.getIndex()); 346 } 347 method.dynamicGetIndex(node.getType(), getCallSiteFlags(), indexNode.isFunction()); 348 return null; 349 } 350 351 @Override 352 public Node enterDefault(final Node otherNode) { 353 otherNode.accept(codegen); // generate code for whatever we are looking at. 354 method.load(symbol); // load the final symbol to the stack (or nop if no slot, then result is already there) 355 return null; 356 } 357 }); 358 359 return method; 360 } 361 362 @Override 363 public Node enter(final AccessNode accessNode) { 364 if (accessNode.testResolved()) { 365 return null; 366 } 367 368 load(accessNode); 369 370 return null; 371 } 372 373 /** 374 * Initialize a specific set of vars to undefined. This has to be done at 375 * the start of each method for local variables that aren't passed as 376 * parameters. 377 * 378 * @param symbols list of symbols. 379 */ 380 private void initSymbols(final Iterable<Symbol> symbols) { 381 final LinkedList<Symbol> numbers = new LinkedList<>(); 382 final LinkedList<Symbol> objects = new LinkedList<>(); 383 384 for (final Symbol symbol : symbols) { 385 /* 386 * The following symbols are guaranteed to be defined and thus safe 387 * from having unsigned written to them: parameters internals this 388 * 389 * Otherwise we must, unless we perform control/escape analysis, 390 * assign them undefined. 391 */ 392 final boolean isInternal = symbol.isParam() || symbol.isInternal() || symbol.isThis() || !symbol.canBeUndefined(); 393 394 if (symbol.hasSlot() && !isInternal) { 395 assert symbol.getSymbolType().isNumber() || symbol.getSymbolType().isObject() : "no potentially undefined narrower local vars than doubles are allowed: " + symbol + " in " + getCurrentFunctionNode(); 396 if (symbol.getSymbolType().isNumber()) { 397 numbers.add(symbol); 398 } else if (symbol.getSymbolType().isObject()) { 399 objects.add(symbol); 400 } 401 } 402 } 403 404 initSymbols(numbers, Type.NUMBER); 405 initSymbols(objects, Type.OBJECT); 406 } 407 408 private void initSymbols(final LinkedList<Symbol> symbols, final Type type) { 409 if (symbols.isEmpty()) { 410 return; 411 } 412 413 method.loadUndefined(type); 414 while (!symbols.isEmpty()) { 415 final Symbol symbol = symbols.removeFirst(); 416 if (!symbols.isEmpty()) { 417 method.dup(); 418 } 419 method.store(symbol); 420 } 421 } 422 423 /** 424 * Create symbol debug information. 425 * 426 * @param block block containing symbols. 427 */ 428 private void symbolInfo(final Block block) { 429 for (final Symbol symbol : block.getFrame().getSymbols()) { 430 method.localVariable(symbol, block.getEntryLabel(), block.getBreakLabel()); 431 } 432 } 433 434 @Override 435 public Node enter(final Block block) { 436 if (block.testResolved()) { 437 return null; 438 } 439 440 method.label(block.getEntryLabel()); 441 initLocals(block); 442 443 return block; 444 } 445 446 @Override 447 public Node leave(final Block block) { 448 method.label(block.getBreakLabel()); 449 symbolInfo(block); 450 451 if (block.needsScope()) { 452 popBlockScope(block); 453 } 454 455 return block; 456 } 457 458 private void popBlockScope(final Block block) { 459 final Label exitLabel = new Label("block_exit"); 460 final Label recoveryLabel = new Label("block_catch"); 461 final Label skipLabel = new Label("skip_catch"); 462 463 /* pop scope a la try-finally */ 464 method.loadScope(); 465 method.invoke(ScriptObject.GET_PROTO); 466 method.storeScope(); 467 method._goto(skipLabel); 468 method.label(exitLabel); 469 470 method._catch(recoveryLabel); 471 method.loadScope(); 472 method.invoke(ScriptObject.GET_PROTO); 473 method.storeScope(); 474 method.athrow(); 475 method.label(skipLabel); 476 method._try(block.getEntryLabel(), exitLabel, recoveryLabel, Throwable.class); 477 } 478 479 @Override 480 public Node enter(final BreakNode breakNode) { 481 if (breakNode.testResolved()) { 482 return null; 483 } 484 485 for (int i = 0; i < breakNode.getScopeNestingLevel(); i++) { 486 closeWith(); 487 } 488 489 method.splitAwareGoto(breakNode.getTargetLabel()); 490 491 return null; 492 } 493 494 private MethodEmitter loadArgs(final List<Node> args) { 495 return loadArgs(args, null, false, args.size()); 496 } 497 498 private MethodEmitter loadArgs(final List<Node> args, final String signature, final boolean isVarArg, final int argCount) { 499 // arg have already been converted to objects here. 500 if (isVarArg || argCount > LinkerCallSite.ARGLIMIT) { 501 loadArgsArray(args); 502 return method; 503 } 504 505 // pad with undefined if size is too short. argCount is the real number of args 506 int n = 0; 507 final Type[] params = signature == null ? null : Type.getMethodArguments(signature); 508 for (final Node arg : args) { 509 assert arg != null; 510 load(arg); 511 if (n >= argCount) { 512 method.pop(); // we had to load the arg for its side effects 513 } else if (params != null) { 514 method.convert(params[n]); 515 } 516 n++; 517 } 518 519 while (n < argCount) { 520 method.loadUndefined(Type.OBJECT); 521 n++; 522 } 523 524 return method; 525 } 526 527 /** 528 * Create a new function object, including generating its stub code. 529 * 530 * @param functionNode FunctionNode to utilize. 531 */ 532 private void newFunctionObject(final FunctionNode functionNode) { 533 // Turn thisProperties into keys and symbols for the FunctionAnalyzer 534 final Map<String, Node> thisProperties = functionNode.getThisProperties(); 535 536 final List<String> keys = new ArrayList<>(); 537 final List<Symbol> symbols = new ArrayList<>(); 538 539 /* TODO - Predefine known properties. 540 for (final Entry<String, Node> entry : thisProperties.entrySet()) { 541 keys.add(entry.getKey()); 542 symbols.add(entry.getValue().getSymbol()); 543 } 544 */ 545 546 new FunctionObjectCreator(this, functionNode, keys, symbols).makeObject(method); 547 } 548 549 @Override 550 public Node enter(final CallNode callNode) { 551 if (callNode.testResolved()) { 552 return null; 553 } 554 555 final List<Node> args = callNode.getArgs(); 556 final Node function = callNode.getFunction(); 557 final FunctionNode currentFunction = getCurrentFunctionNode(); 558 final Block currentBlock = getCurrentBlock(); 559 560 function.accept(new NodeVisitor(compileUnit, method) { 561 562 private void sharedScopeCall(final IdentNode identNode, final int flags) { 563 final Symbol symbol = identNode.getSymbol(); 564 int scopeCallFlags = flags; 565 method.loadScope(); 566 if (symbol.isFastScope(currentFunction)) { 567 method.load(getScopeProtoDepth(currentBlock, symbol)); 568 scopeCallFlags |= CALLSITE_FAST_SCOPE; 569 } else { 570 method.load(-1); // Bypass fast-scope code in shared callsite 571 } 572 loadArgs(args); 573 final Type[] paramTypes = method.getTypesFromStack(args.size()); 574 final SharedScopeCall scopeCall = getScopeCall(symbol, identNode.getType(), callNode.getType(), paramTypes, scopeCallFlags); 575 scopeCall.generateInvoke(method); 576 } 577 578 private void scopeCall(final IdentNode node, final int flags) { 579 load(node); 580 method.convert(Type.OBJECT); // foo() makes no sense if foo == 3 581 // ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly. 582 method.loadNull(); 583 loadArgs(args); 584 method.dynamicCall(callNode.getType(), args.size(), flags); 585 } 586 587 private void evalCall(final IdentNode node, final int flags) { 588 load(node); 589 method.convert(Type.OBJECT); // foo() makes no sense if foo == 3 590 591 final Label not_eval = new Label("not_eval"); 592 final Label eval_done = new Label("eval_done"); 593 594 // check if this is the real built-in eval 595 method.dup(); 596 globalIsEval(); 597 598 method.ifeq(not_eval); 599 // We don't need ScriptFunction object for 'eval' 600 method.pop(); 601 602 method.loadScope(); // Load up self (scope). 603 604 final CallNode.EvalArgs evalArgs = callNode.getEvalArgs(); 605 // load evaluated code 606 load(evalArgs.code); 607 method.convert(Type.OBJECT); 608 // special/extra 'eval' arguments 609 load(evalArgs.evalThis); 610 method.load(evalArgs.location); 611 method.load(evalArgs.strictMode); 612 method.convert(Type.OBJECT); 613 614 // direct call to Global.directEval 615 globalDirectEval(); 616 method.convert(callNode.getType()); 617 method._goto(eval_done); 618 619 method.label(not_eval); 620 // This is some scope 'eval' or global eval replaced by user 621 // but not the built-in ECMAScript 'eval' function call 622 method.loadNull(); 623 loadArgs(args); 624 method.dynamicCall(callNode.getType(), args.size(), flags); 625 626 method.label(eval_done); 627 } 628 629 @Override 630 public Node enter(final IdentNode node) { 631 final Symbol symbol = node.getSymbol(); 632 633 if (symbol.isScope()) { 634 final int flags = getCallSiteFlags() | CALLSITE_SCOPE; 635 final int useCount = symbol.getUseCount(); 636 637 // Threshold for generating shared scope callsite is lower for fast scope symbols because we know 638 // we can dial in the correct scope. However, we als need to enable it for non-fast scopes to 639 // support huge scripts like mandreel.js. 640 if (callNode.isEval()) { 641 evalCall(node, flags); 642 } else if (useCount <= SharedScopeCall.FAST_SCOPE_CALL_THRESHOLD 643 || (!symbol.isFastScope(currentFunction) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD) 644 || callNode.inWithBlock()) { 645 scopeCall(node, flags); 646 } else { 647 sharedScopeCall(node, flags); 648 } 649 assert method.peekType().equals(callNode.getType()); 650 } else { 651 enterDefault(node); 652 } 653 654 return null; 655 } 656 657 @Override 658 public Node enter(final AccessNode node) { 659 load(node.getBase()); 660 method.convert(Type.OBJECT); 661 method.dup(); 662 method.dynamicGet(node.getType(), node.getProperty().getName(), getCallSiteFlags(), true); 663 method.swap(); 664 loadArgs(args); 665 method.dynamicCall(callNode.getType(), args.size(), getCallSiteFlags()); 666 assert method.peekType().equals(callNode.getType()); 667 668 return null; 669 } 670 671 @Override 672 public Node enter(final ReferenceNode node) { 673 final FunctionNode callee = node.getReference(); 674 final boolean isVarArg = callee.isVarArg(); 675 final int argCount = isVarArg ? -1 : callee.getParameters().size(); 676 677 final String signature = new FunctionSignature(true, callee.needsCallee(), callee.getReturnType(), isVarArg ? null : callee.getParameters()).toString(); 678 679 if (callee.isStrictMode()) { // self is undefined 680 method.loadUndefined(Type.OBJECT); 681 } else { // get global from scope (which is the self) 682 globalInstance(); 683 } 684 685 if (callee.needsCallee()) { // TODO: always true 686 newFunctionObject(callee); // TODO: if callee not needed, function object is used only to pass scope (could be optimized). if neither the scope nor the function object is needed by the callee, we can pass null instead. 687 } 688 689 loadArgs(args, signature, isVarArg, argCount); 690 method.invokeStatic(callee.getCompileUnit().getUnitClassName(), callee.getName(), signature); 691 assert method.peekType().equals(callee.getReturnType()) : method.peekType() + " != " + callee.getReturnType(); 692 693 return null; 694 } 695 696 @Override 697 public Node enter(final IndexNode node) { 698 load(node.getBase()); 699 method.convert(Type.OBJECT); 700 method.dup(); 701 load(node.getIndex()); 702 final Type indexType = node.getIndex().getType(); 703 if (indexType.isObject() || indexType.isBoolean()) { 704 method.convert(Type.OBJECT); //TODO 705 } 706 method.dynamicGetIndex(node.getType(), getCallSiteFlags(), true); 707 method.swap(); 708 loadArgs(args); 709 method.dynamicCall(callNode.getType(), args.size(), getCallSiteFlags()); 710 assert method.peekType().equals(callNode.getType()); 711 712 return null; 713 } 714 715 @Override 716 protected Node enterDefault(final Node node) { 717 // Load up function. 718 load(function); 719 method.convert(Type.OBJECT); //TODO, e.g. booleans can be used as functions 720 method.loadNull(); // ScriptFunction will figure out the correct this when it sees CALLSITE_SCOPE 721 722 loadArgs(args); 723 method.dynamicCall(callNode.getType(), args.size(), getCallSiteFlags() | CALLSITE_SCOPE); 724 assert method.peekType().equals(callNode.getType()); 725 726 return null; 727 } 728 }); 729 730 method.store(callNode.getSymbol()); 731 732 return null; 733 } 734 735 @Override 736 public Node enter(final ContinueNode continueNode) { 737 if (continueNode.testResolved()) { 738 return null; 739 } 740 741 for (int i = 0; i < continueNode.getScopeNestingLevel(); i++) { 742 closeWith(); 743 } 744 745 method.splitAwareGoto(continueNode.getTargetLabel()); 746 747 return null; 748 } 749 750 @Override 751 public Node enter(final DoWhileNode doWhileNode) { 752 return enter((WhileNode)doWhileNode); 753 } 754 755 @Override 756 public Node enter(final EmptyNode emptyNode) { 757 return null; 758 } 759 760 @Override 761 public Node enter(final ExecuteNode executeNode) { 762 if (executeNode.testResolved()) { 763 return null; 764 } 765 766 final Node expression = executeNode.getExpression(); 767 expression.accept(this); 768 769 return null; 770 } 771 772 @Override 773 public Node enter(final ForNode forNode) { 774 if (forNode.testResolved()) { 775 return null; 776 } 777 778 final Node test = forNode.getTest(); 779 final Block body = forNode.getBody(); 780 final Node modify = forNode.getModify(); 781 782 final Label breakLabel = forNode.getBreakLabel(); 783 final Label continueLabel = forNode.getContinueLabel(); 784 final Label loopLabel = new Label("loop"); 785 786 Node init = forNode.getInit(); 787 788 if (forNode.isForIn()) { 789 final Symbol iter = forNode.getIterator(); 790 791 // We have to evaluate the optional initializer expression 792 // of the iterator variable of the for-in statement. 793 if (init instanceof VarNode) { 794 init.accept(this); 795 init = ((VarNode)init).getName(); 796 } 797 798 load(modify); 799 assert modify.getType().isObject(); 800 method.invoke(forNode.isForEach() ? ScriptRuntime.TO_VALUE_ITERATOR : ScriptRuntime.TO_PROPERTY_ITERATOR); 801 method.store(iter); 802 method._goto(continueLabel); 803 method.label(loopLabel); 804 805 new Store<Node>(init) { 806 @Override 807 protected void evaluate() { 808 method.load(iter); 809 method.invoke(interfaceCallNoLookup(Iterator.class, "next", Object.class)); 810 } 811 }.store(); 812 813 body.accept(this); 814 815 method.label(continueLabel); 816 method.load(iter); 817 method.invoke(interfaceCallNoLookup(Iterator.class, "hasNext", boolean.class)); 818 method.ifne(loopLabel); 819 method.label(breakLabel); 820 } else { 821 if (init != null) { 822 init.accept(this); 823 } 824 825 final Label testLabel = new Label("test"); 826 827 method._goto(testLabel); 828 method.label(loopLabel); 829 body.accept(this); 830 method.label(continueLabel); 831 832 if (!body.isTerminal() && modify != null) { 833 load(modify); 834 } 835 836 method.label(testLabel); 837 if (test != null) { 838 new BranchOptimizer(this, method).execute(test, loopLabel, true); 839 } else { 840 method._goto(loopLabel); 841 } 842 843 method.label(breakLabel); 844 } 845 846 return null; 847 } 848 849 /** 850 * Initialize the slots in a frame to undefined. 851 * 852 * @param block block with local vars. 853 */ 854 private void initLocals(final Block block) { 855 final FunctionNode function = block.getFunction(); 856 final boolean isFunctionNode = block == function; 857 858 /* 859 * Get the symbols from the frame and realign the frame so that all 860 * slots get correct numbers. The slot numbering is not fixed until 861 * after initLocals has been run 862 */ 863 final Frame frame = block.getFrame(); 864 final List<Symbol> symbols = frame.getSymbols(); 865 866 /* Fix the predefined slots so they have numbers >= 0, like varargs. */ 867 frame.realign(); 868 869 /* 870 * Determine if block needs scope, if not, just do initSymbols for this block. 871 */ 872 if (block.needsScope()) { 873 /* 874 * Determine if function is varargs and consequently variables have to 875 * be in the scope. 876 */ 877 final boolean isVarArg = function.isVarArg(); 878 final boolean varsInScope = function.varsInScope(); 879 880 // TODO for LET we can do better: if *block* does not contain any eval/with, we don't need its vars in scope. 881 882 final List<String> nameList = new ArrayList<>(); 883 final List<Symbol> locals = new ArrayList<>(); 884 885 // If there are variable arguments, we need to load them (functions only). 886 if (isFunctionNode && isVarArg) { 887 method.loadVarArgs(); 888 method.loadCallee(); 889 method.load(function.getParameters().size()); 890 globalAllocateArguments(); 891 method.storeArguments(); 892 } 893 894 // Initalize symbols and values 895 final List<Symbol> newSymbols = new ArrayList<>(); 896 final List<Symbol> values = new ArrayList<>(); 897 898 for (final Symbol symbol : symbols) { 899 if (symbol.isInternal() || symbol.isThis()) { 900 continue; 901 } 902 903 if (symbol.isVar() && (varsInScope || symbol.isScope())) { 904 nameList.add(symbol.getName()); 905 newSymbols.add(symbol); 906 values.add(null); 907 assert symbol.isScope() : "scope for " + symbol + " should have been set in Lower already " + function.getName(); 908 assert !symbol.hasSlot() : "slot for " + symbol + " should have been removed in Lower already" + function.getName(); 909 } else if (symbol.isVar()) { 910 assert symbol.hasSlot() : symbol + " should have a slot only, no scope"; 911 locals.add(symbol); 912 } else if (symbol.isParam() && (varsInScope || isVarArg || symbol.isScope())) { 913 nameList.add(symbol.getName()); 914 newSymbols.add(symbol); 915 values.add(isVarArg ? null : symbol); 916 assert symbol.isScope() : "scope for " + symbol + " should have been set in Lower already " + function.getName() + " varsInScope="+varsInScope+" isVarArg="+isVarArg+" symbol.isScope()=" + symbol.isScope(); 917 assert !(isVarArg && symbol.hasSlot()) : "slot for " + symbol + " should have been removed in Lower already " + function.getName(); 918 } 919 } 920 921 /* Correct slot numbering again */ 922 frame.realign(); 923 924 // we may have locals that need to be initialized 925 initSymbols(locals); 926 927 if (isFunctionNode) { 928 initScope(); 929 } 930 931 /* 932 * Create a new object based on the symbols and values, generate 933 * bootstrap code for object 934 */ 935 final FieldObjectCreator<Symbol> foc = new FieldObjectCreator<Symbol>(this, nameList, newSymbols, values, true, isVarArg) { 936 @Override 937 protected Type getValueType(final Symbol value) { 938 return value.getSymbolType(); 939 } 940 941 @Override 942 protected void loadValue(final Symbol value) { 943 method.load(value); 944 } 945 }; 946 foc.makeObject(method); 947 948 // runScript(): merge scope into global 949 if (isFunctionNode && function.isScript()) { 950 method.invoke(ScriptRuntime.MERGE_SCOPE); 951 } 952 953 method.storeScope(); 954 } else { 955 initSymbols(symbols); 956 957 if (isFunctionNode) { 958 initScope(); 959 } 960 } 961 962 // Debugging: print symbols? @see --print-symbols flag 963 printSymbols(block, (isFunctionNode ? "Function " : "Block in ") + (function.getIdent() == null ? "<anonymous>" : function.getIdent().getName())); 964 } 965 966 private void initScope() { 967 method.loadCallee(); 968 method.invoke(ScriptFunction.GET_SCOPE); 969 method.storeScope(); 970 } 971 972 @Override 973 public Node enter(final FunctionNode functionNode) { 974 if (functionNode.testResolved()) { 975 return null; 976 } 977 978 compileUnit = functionNode.getCompileUnit(); 979 assert compileUnit != null; 980 981 method = compileUnit.getClassEmitter().method(functionNode); 982 functionNode.setMethodEmitter(method); 983 // Mark end for variable tables. 984 method.begin(); 985 method.label(functionNode.getEntryLabel()); 986 987 initLocals(functionNode); 988 989 return functionNode; 990 } 991 992 @Override 993 public Node leave(final FunctionNode functionNode) { 994 // Mark end for variable tables. 995 method.label(functionNode.getBreakLabel()); 996 997 if (!functionNode.needsScope()) { 998 method.markerVariable(LEAF.tag(), functionNode.getEntryLabel(), functionNode.getBreakLabel()); 999 } 1000 1001 symbolInfo(functionNode); 1002 try { 1003 method.end(); // wrap up this method 1004 } catch (final Throwable t) { 1005 Context.printStackTrace(t); 1006 final VerifyError e = new VerifyError("Code generation bug in \"" + functionNode.getName() + "\": likely stack misaligned: " + t + " " + functionNode.getSource().getName()); 1007 e.initCause(t); 1008 throw e; 1009 } 1010 1011 return functionNode; 1012 } 1013 1014 @Override 1015 public Node enter(final IdentNode identNode) { 1016 return null; 1017 } 1018 1019 @Override 1020 public Node enter(final IfNode ifNode) { 1021 if (ifNode.testResolved()) { 1022 return null; 1023 } 1024 1025 final Node test = ifNode.getTest(); 1026 final Block pass = ifNode.getPass(); 1027 final Block fail = ifNode.getFail(); 1028 1029 final Label failLabel = new Label("if_fail"); 1030 final Label afterLabel = fail == null ? failLabel : new Label("if_done"); 1031 1032 new BranchOptimizer(this, method).execute(test, failLabel, false); 1033 1034 boolean passTerminal = false; 1035 boolean failTerminal = false; 1036 1037 pass.accept(this); 1038 if (!pass.hasTerminalFlags()) { 1039 method._goto(afterLabel); //don't fallthru to fail block 1040 } else { 1041 passTerminal = pass.isTerminal(); 1042 } 1043 1044 if (fail != null) { 1045 method.label(failLabel); 1046 fail.accept(this); 1047 failTerminal = fail.isTerminal(); 1048 } 1049 1050 //if if terminates, put the after label there 1051 if (!passTerminal || !failTerminal) { 1052 method.label(afterLabel); 1053 } 1054 1055 return null; 1056 } 1057 1058 @Override 1059 public Node enter(final IndexNode indexNode) { 1060 if (indexNode.testResolved()) { 1061 return null; 1062 } 1063 1064 load(indexNode); 1065 1066 return null; 1067 } 1068 1069 @Override 1070 public Node enter(final LineNumberNode lineNumberNode) { 1071 if (lineNumberNode.testResolved()) { 1072 return null; 1073 } 1074 1075 final Label label = new Label("line:" + lineNumberNode.getLineNumber() + " (" + getCurrentFunctionNode().getName() + ")"); 1076 method.label(label); 1077 method.lineNumber(lineNumberNode.getLineNumber(), label); 1078 1079 return null; 1080 } 1081 1082 /** 1083 * Load a list of nodes as an array of a specific type 1084 * The array will contain the visited nodes. 1085 * 1086 * @param arrayLiteralNode the array of contents 1087 * @param arrayType the type of the array, e.g. ARRAY_NUMBER or ARRAY_OBJECT 1088 * 1089 * @return the method generator that was used 1090 */ 1091 private MethodEmitter loadArray(final ArrayLiteralNode arrayLiteralNode, final ArrayType arrayType) { 1092 assert arrayType == Type.INT_ARRAY || arrayType == Type.NUMBER_ARRAY || arrayType == Type.OBJECT_ARRAY; 1093 1094 final Node[] nodes = arrayLiteralNode.getValue(); 1095 final Object presets = arrayLiteralNode.getPresets(); 1096 final int[] postsets = arrayLiteralNode.getPostsets(); 1097 final Class<?> type = arrayType.getTypeClass(); 1098 final List<ArrayUnit> units = arrayLiteralNode.getUnits(); 1099 1100 loadConstant(presets); 1101 1102 final Type elementType = arrayType.getElementType(); 1103 1104 if (units != null) { 1105 final CompileUnit savedCompileUnit = compileUnit; 1106 final MethodEmitter savedMethod = method; 1107 1108 try { 1109 for (final ArrayUnit unit : units) { 1110 compileUnit = unit.getCompileUnit(); 1111 1112 final String className = compileUnit.getUnitClassName(); 1113 final String name = compiler.uniqueName(SPLIT_PREFIX.tag()); 1114 final String signature = methodDescriptor(type, Object.class, ScriptFunction.class, ScriptObject.class, type); 1115 1116 method = compileUnit.getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature); 1117 method.setFunctionNode(getCurrentFunctionNode()); 1118 method.begin(); 1119 1120 fixScopeSlot(); 1121 1122 method.load(arrayType, SPLIT_ARRAY_ARG.slot()); 1123 1124 for (int i = unit.getLo(); i < unit.getHi(); i++) { 1125 storeElement(nodes, elementType, postsets[i]); 1126 } 1127 1128 method._return(); 1129 method.end(); 1130 1131 savedMethod.loadThis(); 1132 savedMethod.swap(); 1133 savedMethod.loadCallee(); 1134 savedMethod.swap(); 1135 savedMethod.loadScope(); 1136 savedMethod.swap(); 1137 savedMethod.invokeStatic(className, name, signature); 1138 } 1139 } finally { 1140 compileUnit = savedCompileUnit; 1141 method = savedMethod; 1142 } 1143 1144 return method; 1145 } 1146 1147 for (final int postset : postsets) { 1148 storeElement(nodes, elementType, postset); 1149 } 1150 1151 return method; 1152 } 1153 1154 private void storeElement(final Node[] nodes, final Type elementType, final int index) { 1155 method.dup(); 1156 method.load(index); 1157 1158 final Node element = nodes[index]; 1159 1160 if (element == null) { 1161 method.loadEmpty(elementType); 1162 } else { 1163 assert elementType.isEquivalentTo(element.getType()) : "array element type doesn't match array type"; 1164 load(element); 1165 } 1166 1167 method.arraystore(); 1168 } 1169 1170 private MethodEmitter loadArgsArray(final List<Node> args) { 1171 final Object[] array = new Object[args.size()]; 1172 loadConstant(array); 1173 1174 for (int i = 0; i < args.size(); i++) { 1175 method.dup(); 1176 method.load(i); 1177 load(args.get(i)).convert(Type.OBJECT); //has to be upcast to object or we fail 1178 method.arraystore(); 1179 } 1180 1181 return method; 1182 } 1183 1184 /** 1185 * Load a constant from the constant array. This is only public to be callable from the objects 1186 * subpackage. Do not call directly. 1187 * 1188 * @param string string to load 1189 */ 1190 public void loadConstant(final String string) { 1191 final String unitClassName = compileUnit.getUnitClassName(); 1192 final ClassEmitter classEmitter = compileUnit.getClassEmitter(); 1193 final int index = compiler.getConstantData().add(string); 1194 1195 method.load(index); 1196 method.invokeStatic(unitClassName, GET_STRING.tag(), methodDescriptor(String.class, int.class)); 1197 classEmitter.needGetConstantMethod(String.class); 1198 } 1199 1200 /** 1201 * Load a constant from the constant array. This is only public to be callable from the objects 1202 * subpackage. Do not call directly. 1203 * 1204 * @param object object to load 1205 */ 1206 public void loadConstant(final Object object) { 1207 final String unitClassName = compileUnit.getUnitClassName(); 1208 final ClassEmitter classEmitter = compileUnit.getClassEmitter(); 1209 final int index = compiler.getConstantData().add(object); 1210 final Class<?> cls = object.getClass(); 1211 1212 if (cls == PropertyMap.class) { 1213 method.load(index); 1214 method.invokeStatic(unitClassName, GET_MAP.tag(), methodDescriptor(PropertyMap.class, int.class)); 1215 classEmitter.needGetConstantMethod(PropertyMap.class); 1216 } else if (cls.isArray()) { 1217 method.load(index); 1218 final String methodName = ClassEmitter.getArrayMethodName(cls); 1219 method.invokeStatic(unitClassName, methodName, methodDescriptor(cls, int.class)); 1220 classEmitter.needGetConstantMethod(cls); 1221 } else { 1222 method.loadConstants(unitClassName).load(index).arrayload(); 1223 if (cls != Object.class) { 1224 method.checkcast(cls); 1225 } 1226 } 1227 } 1228 1229 // literal values 1230 private MethodEmitter load(final LiteralNode<?> node) { 1231 final Object value = node.getValue(); 1232 1233 if (value == null) { 1234 method.loadNull(); 1235 } else if (value instanceof Undefined) { 1236 method.loadUndefined(Type.OBJECT); 1237 } else if (value instanceof String) { 1238 final String string = (String)value; 1239 1240 if (string.length() > (MethodEmitter.LARGE_STRING_THRESHOLD / 3)) { // 3 == max bytes per encoded char 1241 loadConstant(string); 1242 } else { 1243 method.load(string); 1244 } 1245 } else if (value instanceof RegexToken) { 1246 loadRegex((RegexToken)value); 1247 } else if (value instanceof Boolean) { 1248 method.load((Boolean)value); 1249 } else if (value instanceof Integer) { 1250 method.load((Integer)value); 1251 } else if (value instanceof Long) { 1252 method.load((Long)value); 1253 } else if (value instanceof Double) { 1254 method.load((Double)value); 1255 } else if (node instanceof ArrayLiteralNode) { 1256 final ArrayType type = (ArrayType)node.getType(); 1257 loadArray((ArrayLiteralNode)node, type); 1258 globalAllocateArray(type); 1259 } else { 1260 assert false : "Unknown literal for " + node.getClass() + " " + value.getClass() + " " + value; 1261 } 1262 1263 return method; 1264 } 1265 1266 private MethodEmitter loadRegexToken(final RegexToken value) { 1267 method.load(value.getExpression()); 1268 method.load(value.getOptions()); 1269 return globalNewRegExp(); 1270 } 1271 1272 private MethodEmitter loadRegex(final RegexToken regexToken) { 1273 if (regexFieldCount > MAX_REGEX_FIELDS) { 1274 return loadRegexToken(regexToken); 1275 } 1276 // emit field 1277 final String regexName = compiler.uniqueName(REGEX_PREFIX.tag()); 1278 final ClassEmitter classEmitter = compileUnit.getClassEmitter(); 1279 1280 classEmitter.field(EnumSet.of(PRIVATE, STATIC), regexName, Object.class); 1281 regexFieldCount++; 1282 1283 // get field, if null create new regex, finally clone regex object 1284 method.getStatic(compileUnit.getUnitClassName(), regexName, typeDescriptor(Object.class)); 1285 method.dup(); 1286 final Label cachedLabel = new Label("cached"); 1287 method.ifnonnull(cachedLabel); 1288 1289 method.pop(); 1290 loadRegexToken(regexToken); 1291 method.dup(); 1292 method.putStatic(compileUnit.getUnitClassName(), regexName, typeDescriptor(Object.class)); 1293 1294 method.label(cachedLabel); 1295 globalRegExpCopy(); 1296 1297 return method; 1298 } 1299 1300 @SuppressWarnings("rawtypes") 1301 @Override 1302 public Node enter(final LiteralNode literalNode) { 1303 load(literalNode).store(literalNode.getSymbol()); 1304 return null; 1305 } 1306 1307 @Override 1308 public Node enter(final ObjectNode objectNode) { 1309 if (objectNode.testResolved()) { 1310 return null; 1311 } 1312 1313 final List<Node> elements = objectNode.getElements(); 1314 final int size = elements.size(); 1315 1316 final List<String> keys = new ArrayList<>(); 1317 final List<Symbol> symbols = new ArrayList<>(); 1318 final List<Node> values = new ArrayList<>(); 1319 1320 boolean hasGettersSetters = false; 1321 1322 for (int i = 0; i < size; i++) { 1323 final PropertyNode propertyNode = (PropertyNode)elements.get(i); 1324 final Node value = propertyNode.getValue(); 1325 final String key = propertyNode.getKeyName(); 1326 final Symbol symbol = value == null ? null : propertyNode.getSymbol(); 1327 1328 if (value == null) { 1329 hasGettersSetters = true; 1330 } 1331 1332 keys.add(key); 1333 symbols.add(symbol); 1334 values.add(value); 1335 } 1336 1337 new FieldObjectCreator<Node>(this, keys, symbols, values) { 1338 @Override 1339 protected Type getValueType(final Node node) { 1340 return node.getType(); 1341 } 1342 1343 @Override 1344 protected void loadValue(final Node node) { 1345 load(node); 1346 } 1347 1348 /** 1349 * Ensure that the properties start out as object types so that 1350 * we can do putfield initializations instead of dynamicSetIndex 1351 * which would be the case to determine initial property type 1352 * otherwise. 1353 * 1354 * Use case, it's very expensive to do a million var x = {a:obj, b:obj} 1355 * just to have to invalidate them immediately on initialization 1356 * 1357 * see NASHORN-594 1358 */ 1359 @Override 1360 protected MapCreator newMapCreator(final Class<?> fieldObjectClass) { 1361 return new ObjectMapCreator(fieldObjectClass, keys, symbols); 1362 } 1363 1364 }.makeObject(method); 1365 1366 method.dup(); 1367 globalObjectPrototype(); 1368 method.invoke(ScriptObject.SET_PROTO); 1369 1370 if (!hasGettersSetters) { 1371 method.store(objectNode.getSymbol()); 1372 return null; 1373 } 1374 1375 for (final Node element : elements) { 1376 final PropertyNode propertyNode = (PropertyNode)element; 1377 final Object key = propertyNode.getKey(); 1378 final ReferenceNode getter = (ReferenceNode)propertyNode.getGetter(); 1379 final ReferenceNode setter = (ReferenceNode)propertyNode.getSetter(); 1380 1381 if (getter == null && setter == null) { 1382 continue; 1383 } 1384 1385 method.dup().loadKey(key); 1386 1387 if (getter == null) { 1388 method.loadNull(); 1389 } else { 1390 getter.accept(this); 1391 } 1392 1393 if (setter == null) { 1394 method.loadNull(); 1395 } else { 1396 setter.accept(this); 1397 } 1398 1399 method.invoke(ScriptObject.SET_USER_ACCESSORS); 1400 } 1401 1402 method.store(objectNode.getSymbol()); 1403 1404 return null; 1405 } 1406 1407 @Override 1408 public Node enter(final ReferenceNode referenceNode) { 1409 if (referenceNode.testResolved()) { 1410 return null; 1411 } 1412 1413 newFunctionObject(referenceNode.getReference()); 1414 1415 return null; 1416 } 1417 1418 @Override 1419 public Node enter(final ReturnNode returnNode) { 1420 if (returnNode.testResolved()) { 1421 return null; 1422 } 1423 1424 // Set the split return flag in the scope if this is a split method fragment. 1425 if (method.getSplitNode() != null) { 1426 assert method.getSplitNode().hasReturn() : "unexpected return in split node"; 1427 1428 method.loadScope(); 1429 method.checkcast(Scope.class); 1430 method.load(0); 1431 method.invoke(Scope.SET_SPLIT_STATE); 1432 } 1433 1434 final Node expression = returnNode.getExpression(); 1435 if (expression != null) { 1436 load(expression); 1437 } else { 1438 method.loadUndefined(getCurrentFunctionNode().getReturnType()); 1439 } 1440 1441 method._return(getCurrentFunctionNode().getReturnType()); 1442 1443 return null; 1444 } 1445 1446 private static boolean isNullLiteral(final Node node) { 1447 return node instanceof LiteralNode<?> && ((LiteralNode<?>) node).isNull(); 1448 } 1449 1450 private boolean nullCheck(final RuntimeNode runtimeNode, final List<Node> args, final String signature) { 1451 final Request request = runtimeNode.getRequest(); 1452 1453 if (!Request.isEQ(request) && !Request.isNE(request)) { 1454 return false; 1455 } 1456 1457 assert args.size() == 2 : "EQ or NE or TYPEOF need two args"; 1458 1459 Node lhs = args.get(0); 1460 Node rhs = args.get(1); 1461 1462 if (isNullLiteral(lhs)) { 1463 final Node tmp = lhs; 1464 lhs = rhs; 1465 rhs = tmp; 1466 } 1467 1468 if (isNullLiteral(rhs)) { 1469 final Label trueLabel = new Label("trueLabel"); 1470 final Label falseLabel = new Label("falseLabel"); 1471 final Label endLabel = new Label("end"); 1472 1473 load(lhs); 1474 method.dup(); 1475 if (Request.isEQ(request)) { 1476 method.ifnull(trueLabel); 1477 } else if (Request.isNE(request)) { 1478 method.ifnonnull(trueLabel); 1479 } else { 1480 assert false : "Invalid request " + request; 1481 } 1482 1483 method.label(falseLabel); 1484 load(rhs); 1485 method.invokeStatic(CompilerConstants.className(ScriptRuntime.class), request.toString(), signature); 1486 method._goto(endLabel); 1487 1488 method.label(trueLabel); 1489 // if NE (not strict) this can be "undefined != null" which is supposed to be false 1490 if (request == Request.NE) { 1491 method.loadUndefined(Type.OBJECT); 1492 final Label isUndefined = new Label("isUndefined"); 1493 final Label afterUndefinedCheck = new Label("afterUndefinedCheck"); 1494 method.if_acmpeq(isUndefined); 1495 // not undefined 1496 method.load(true); 1497 method._goto(afterUndefinedCheck); 1498 method.label(isUndefined); 1499 method.load(false); 1500 method.label(afterUndefinedCheck); 1501 } else { 1502 method.pop(); 1503 method.load(true); 1504 } 1505 method.label(endLabel); 1506 method.convert(runtimeNode.getType()); 1507 method.store(runtimeNode.getSymbol()); 1508 1509 return true; 1510 } 1511 1512 return false; 1513 } 1514 1515 private boolean specializationCheck(final RuntimeNode.Request request, final Node node, final List<Node> args) { 1516 if (!request.canSpecialize()) { 1517 return false; 1518 } 1519 1520 assert args.size() == 2; 1521 final Node lhs = args.get(0); 1522 final Node rhs = args.get(1); 1523 1524 final Type returnType = node.getType(); 1525 load(lhs); 1526 load(rhs); 1527 1528 Request finalRequest = request; 1529 1530 final Request reverse = Request.reverse(request); 1531 if (method.peekType().isObject() && reverse != null) { 1532 if (!method.peekType(1).isObject()) { 1533 method.swap(); 1534 finalRequest = reverse; 1535 } 1536 } 1537 1538 method.dynamicRuntimeCall( 1539 new SpecializedRuntimeNode( 1540 finalRequest, 1541 new Type[] { 1542 method.peekType(1), 1543 method.peekType() 1544 }, 1545 returnType).getInitialName(), 1546 returnType, 1547 finalRequest); 1548 1549 method.convert(node.getType()); 1550 method.store(node.getSymbol()); 1551 1552 return true; 1553 } 1554 1555 @Override 1556 public Node enter(final RuntimeNode runtimeNode) { 1557 if (runtimeNode.testResolved()) { 1558 return null; 1559 } 1560 1561 /* 1562 * First check if this should be something other than a runtime node 1563 * AccessSpecializer might have changed the type 1564 */ 1565 if (runtimeNode.isPrimitive()) { 1566 1567 final Node lhs = runtimeNode.getArgs().get(0); 1568 Node rhs = null; 1569 1570 if (runtimeNode.getArgs().size() > 1) { 1571 rhs = runtimeNode.getArgs().get(1); 1572 } 1573 1574 final Type type = runtimeNode.getType(); 1575 final Symbol symbol = runtimeNode.getSymbol(); 1576 1577 switch (runtimeNode.getRequest()) { 1578 case EQ: 1579 case EQ_STRICT: 1580 return enterCmp(lhs, rhs, Condition.EQ, type, symbol); 1581 case NE: 1582 case NE_STRICT: 1583 return enterCmp(lhs, rhs, Condition.NE, type, symbol); 1584 case LE: 1585 return enterCmp(lhs, rhs, Condition.LE, type, symbol); 1586 case LT: 1587 return enterCmp(lhs, rhs, Condition.LT, type, symbol); 1588 case GE: 1589 return enterCmp(lhs, rhs, Condition.GE, type, symbol); 1590 case GT: 1591 return enterCmp(lhs, rhs, Condition.GT, type, symbol); 1592 case ADD: 1593 return enterNumericAdd(lhs, rhs, type, symbol); 1594 default: 1595 // it's ok to send this one on with only primitive arguments, maybe INSTANCEOF(true, true) or similar 1596 // assert false : runtimeNode + " has all primitive arguments. This is an inconsistent state"; 1597 break; 1598 } 1599 } 1600 1601 // Get the request arguments. 1602 final List<Node> args = runtimeNode.getArgs(); 1603 1604 if (nullCheck(runtimeNode, args, new FunctionSignature(false, runtimeNode.getType(), args).toString())) { 1605 return null; 1606 } 1607 1608 if (specializationCheck(runtimeNode.getRequest(), runtimeNode, args)) { 1609 return null; 1610 } 1611 1612 for (final Node arg : runtimeNode.getArgs()) { 1613 load(arg).convert(Type.OBJECT); //TODO this should not be necessary below Lower 1614 } 1615 1616 method.invokeStatic( 1617 CompilerConstants.className(ScriptRuntime.class), 1618 runtimeNode.getRequest().toString(), 1619 new FunctionSignature( 1620 false, 1621 runtimeNode.getType(), 1622 runtimeNode.getArgs().size()).toString()); 1623 method.convert(runtimeNode.getType()); 1624 method.store(runtimeNode.getSymbol()); 1625 1626 return null; 1627 } 1628 1629 @Override 1630 public Node enter(final SplitNode splitNode) { 1631 if (splitNode.testResolved()) { 1632 return null; 1633 } 1634 1635 final CompileUnit splitCompileUnit = splitNode.getCompileUnit(); 1636 1637 final FunctionNode fn = getCurrentFunctionNode(); 1638 final String className = splitCompileUnit.getUnitClassName(); 1639 final String name = splitNode.getName(); 1640 1641 final Class<?> rtype = fn.getReturnType().getTypeClass(); 1642 final Class<?>[] ptypes = fn.isVarArg() ? 1643 new Class<?>[] {Object.class, ScriptFunction.class, ScriptObject.class, Object.class} : 1644 new Class<?>[] {Object.class, ScriptFunction.class, ScriptObject.class}; 1645 1646 setCurrentCompileUnit(splitCompileUnit); 1647 splitNode.setCompileUnit(splitCompileUnit); 1648 1649 final Call splitCall = staticCallNoLookup( 1650 className, 1651 name, 1652 methodDescriptor(rtype, ptypes)); 1653 1654 setCurrentMethodEmitter( 1655 splitCompileUnit.getClassEmitter().method( 1656 EnumSet.of(Flag.PUBLIC, Flag.STATIC), 1657 name, 1658 rtype, 1659 ptypes)); 1660 1661 method.setFunctionNode(fn); 1662 method.setSplitNode(splitNode); 1663 splitNode.setMethodEmitter(method); 1664 1665 final MethodEmitter caller = splitNode.getCaller(); 1666 caller.loadThis(); 1667 caller.loadCallee(); 1668 caller.loadScope(); 1669 if (fn.isVarArg()) { 1670 caller.loadArguments(); 1671 } 1672 caller.invoke(splitCall); 1673 caller.storeResult(); 1674 1675 method.begin(); 1676 1677 method.loadUndefined(fn.getReturnType()); 1678 method.storeResult(); 1679 1680 fixScopeSlot(); 1681 1682 return splitNode; 1683 } 1684 1685 private void fixScopeSlot() { 1686 if (getCurrentFunctionNode().getScopeNode().getSymbol().getSlot() != SCOPE.slot()) { 1687 // TODO hack to move the scope to the expected slot (that's needed because split methods reuse the same slots as the root method) 1688 method.load(Type.typeFor(ScriptObject.class), SCOPE.slot()); 1689 method.storeScope(); 1690 } 1691 } 1692 1693 @Override 1694 public Node leave(final SplitNode splitNode) { 1695 try { 1696 // Wrap up this method. 1697 method.loadResult(); 1698 method._return(getCurrentFunctionNode().getReturnType()); 1699 method.end(); 1700 } catch (final Throwable t) { 1701 Context.printStackTrace(t); 1702 final VerifyError e = new VerifyError("Code generation bug in \"" + splitNode.getName() + "\": likely stack misaligned: " + t + " " + compiler.getSource().getName()); 1703 e.initCause(t); 1704 throw e; 1705 } 1706 1707 // Handle return from split method if there was one. 1708 final MethodEmitter caller = splitNode.getCaller(); 1709 final List<Label> targets = splitNode.getExternalTargets(); 1710 final int targetCount = targets.size(); 1711 1712 if (splitNode.hasReturn() || targetCount > 0) { 1713 1714 caller.loadScope(); 1715 caller.checkcast(Scope.class); 1716 caller.invoke(Scope.GET_SPLIT_STATE); 1717 1718 // Split state is -1 for no split state, 0 for return, 1..n+1 for break/continue 1719 final Label breakLabel = new Label("no_split_state"); 1720 final int low = splitNode.hasReturn() ? 0 : 1; 1721 final int labelCount = targetCount + 1 - low; 1722 final Label[] labels = new Label[labelCount]; 1723 1724 for (int i = 0; i < labelCount; i++) { 1725 labels[i] = new Label("split_state_" + i); 1726 } 1727 1728 caller.tableSwitch(low, targetCount, breakLabel, labels); 1729 for (int i = low; i <= targetCount; i++) { 1730 caller.label(labels[i - low]); 1731 if (i == 0) { 1732 caller.loadResult(); 1733 caller._return(getCurrentFunctionNode().getReturnType()); 1734 } else { 1735 // Clear split state. 1736 caller.loadScope(); 1737 caller.checkcast(Scope.class); 1738 caller.load(-1); 1739 caller.invoke(Scope.SET_SPLIT_STATE); 1740 caller.splitAwareGoto(targets.get(i - 1)); 1741 } 1742 } 1743 1744 caller.label(breakLabel); 1745 } 1746 1747 return splitNode; 1748 } 1749 1750 @Override 1751 public Node enter(final SwitchNode switchNode) { 1752 if (switchNode.testResolved()) { 1753 return null; 1754 } 1755 1756 final Node expression = switchNode.getExpression(); 1757 final Symbol tag = switchNode.getTag(); 1758 final boolean allInteger = tag.getSymbolType().isInteger(); 1759 final List<CaseNode> cases = switchNode.getCases(); 1760 final CaseNode defaultCase = switchNode.getDefaultCase(); 1761 final Label breakLabel = switchNode.getBreakLabel(); 1762 1763 Label defaultLabel = breakLabel; 1764 boolean hasDefault = false; 1765 1766 if (defaultCase != null) { 1767 defaultLabel = defaultCase.getEntry(); 1768 hasDefault = true; 1769 } 1770 1771 if (cases.isEmpty()) { 1772 method.label(breakLabel); 1773 return null; 1774 } 1775 1776 if (allInteger) { 1777 // Tree for sorting values. 1778 final TreeMap<Integer, Label> tree = new TreeMap<>(); 1779 1780 // Build up sorted tree. 1781 for (final CaseNode caseNode : cases) { 1782 final Node test = caseNode.getTest(); 1783 1784 if (test != null) { 1785 final Integer value = (Integer)((LiteralNode<?>) test).getValue(); 1786 final Label entry = caseNode.getEntry(); 1787 1788 // Take first duplicate. 1789 if (!(tree.containsKey(value))) { 1790 tree.put(value, entry); 1791 } 1792 } 1793 } 1794 1795 // Copy values and labels to arrays. 1796 final int size = tree.size(); 1797 final Integer[] values = tree.keySet().toArray(new Integer[size]); 1798 final Label[] labels = tree.values().toArray(new Label[size]); 1799 1800 // Discern low, high and range. 1801 final int lo = values[0]; 1802 final int hi = values[size - 1]; 1803 final int range = hi - lo + 1; 1804 1805 // Find an unused value for default. 1806 int deflt = Integer.MIN_VALUE; 1807 for (final int value : values) { 1808 if (deflt == value) { 1809 deflt++; 1810 } else if (deflt < value) { 1811 break; 1812 } 1813 } 1814 1815 // Load switch expression. 1816 load(expression); 1817 final Type type = expression.getType(); 1818 1819 // If expression not int see if we can convert, if not use deflt to trigger default. 1820 if (!type.isInteger()) { 1821 method.load(deflt); 1822 method.invoke(staticCallNoLookup(ScriptRuntime.class, "switchTagAsInt", int.class, type.getTypeClass(), int.class)); 1823 } 1824 1825 // If reasonable size and not too sparse (80%), use table otherwise use lookup. 1826 if (range > 0 && range < 4096 && range < (size * 5 / 4)) { 1827 final Label[] table = new Label[range]; 1828 Arrays.fill(table, defaultLabel); 1829 1830 for (int i = 0; i < size; i++) { 1831 final int value = values[i]; 1832 table[value - lo] = labels[i]; 1833 } 1834 1835 method.tableSwitch(lo, hi, defaultLabel, table); 1836 } else { 1837 final int[] ints = new int[size]; 1838 1839 for (int i = 0; i < size; i++) { 1840 ints[i] = values[i]; 1841 } 1842 1843 method.lookupSwitch(defaultLabel, ints, labels); 1844 } 1845 } else { 1846 load(expression); 1847 1848 if (expression.getType().isInteger()) { 1849 method.convert(Type.NUMBER).dup(); 1850 method.store(tag); 1851 method.conditionalJump(MethodEmitter.Condition.NE, true, defaultLabel); 1852 } else { 1853 method.store(tag); 1854 } 1855 1856 for (final CaseNode caseNode : cases) { 1857 final Node test = caseNode.getTest(); 1858 1859 if (test != null) { 1860 method.load(tag); 1861 load(test); 1862 method.invoke(ScriptRuntime.EQ_STRICT); 1863 method.ifne(caseNode.getEntry()); 1864 } 1865 } 1866 1867 method._goto(hasDefault ? defaultLabel : breakLabel); 1868 } 1869 1870 for (final CaseNode caseNode : cases) { 1871 method.label(caseNode.getEntry()); 1872 caseNode.getBody().accept(this); 1873 } 1874 1875 if (!switchNode.isTerminal()) { 1876 method.label(breakLabel); 1877 } 1878 1879 return null; 1880 } 1881 1882 @Override 1883 public Node enter(final ThrowNode throwNode) { 1884 if (throwNode.testResolved()) { 1885 return null; 1886 } 1887 1888 method._new(ECMAException.class).dup(); 1889 1890 final Node expression = throwNode.getExpression(); 1891 final Source source = compiler.getSource(); 1892 final int position = throwNode.position(); 1893 final int line = source.getLine(position); 1894 final int column = source.getColumn(position); 1895 1896 load(expression); 1897 assert expression.getType().isObject(); 1898 1899 method.load(source.getName()); 1900 method.load(line); 1901 method.load(column); 1902 method.invoke(ECMAException.THROW_INIT); 1903 1904 method.athrow(); 1905 1906 return null; 1907 } 1908 1909 @Override 1910 public Node enter(final TryNode tryNode) { 1911 if (tryNode.testResolved()) { 1912 return null; 1913 } 1914 1915 final Block body = tryNode.getBody(); 1916 final List<Block> catchBlocks = tryNode.getCatchBlocks(); 1917 final Symbol symbol = tryNode.getException(); 1918 final Label entry = new Label("try"); 1919 final Label recovery = new Label("catch"); 1920 final Label exit = tryNode.getExit(); 1921 final Label skip = new Label("skip"); 1922 1923 method.label(entry); 1924 1925 body.accept(this); 1926 1927 if (!body.hasTerminalFlags()) { 1928 method._goto(skip); 1929 } 1930 1931 method.label(exit); 1932 1933 method._catch(recovery); 1934 method.store(symbol); 1935 1936 for (int i = 0; i < catchBlocks.size(); i++) { 1937 final Block saveBlock = getCurrentBlock(); 1938 final Block catchBlock = catchBlocks.get(i); 1939 1940 setCurrentBlock(catchBlock); 1941 1942 try { 1943 enter(catchBlock); 1944 1945 final CatchNode catchNode = (CatchNode)catchBlocks.get(i).getStatements().get(0); 1946 final IdentNode exception = catchNode.getException(); 1947 final Node exceptionCondition = catchNode.getExceptionCondition(); 1948 final Block catchBody = catchNode.getBody(); 1949 1950 if (catchNode.isSyntheticRethrow()) { 1951 // Generate catch body (inlined finally) and rethrow exception 1952 catchBody.accept(this); 1953 method.load(symbol).athrow(); 1954 continue; 1955 } 1956 1957 new Store<IdentNode>(exception) { 1958 @Override 1959 protected void evaluate() { 1960 /* 1961 * If caught object is an instance of ECMAException, then 1962 * bind obj.thrown to the script catch var. Or else bind the 1963 * caught object itself to the script catch var. 1964 */ 1965 final Label notEcmaException = new Label("no_ecma_exception"); 1966 1967 method.load(symbol).dup()._instanceof(ECMAException.class).ifeq(notEcmaException); 1968 method.checkcast(ECMAException.class); //TODO is this necessary? 1969 method.getField(ECMAException.THROWN); 1970 method.label(notEcmaException); 1971 } 1972 }.store(); 1973 1974 final Label next; 1975 1976 if (exceptionCondition != null) { 1977 next = new Label("next"); 1978 load(exceptionCondition).convert(Type.BOOLEAN).ifeq(next); 1979 } else { 1980 next = null; 1981 } 1982 1983 catchBody.accept(this); 1984 1985 if (i + 1 != catchBlocks.size() && !catchBody.hasTerminalFlags()) { 1986 method._goto(skip); 1987 } 1988 1989 if (next != null) { 1990 if (i + 1 == catchBlocks.size()) { 1991 // no next catch block - rethrow if condition failed 1992 method._goto(skip); 1993 method.label(next); 1994 method.load(symbol).athrow(); 1995 } else { 1996 method.label(next); 1997 } 1998 } 1999 2000 leave(catchBlock); 2001 } finally { 2002 setCurrentBlock(saveBlock); 2003 } 2004 } 2005 2006 method.label(skip); 2007 method._try(entry, exit, recovery, Throwable.class); 2008 2009 // Finally body is always inlined elsewhere so it doesn't need to be emitted 2010 2011 return null; 2012 } 2013 2014 @Override 2015 public Node enter(final VarNode varNode) { 2016 final Node init = varNode.getInit(); 2017 2018 if (varNode.testResolved() || init == null) { 2019 return null; 2020 } 2021 2022 final Symbol varSymbol = varNode.getSymbol(); 2023 2024 assert varSymbol != null : "variable node " + varNode + " requires a symbol"; 2025 2026 assert method != null; 2027 2028 final boolean needsScope = varSymbol.isScope(); 2029 if (needsScope) { 2030 method.loadScope(); 2031 } 2032 2033 load(init); 2034 2035 if (needsScope) { 2036 int flags = CALLSITE_SCOPE | getCallSiteFlags(); 2037 if (varNode.isFunctionVarNode()) { 2038 flags |= CALLSITE_FUNCTION_DECLARATION; 2039 } 2040 final IdentNode identNode = varNode.getName(); 2041 final Type type = identNode.getType(); 2042 if(varSymbol.isFastScope(getCurrentFunctionNode())) { 2043 storeFastScopeVar(type, varSymbol, flags); 2044 } else { 2045 method.dynamicSet(type, identNode.getName(), flags); 2046 } 2047 } else { 2048 assert varNode.getType() == varNode.getName().getType() : "varNode type=" + varNode.getType() + " nametype=" + varNode.getName().getType() + " inittype=" + init.getType(); 2049 2050 method.convert(varNode.getType()); // aw: convert moved here 2051 method.store(varSymbol); 2052 } 2053 2054 return null; 2055 } 2056 2057 @Override 2058 public Node enter(final WhileNode whileNode) { 2059 if (whileNode.testResolved()) { 2060 return null; 2061 } 2062 2063 final Node test = whileNode.getTest(); 2064 final Block body = whileNode.getBody(); 2065 final Label breakLabel = whileNode.getBreakLabel(); 2066 final Label continueLabel = whileNode.getContinueLabel(); 2067 final Label loopLabel = new Label("loop"); 2068 2069 if (!(whileNode instanceof DoWhileNode)) { 2070 method._goto(continueLabel); 2071 } 2072 2073 method.label(loopLabel); 2074 body.accept(this); 2075 if (!whileNode.isTerminal()) { 2076 method.label(continueLabel); 2077 new BranchOptimizer(this, method).execute(test, loopLabel, true); 2078 method.label(breakLabel); 2079 } 2080 2081 return null; 2082 } 2083 2084 private void closeWith() { 2085 method.loadScope(); 2086 method.invoke(ScriptRuntime.CLOSE_WITH); 2087 method.storeScope(); 2088 } 2089 2090 @Override 2091 public Node enter(final WithNode withNode) { 2092 if (withNode.testResolved()) { 2093 return null; 2094 } 2095 2096 final Node expression = withNode.getExpression(); 2097 final Node body = withNode.getBody(); 2098 2099 final Label tryLabel = new Label("with_try"); 2100 final Label endLabel = new Label("with_end"); 2101 final Label catchLabel = new Label("with_catch"); 2102 final Label exitLabel = new Label("with_exit"); 2103 2104 method.label(tryLabel); 2105 2106 method.loadScope(); 2107 load(expression); 2108 2109 assert expression.getType().isObject() : "with expression needs to be object: " + expression; 2110 2111 method.invoke(ScriptRuntime.OPEN_WITH); 2112 method.storeScope(); 2113 2114 body.accept(this); 2115 2116 if (!body.isTerminal()) { 2117 closeWith(); 2118 method._goto(exitLabel); 2119 } 2120 2121 method.label(endLabel); 2122 2123 method._catch(catchLabel); 2124 closeWith(); 2125 method.athrow(); 2126 2127 method.label(exitLabel); 2128 2129 method._try(tryLabel, endLabel, catchLabel); 2130 2131 return null; 2132 } 2133 2134 @Override 2135 public Node enterADD(final UnaryNode unaryNode) { 2136 if (unaryNode.testResolved()) { 2137 return null; 2138 } 2139 2140 load(unaryNode.rhs()); 2141 assert unaryNode.rhs().getType().isNumber(); 2142 method.store(unaryNode.getSymbol()); 2143 2144 return null; 2145 } 2146 2147 @Override 2148 public Node enterBIT_NOT(final UnaryNode unaryNode) { 2149 if (unaryNode.testResolved()) { 2150 return null; 2151 } 2152 2153 load(unaryNode.rhs()).convert(Type.INT).load(-1).xor().store(unaryNode.getSymbol()); 2154 2155 return null; 2156 } 2157 2158 // do this better with convert calls to method. TODO 2159 @Override 2160 public Node enterCONVERT(final UnaryNode unaryNode) { 2161 if (unaryNode.testResolved()) { 2162 return null; 2163 } 2164 2165 final Node rhs = unaryNode.rhs(); 2166 final Type to = unaryNode.getType(); 2167 2168 if (to.isObject() && rhs instanceof LiteralNode) { 2169 final LiteralNode<?> literalNode = (LiteralNode<?>)rhs; 2170 final Object value = literalNode.getValue(); 2171 2172 if (value instanceof Number) { 2173 assert !to.isArray() : "type hygiene - cannot convert number to array: (" + to.getTypeClass().getSimpleName() + ')' + value; 2174 if (value instanceof Integer) { 2175 method.load((Integer)value); 2176 } else if (value instanceof Long) { 2177 method.load((Long)value); 2178 } else if (value instanceof Double) { 2179 method.load((Double)value); 2180 } else { 2181 assert false; 2182 } 2183 method.convert(Type.OBJECT); 2184 } else if (value instanceof Boolean) { 2185 method.getField(staticField(Boolean.class, value.toString().toUpperCase(), Boolean.class)); 2186 } else { 2187 load(rhs); 2188 method.convert(unaryNode.getType()); 2189 } 2190 } else { 2191 load(rhs); 2192 method.convert(unaryNode.getType()); 2193 } 2194 2195 method.store(unaryNode.getSymbol()); 2196 2197 return null; 2198 } 2199 2200 @Override 2201 public Node enterDECINC(final UnaryNode unaryNode) { 2202 if (unaryNode.testResolved()) { 2203 return null; 2204 } 2205 2206 final Node rhs = unaryNode.rhs(); 2207 final Type type = unaryNode.getType(); 2208 final TokenType tokenType = unaryNode.tokenType(); 2209 final boolean isPostfix = tokenType == TokenType.DECPOSTFIX || tokenType == TokenType.INCPOSTFIX; 2210 final boolean isIncrement = tokenType == TokenType.INCPREFIX || tokenType == TokenType.INCPOSTFIX; 2211 2212 assert !type.isObject(); 2213 2214 new SelfModifyingStore<UnaryNode>(unaryNode, rhs) { 2215 2216 @Override 2217 protected void evaluate() { 2218 load(rhs, true); 2219 2220 method.convert(type); 2221 if (!isPostfix) { 2222 if (type.isInteger()) { 2223 method.load(isIncrement ? 1 : -1); 2224 } else if (type.isLong()) { 2225 method.load(isIncrement ? 1L : -1L); 2226 } else { 2227 method.load(isIncrement ? 1.0 : -1.0); 2228 } 2229 method.add(); 2230 } 2231 } 2232 2233 @Override 2234 protected void storeNonDiscard() { 2235 super.storeNonDiscard(); 2236 if (isPostfix) { 2237 if (type.isInteger()) { 2238 method.load(isIncrement ? 1 : -1); 2239 } else if (type.isLong()) { 2240 method.load(isIncrement ? 1L : 1L); 2241 } else { 2242 method.load(isIncrement ? 1.0 : -1.0); 2243 } 2244 method.add(); 2245 } 2246 } 2247 }.store(); 2248 2249 return null; 2250 } 2251 2252 @Override 2253 public Node enterDISCARD(final UnaryNode unaryNode) { 2254 if (unaryNode.testResolved()) { 2255 return null; 2256 } 2257 2258 final Node rhs = unaryNode.rhs(); 2259 2260 load(rhs); 2261 2262 if (rhs.shouldDiscard()) { 2263 method.pop(); 2264 } 2265 2266 return null; 2267 } 2268 2269 @Override 2270 public Node enterNEW(final UnaryNode unaryNode) { 2271 if (unaryNode.testResolved()) { 2272 return null; 2273 } 2274 2275 final CallNode callNode = (CallNode)unaryNode.rhs(); 2276 final List<Node> args = callNode.getArgs(); 2277 2278 // Load function reference. 2279 load(callNode.getFunction()).convert(Type.OBJECT); // must detect type error 2280 2281 // Load arguments. 2282 loadArgs(args); 2283 2284 method.dynamicNew(args.size(), getCallSiteFlags()); 2285 method.store(unaryNode.getSymbol()); 2286 2287 return null; 2288 } 2289 2290 @Override 2291 public Node enterNOT(final UnaryNode unaryNode) { 2292 if (unaryNode.testResolved()) { 2293 return null; 2294 } 2295 2296 final Node rhs = unaryNode.rhs(); 2297 2298 load(rhs); 2299 2300 final Label trueLabel = new Label("true"); 2301 final Label afterLabel = new Label("after"); 2302 2303 method.convert(Type.BOOLEAN); 2304 method.ifne(trueLabel); 2305 method.load(true); 2306 method._goto(afterLabel); 2307 method.label(trueLabel); 2308 method.load(false); 2309 method.label(afterLabel); 2310 method.store(unaryNode.getSymbol()); 2311 2312 return null; 2313 } 2314 2315 @Override 2316 public Node enterSUB(final UnaryNode unaryNode) { 2317 if (unaryNode.testResolved()) { 2318 return null; 2319 } 2320 2321 load(unaryNode.rhs()).neg().store(unaryNode.getSymbol()); 2322 2323 return null; 2324 } 2325 2326 private Node enterNumericAdd(final Node lhs, final Node rhs, final Type type, final Symbol symbol) { 2327 assert lhs.getType().equals(rhs.getType()) && lhs.getType().equals(type); 2328 load(lhs); 2329 load(rhs); 2330 method.add(); 2331 method.store(symbol); 2332 2333 return null; 2334 } 2335 2336 @Override 2337 public Node enterADD(final BinaryNode binaryNode) { 2338 if (binaryNode.testResolved()) { 2339 return null; 2340 } 2341 2342 final Node lhs = binaryNode.lhs(); 2343 final Node rhs = binaryNode.rhs(); 2344 2345 final Type type = binaryNode.getType(); 2346 if (type.isNumeric()) { 2347 enterNumericAdd(lhs, rhs, type, binaryNode.getSymbol()); 2348 } else { 2349 load(lhs).convert(Type.OBJECT); 2350 load(rhs).convert(Type.OBJECT); 2351 method.add(); 2352 method.store(binaryNode.getSymbol()); 2353 } 2354 2355 return null; 2356 } 2357 2358 private Node enterAND_OR(final BinaryNode binaryNode) { 2359 if (binaryNode.testResolved()) { 2360 return null; 2361 } 2362 2363 final Node lhs = binaryNode.lhs(); 2364 final Node rhs = binaryNode.rhs(); 2365 2366 final Label skip = new Label("skip"); 2367 2368 load(lhs).convert(Type.OBJECT).dup().convert(Type.BOOLEAN); 2369 2370 if (binaryNode.tokenType() == TokenType.AND) { 2371 method.ifeq(skip); 2372 } else { 2373 method.ifne(skip); 2374 } 2375 2376 method.pop(); 2377 load(rhs).convert(Type.OBJECT); 2378 method.label(skip); 2379 method.store(binaryNode.getSymbol()); 2380 2381 return null; 2382 } 2383 2384 @Override 2385 public Node enterAND(final BinaryNode binaryNode) { 2386 return enterAND_OR(binaryNode); 2387 } 2388 2389 @Override 2390 public Node enterASSIGN(final BinaryNode binaryNode) { 2391 if (binaryNode.testResolved()) { 2392 return null; 2393 } 2394 2395 final Node lhs = binaryNode.lhs(); 2396 final Node rhs = binaryNode.rhs(); 2397 2398 final Type lhsType = lhs.getType(); 2399 final Type rhsType = rhs.getType(); 2400 2401 if (!lhsType.isEquivalentTo(rhsType)) { 2402 //this is OK if scoped, only locals are wrong 2403 assert !(lhs instanceof IdentNode) || lhs.getSymbol().isScope() : new ASTWriter(binaryNode); 2404 } 2405 2406 new Store<BinaryNode>(binaryNode, lhs) { 2407 @Override 2408 protected void evaluate() { 2409 load(rhs); 2410 } 2411 }.store(); 2412 2413 return null; 2414 } 2415 2416 /** 2417 * Helper class for assignment ops, e.g. *=, += and so on.. 2418 */ 2419 private abstract class AssignOp extends SelfModifyingStore<BinaryNode> { 2420 2421 /** The type of the resulting operation */ 2422 private final Type opType; 2423 2424 /** 2425 * Constructor 2426 * 2427 * @param node the assign op node 2428 */ 2429 AssignOp(final BinaryNode node) { 2430 this(node.getType(), node); 2431 } 2432 2433 /** 2434 * Constructor 2435 * 2436 * @param opType type of the computation - overriding the type of the node 2437 * @param node the assign op node 2438 */ 2439 AssignOp(final Type opType, final BinaryNode node) { 2440 super(node, node.lhs()); 2441 this.opType = opType; 2442 } 2443 2444 @Override 2445 public void store() { 2446 if (assignNode.testResolved()) { 2447 return; 2448 } 2449 super.store(); 2450 } 2451 2452 protected abstract void op(); 2453 2454 @Override 2455 protected void evaluate() { 2456 load(assignNode.lhs(), true).convert(opType); 2457 load(assignNode.rhs()).convert(opType); 2458 op(); 2459 method.convert(assignNode.getType()); 2460 } 2461 } 2462 2463 @Override 2464 public Node enterASSIGN_ADD(final BinaryNode binaryNode) { 2465 assert RuntimeNode.Request.ADD.canSpecialize(); 2466 final boolean specialize = binaryNode.getType() == Type.OBJECT; 2467 2468 new AssignOp(binaryNode) { 2469 @Override 2470 protected boolean isSelfModifying() { 2471 return !specialize; 2472 } 2473 2474 @Override 2475 protected void op() { 2476 method.add(); 2477 } 2478 2479 @Override 2480 protected void evaluate() { 2481 if (specialize && specializationCheck(Request.ADD, assignNode, Arrays.asList(assignNode.lhs(), assignNode.rhs()))) { 2482 return; 2483 } 2484 super.evaluate(); 2485 } 2486 }.store(); 2487 2488 return null; 2489 } 2490 2491 @Override 2492 public Node enterASSIGN_BIT_AND(final BinaryNode binaryNode) { 2493 new AssignOp(Type.INT, binaryNode) { 2494 @Override 2495 protected void op() { 2496 method.and(); 2497 } 2498 }.store(); 2499 2500 return null; 2501 } 2502 2503 @Override 2504 public Node enterASSIGN_BIT_OR(final BinaryNode binaryNode) { 2505 new AssignOp(Type.INT, binaryNode) { 2506 @Override 2507 protected void op() { 2508 method.or(); 2509 } 2510 }.store(); 2511 2512 return null; 2513 } 2514 2515 @Override 2516 public Node enterASSIGN_BIT_XOR(final BinaryNode binaryNode) { 2517 new AssignOp(Type.INT, binaryNode) { 2518 @Override 2519 protected void op() { 2520 method.xor(); 2521 } 2522 }.store(); 2523 2524 return null; 2525 } 2526 2527 @Override 2528 public Node enterASSIGN_DIV(final BinaryNode binaryNode) { 2529 new AssignOp(binaryNode) { 2530 @Override 2531 protected void op() { 2532 method.div(); 2533 } 2534 }.store(); 2535 2536 return null; 2537 } 2538 2539 @Override 2540 public Node enterASSIGN_MOD(final BinaryNode binaryNode) { 2541 new AssignOp(binaryNode) { 2542 @Override 2543 protected void op() { 2544 method.rem(); 2545 } 2546 }.store(); 2547 2548 return null; 2549 } 2550 2551 @Override 2552 public Node enterASSIGN_MUL(final BinaryNode binaryNode) { 2553 new AssignOp(binaryNode) { 2554 @Override 2555 protected void op() { 2556 method.mul(); 2557 } 2558 }.store(); 2559 2560 return null; 2561 } 2562 2563 @Override 2564 public Node enterASSIGN_SAR(final BinaryNode binaryNode) { 2565 new AssignOp(Type.INT, binaryNode) { 2566 @Override 2567 protected void op() { 2568 method.sar(); 2569 } 2570 }.store(); 2571 2572 return null; 2573 } 2574 2575 @Override 2576 public Node enterASSIGN_SHL(final BinaryNode binaryNode) { 2577 new AssignOp(Type.INT, binaryNode) { 2578 @Override 2579 protected void op() { 2580 method.shl(); 2581 } 2582 }.store(); 2583 2584 return null; 2585 } 2586 2587 @Override 2588 public Node enterASSIGN_SHR(final BinaryNode binaryNode) { 2589 new AssignOp(Type.INT, binaryNode) { 2590 @Override 2591 protected void op() { 2592 method.shr(); 2593 method.convert(Type.LONG).load(0xffff_ffffL).and(); 2594 } 2595 }.store(); 2596 2597 return null; 2598 } 2599 2600 @Override 2601 public Node enterASSIGN_SUB(final BinaryNode binaryNode) { 2602 new AssignOp(binaryNode) { 2603 @Override 2604 protected void op() { 2605 method.sub(); 2606 } 2607 }.store(); 2608 2609 return null; 2610 } 2611 2612 /** 2613 * Helper class for binary arithmetic ops 2614 */ 2615 private abstract class BinaryArith { 2616 2617 protected abstract void op(); 2618 2619 protected void evaluate(final BinaryNode node) { 2620 if (node.testResolved()) { 2621 return; 2622 } 2623 load(node.lhs()); 2624 load(node.rhs()); 2625 op(); 2626 method.store(node.getSymbol()); 2627 } 2628 } 2629 2630 @Override 2631 public Node enterBIT_AND(final BinaryNode binaryNode) { 2632 new BinaryArith() { 2633 @Override 2634 protected void op() { 2635 method.and(); 2636 } 2637 }.evaluate(binaryNode); 2638 2639 return null; 2640 } 2641 2642 @Override 2643 public Node enterBIT_OR(final BinaryNode binaryNode) { 2644 new BinaryArith() { 2645 @Override 2646 protected void op() { 2647 method.or(); 2648 } 2649 }.evaluate(binaryNode); 2650 2651 return null; 2652 } 2653 2654 @Override 2655 public Node enterBIT_XOR(final BinaryNode binaryNode) { 2656 new BinaryArith() { 2657 @Override 2658 protected void op() { 2659 method.xor(); 2660 } 2661 }.evaluate(binaryNode); 2662 2663 return null; 2664 } 2665 2666 private Node enterComma(final BinaryNode binaryNode) { 2667 if (binaryNode.testResolved()) { 2668 return null; 2669 } 2670 2671 final Node lhs = binaryNode.lhs(); 2672 final Node rhs = binaryNode.rhs(); 2673 2674 load(lhs); 2675 load(rhs); 2676 method.store(binaryNode.getSymbol()); 2677 2678 return null; 2679 } 2680 2681 @Override 2682 public Node enterCOMMARIGHT(final BinaryNode binaryNode) { 2683 return enterComma(binaryNode); 2684 } 2685 2686 @Override 2687 public Node enterCOMMALEFT(final BinaryNode binaryNode) { 2688 return enterComma(binaryNode); 2689 } 2690 2691 @Override 2692 public Node enterDIV(final BinaryNode binaryNode) { 2693 new BinaryArith() { 2694 @Override 2695 protected void op() { 2696 method.div(); 2697 } 2698 }.evaluate(binaryNode); 2699 2700 return null; 2701 } 2702 2703 private Node enterCmp(final Node lhs, final Node rhs, final Condition cond, final Type type, final Symbol symbol) { 2704 final Type lhsType = lhs.getType(); 2705 final Type rhsType = rhs.getType(); 2706 2707 final Type widest = Type.widest(lhsType, rhsType); 2708 assert widest.isNumeric() || widest.isBoolean() : widest; 2709 2710 load(lhs); 2711 method.convert(widest); 2712 load(rhs); 2713 method.convert(widest); 2714 2715 final Label trueLabel = new Label("trueLabel"); 2716 final Label afterLabel = new Label("skip"); 2717 2718 method.conditionalJump(cond, trueLabel); 2719 2720 method.load(Boolean.FALSE); 2721 method._goto(afterLabel); 2722 method.label(trueLabel); 2723 method.load(Boolean.TRUE); 2724 method.label(afterLabel); 2725 2726 method.convert(type); 2727 method.store(symbol); 2728 2729 return null; 2730 } 2731 2732 private Node enterCmp(final BinaryNode binaryNode, final Condition cond) { 2733 if (binaryNode.testResolved()) { 2734 return null; 2735 } 2736 return enterCmp(binaryNode.lhs(), binaryNode.rhs(), cond, binaryNode.getType(), binaryNode.getSymbol()); 2737 } 2738 2739 @Override 2740 public Node enterEQ(final BinaryNode binaryNode) { 2741 return enterCmp(binaryNode, Condition.EQ); 2742 } 2743 2744 @Override 2745 public Node enterEQ_STRICT(final BinaryNode binaryNode) { 2746 return enterCmp(binaryNode, Condition.EQ); 2747 } 2748 2749 @Override 2750 public Node enterGE(final BinaryNode binaryNode) { 2751 return enterCmp(binaryNode, Condition.GE); 2752 } 2753 2754 @Override 2755 public Node enterGT(final BinaryNode binaryNode) { 2756 return enterCmp(binaryNode, Condition.GT); 2757 } 2758 2759 @Override 2760 public Node enterLE(final BinaryNode binaryNode) { 2761 return enterCmp(binaryNode, Condition.LE); 2762 } 2763 2764 @Override 2765 public Node enterLT(final BinaryNode binaryNode) { 2766 return enterCmp(binaryNode, Condition.LT); 2767 } 2768 2769 @Override 2770 public Node enterMOD(final BinaryNode binaryNode) { 2771 new BinaryArith() { 2772 @Override 2773 protected void op() { 2774 method.rem(); 2775 } 2776 }.evaluate(binaryNode); 2777 2778 return null; 2779 } 2780 2781 @Override 2782 public Node enterMUL(final BinaryNode binaryNode) { 2783 new BinaryArith() { 2784 @Override 2785 protected void op() { 2786 method.mul(); 2787 } 2788 }.evaluate(binaryNode); 2789 2790 return null; 2791 } 2792 2793 @Override 2794 public Node enterNE(final BinaryNode binaryNode) { 2795 return enterCmp(binaryNode, Condition.NE); 2796 } 2797 2798 @Override 2799 public Node enterNE_STRICT(final BinaryNode binaryNode) { 2800 return enterCmp(binaryNode, Condition.NE); 2801 } 2802 2803 @Override 2804 public Node enterOR(final BinaryNode binaryNode) { 2805 return enterAND_OR(binaryNode); 2806 } 2807 2808 @Override 2809 public Node enterSAR(final BinaryNode binaryNode) { 2810 new BinaryArith() { 2811 @Override 2812 protected void op() { 2813 method.sar(); 2814 } 2815 }.evaluate(binaryNode); 2816 2817 return null; 2818 } 2819 2820 @Override 2821 public Node enterSHL(final BinaryNode binaryNode) { 2822 new BinaryArith() { 2823 @Override 2824 protected void op() { 2825 method.shl(); 2826 } 2827 }.evaluate(binaryNode); 2828 2829 return null; 2830 } 2831 2832 @Override 2833 public Node enterSHR(final BinaryNode binaryNode) { 2834 new BinaryArith() { 2835 @Override 2836 protected void op() { 2837 method.shr(); 2838 method.convert(Type.LONG).load(0xffff_ffffL).and(); 2839 } 2840 }.evaluate(binaryNode); 2841 2842 return null; 2843 } 2844 2845 @Override 2846 public Node enterSUB(final BinaryNode binaryNode) { 2847 new BinaryArith() { 2848 @Override 2849 protected void op() { 2850 method.sub(); 2851 } 2852 }.evaluate(binaryNode); 2853 2854 return null; 2855 } 2856 2857 /* 2858 * Ternary visits. 2859 */ 2860 @Override 2861 public Node enter(final TernaryNode ternaryNode) { 2862 if (ternaryNode.testResolved()) { 2863 return null; 2864 } 2865 2866 final Node lhs = ternaryNode.lhs(); 2867 final Node rhs = ternaryNode.rhs(); 2868 final Node third = ternaryNode.third(); 2869 2870 final Symbol symbol = ternaryNode.getSymbol(); 2871 final Label falseLabel = new Label("ternary_false"); 2872 final Label exitLabel = new Label("ternary_exit"); 2873 2874 final Type widest = Type.widest(rhs.getType(), third.getType()); 2875 2876 load(lhs); 2877 assert lhs.getType().isBoolean() : "lhs in ternary must be boolean"; 2878 2879 // we still keep the conversion here as the AccessSpecializer can have separated the types, e.g. var y = x ? x=55 : 17 2880 // will left as (Object)x=55 : (Object)17 by Lower. Then the first term can be {I}x=55 of type int, which breaks the 2881 // symmetry for the temporary slot for this TernaryNode. This is evidence that we assign types and explicit conversions 2882 // to early, or Apply the AccessSpecializer too late. We are mostly probably looking for a separate type pass to 2883 // do this property. Then we never need any conversions in CodeGenerator 2884 method.ifeq(falseLabel); 2885 load(rhs); 2886 method.convert(widest); 2887 method._goto(exitLabel); 2888 method.label(falseLabel); 2889 load(third); 2890 method.convert(widest); 2891 method.label(exitLabel); 2892 method.store(symbol); 2893 2894 return null; 2895 } 2896 2897 /** 2898 * Generate all shared scope calls generated during codegen. 2899 */ 2900 protected void generateScopeCalls() { 2901 for (final SharedScopeCall scopeAccess : scopeCalls.values()) { 2902 scopeAccess.generateScopeCall(); 2903 } 2904 } 2905 2906 /** 2907 * Get a shared static method representing a dynamic scope callsite. 2908 * 2909 * @param symbol the symbol 2910 * @param valueType the value type of the symbol 2911 * @param returnType the return type 2912 * @param paramTypes the parameter types 2913 * @param flags the callsite flags 2914 * @return an object representing a shared scope call 2915 */ 2916 private SharedScopeCall getScopeCall(final Symbol symbol, final Type valueType, final Type returnType, 2917 final Type[] paramTypes, final int flags) { 2918 2919 final SharedScopeCall scopeCall = new SharedScopeCall(symbol, valueType, returnType, paramTypes, flags); 2920 if (scopeCalls.containsKey(scopeCall)) { 2921 return scopeCalls.get(scopeCall); 2922 } 2923 scopeCall.setClassAndName(compileUnit, compiler); 2924 scopeCalls.put(scopeCall, scopeCall); 2925 return scopeCall; 2926 } 2927 2928 /** 2929 * Get a shared static method representing a dynamic scope get access. 2930 * 2931 * @param type the type of the variable 2932 * @param symbol the symbol 2933 * @param flags the callsite flags 2934 * @return an object representing a shared scope call 2935 */ 2936 private SharedScopeCall getScopeGet(final Type type, final Symbol symbol, final int flags) { 2937 2938 final SharedScopeCall scopeCall = new SharedScopeCall(symbol, type, type, null, flags); 2939 if (scopeCalls.containsKey(scopeCall)) { 2940 return scopeCalls.get(scopeCall); 2941 } 2942 scopeCall.setClassAndName(compileUnit, compiler); 2943 scopeCalls.put(scopeCall, scopeCall); 2944 return scopeCall; 2945 } 2946 2947 /** 2948 * Debug code used to print symbols 2949 * 2950 * @param block the block we are in 2951 * @param ident identifier for block or function where applicable 2952 */ 2953 private void printSymbols(final Block block, final String ident) { 2954 if (!context._print_symbols) { 2955 return; 2956 } 2957 2958 @SuppressWarnings("resource") 2959 final PrintWriter out = context.getErr(); 2960 out.println("[BLOCK in '" + ident + "']"); 2961 if (!block.printSymbols(out)) { 2962 out.println("<no symbols>"); 2963 } 2964 out.println(); 2965 } 2966 2967 2968 /** 2969 * The difference between a store and a self modifying store is that 2970 * the latter may load part of the target on the stack, e.g. the base 2971 * of an AccessNode or the base and index of an IndexNode. These are used 2972 * both as target and as an extra source. Previously it was problematic 2973 * for self modifying stores if the target/lhs didn't belong to one 2974 * of three trivial categories: IdentNode, AcessNodes, IndexNodes. In that 2975 * case it was evaluated and tagged as "resolved", which meant at the second 2976 * time the lhs of this store was read (e.g. in a = a (second) + b for a += b, 2977 * it would be evaluated to a nop in the scope and cause stack underflow 2978 * 2979 * see NASHORN-703 2980 * 2981 * @param <T> 2982 */ 2983 private abstract class SelfModifyingStore<T extends Node> extends Store<T> { 2984 protected SelfModifyingStore(final T assignNode, final Node target) { 2985 super(assignNode, target); 2986 } 2987 2988 @Override 2989 protected boolean isSelfModifying() { 2990 return true; 2991 } 2992 } 2993 2994 /** 2995 * Helper class to generate stores 2996 */ 2997 private abstract class Store<T extends Node> { 2998 2999 /** An assignment node, e.g. x += y */ 3000 protected final T assignNode; 3001 3002 /** The target node to store to, e.g. x */ 3003 private final Node target; 3004 3005 /** Should the result always be discarded, no matter what? */ 3006 private final boolean alwaysDiscard; 3007 3008 /** How deep on the stack do the arguments go if this generates an indy call */ 3009 private int depth; 3010 3011 /** If we have too many arguments, we need temporary storage, this is stored in 'quick' */ 3012 private Symbol quick; 3013 3014 /** 3015 * Constructor 3016 * 3017 * @param assignNode the node representing the whole assignment 3018 * @param target the target node of the assignment (destination) 3019 */ 3020 protected Store(final T assignNode, final Node target) { 3021 this.assignNode = assignNode; 3022 this.target = target; 3023 this.alwaysDiscard = assignNode == target; 3024 } 3025 3026 /** 3027 * Constructor 3028 * 3029 * @param assignNode the node representing the whole assignment 3030 */ 3031 protected Store(final T assignNode) { 3032 this(assignNode, assignNode); 3033 } 3034 3035 /** 3036 * Is this a self modifying store operation, e.g. *= or ++ 3037 * @return true if self modifying store 3038 */ 3039 protected boolean isSelfModifying() { 3040 return false; 3041 } 3042 3043 private void prologue() { 3044 final Symbol targetSymbol = target.getSymbol(); 3045 final Symbol scopeSymbol = getCurrentFunctionNode().getScopeNode().getSymbol(); 3046 3047 /** 3048 * This loads the parts of the target, e.g base and index. they are kept 3049 * on the stack throughout the store and used at the end to execute it 3050 */ 3051 3052 target.accept(new NodeVisitor(compileUnit, method) { 3053 @Override 3054 public Node enter(final IdentNode node) { 3055 if (targetSymbol.isScope()) { 3056 method.load(scopeSymbol); 3057 depth++; 3058 } 3059 return null; 3060 } 3061 3062 private void enterBaseNode() { 3063 assert target instanceof BaseNode : "error - base node " + target + " must be instanceof BaseNode"; 3064 final BaseNode baseNode = (BaseNode)target; 3065 final Node base = baseNode.getBase(); 3066 3067 load(base); 3068 method.convert(Type.OBJECT); 3069 depth += Type.OBJECT.getSlots(); 3070 3071 if (isSelfModifying()) { 3072 method.dup(); 3073 } 3074 } 3075 3076 @Override 3077 public Node enter(final AccessNode node) { 3078 enterBaseNode(); 3079 return null; 3080 } 3081 3082 @Override 3083 public Node enter(final IndexNode node) { 3084 enterBaseNode(); 3085 3086 final Node index = node.getIndex(); 3087 // could be boolean here as well 3088 load(index); 3089 if (!index.getType().isNumeric()) { 3090 method.convert(Type.OBJECT); 3091 } 3092 depth += index.getType().getSlots(); 3093 3094 if (isSelfModifying()) { 3095 //convert "base base index" to "base index base index" 3096 method.dup(1); 3097 } 3098 3099 return null; 3100 } 3101 3102 }); 3103 } 3104 3105 private Symbol quickSymbol(final Type type) { 3106 return quickSymbol(type, QUICK_PREFIX.tag()); 3107 } 3108 3109 /** 3110 * Quick symbol generates an extra local variable, always using the same 3111 * slot, one that is available after the end of the frame. 3112 * 3113 * @param type the type of the symbol 3114 * @param prefix the prefix for the variable name for the symbol 3115 * 3116 * @return the quick symbol 3117 */ 3118 private Symbol quickSymbol(final Type type, final String prefix) { 3119 final String name = compiler.uniqueName(prefix); 3120 final Symbol symbol = new Symbol(name, IS_TEMP | IS_INTERNAL, null, null); 3121 3122 symbol.setType(type); 3123 symbol.setSlot(getCurrentBlock().getFrame().getSlotCount()); 3124 3125 return symbol; 3126 } 3127 3128 // store the result that "lives on" after the op, e.g. "i" in i++ postfix. 3129 protected void storeNonDiscard() { 3130 if (assignNode.shouldDiscard() || alwaysDiscard) { 3131 assignNode.setDiscard(false); 3132 return; 3133 } 3134 3135 final Symbol symbol = assignNode.getSymbol(); 3136 if (symbol.hasSlot()) { 3137 method.dup().store(symbol); 3138 return; 3139 } 3140 3141 if (method.dup(depth) == null) { 3142 method.dup(); 3143 this.quick = quickSymbol(method.peekType()); 3144 method.store(quick); 3145 } 3146 } 3147 3148 private void epilogue() { 3149 final Symbol targetSymbol = target.getSymbol(); 3150 final FunctionNode currentFunction = getCurrentFunctionNode(); 3151 3152 /** 3153 * Take the original target args from the stack and use them 3154 * together with the value to be stored to emit the store code 3155 */ 3156 if (targetSymbol.hasSlot()) { 3157 method.convert(target.getType()); 3158 } 3159 3160 target.accept(new NodeVisitor(compileUnit, method) { 3161 @Override 3162 public Node enter(final IdentNode node) { 3163 final Symbol symbol = target.getSymbol(); 3164 if (symbol.isScope()) { 3165 if(symbol.isFastScope(currentFunction)) { 3166 storeFastScopeVar(target.getType(), symbol, CALLSITE_SCOPE | getCallSiteFlags()); 3167 } else { 3168 method.dynamicSet(target.getType(), node.getName(), CALLSITE_SCOPE | getCallSiteFlags()); 3169 } 3170 } else { 3171 assert targetSymbol != null; 3172 method.store(targetSymbol); 3173 } 3174 return null; 3175 3176 } 3177 3178 @Override 3179 public Node enter(final AccessNode node) { 3180 method.dynamicSet(node.getProperty().getType(), node.getProperty().getName(), getCallSiteFlags()); 3181 return null; 3182 } 3183 3184 @Override 3185 public Node enter(final IndexNode node) { 3186 method.dynamicSetIndex(getCallSiteFlags()); 3187 return null; 3188 } 3189 }); 3190 3191 3192 // whatever is on the stack now is the final answer 3193 } 3194 3195 protected abstract void evaluate(); 3196 3197 void store() { 3198 prologue(); 3199 evaluate(); // leaves an operation of whatever the operationType was on the stack 3200 storeNonDiscard(); 3201 epilogue(); 3202 if (quick != null) { 3203 method.load(quick); 3204 } 3205 } 3206 3207 } 3208 3209 /* 3210 * Globals are special. We cannot refer to any Global (or NativeObject) class by .class, as they are different 3211 * for different contexts. As far as I can tell, the only NativeObject that we need to deal with like this 3212 * is from the code pipeline is Global 3213 */ 3214 private MethodEmitter globalInstance() { 3215 return method.invokeStatic(Compiler.GLOBAL_OBJECT, "instance", "()L" + Compiler.GLOBAL_OBJECT + ';'); 3216 } 3217 3218 private MethodEmitter globalObjectPrototype() { 3219 return method.invokeStatic(Compiler.GLOBAL_OBJECT, "objectPrototype", methodDescriptor(ScriptObject.class)); 3220 } 3221 3222 private MethodEmitter globalAllocateArguments() { 3223 return method.invokeStatic(Compiler.GLOBAL_OBJECT, "allocateArguments", methodDescriptor(Object.class, Object[].class, Object.class, int.class)); 3224 } 3225 3226 private MethodEmitter globalNewRegExp() { 3227 return method.invokeStatic(Compiler.GLOBAL_OBJECT, "newRegExp", methodDescriptor(Object.class, String.class, String.class)); 3228 } 3229 3230 private MethodEmitter globalRegExpCopy() { 3231 return method.invokeStatic(Compiler.GLOBAL_OBJECT, "regExpCopy", methodDescriptor(Object.class, Object.class)); 3232 } 3233 3234 private MethodEmitter globalAllocateArray(final ArrayType type) { 3235 //make sure the native array is treated as an array type 3236 return method.invokeStatic(Compiler.GLOBAL_OBJECT, "allocate", methodDescriptor(Object.class, type.getTypeClass()), type); 3237 } 3238 3239 private MethodEmitter globalIsEval() { 3240 return method.invokeStatic(Compiler.GLOBAL_OBJECT, "isEval", methodDescriptor(boolean.class, Object.class)); 3241 } 3242 3243 private MethodEmitter globalDirectEval() { 3244 return method.invokeStatic(Compiler.GLOBAL_OBJECT, "directEval", 3245 methodDescriptor(Object.class, Object.class, Object.class, Object.class, Object.class, Object.class)); 3246 } 3247 }