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.CompilerConstants.ARGUMENTS; 29 import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS_VAR; 30 import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE; 31 import static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX; 32 import static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX; 33 import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN; 34 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; 35 import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX; 36 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; 37 import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS; 38 import static jdk.nashorn.internal.ir.Symbol.HAS_OBJECT_VALUE; 39 import static jdk.nashorn.internal.ir.Symbol.IS_CONST; 40 import static jdk.nashorn.internal.ir.Symbol.IS_FUNCTION_SELF; 41 import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL; 42 import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL; 43 import static jdk.nashorn.internal.ir.Symbol.IS_LET; 44 import static jdk.nashorn.internal.ir.Symbol.IS_PARAM; 45 import static jdk.nashorn.internal.ir.Symbol.IS_PROGRAM_LEVEL; 46 import static jdk.nashorn.internal.ir.Symbol.IS_SCOPE; 47 import static jdk.nashorn.internal.ir.Symbol.IS_THIS; 48 import static jdk.nashorn.internal.ir.Symbol.IS_VAR; 49 import static jdk.nashorn.internal.ir.Symbol.KINDMASK; 50 51 import java.util.ArrayDeque; 52 import java.util.ArrayList; 53 import java.util.Deque; 54 import java.util.HashMap; 55 import java.util.HashSet; 56 import java.util.Iterator; 57 import java.util.List; 58 import java.util.ListIterator; 59 import java.util.Map; 60 import java.util.Set; 61 import jdk.nashorn.internal.ir.AccessNode; 62 import jdk.nashorn.internal.ir.BinaryNode; 63 import jdk.nashorn.internal.ir.Block; 64 import jdk.nashorn.internal.ir.CatchNode; 65 import jdk.nashorn.internal.ir.Expression; 66 import jdk.nashorn.internal.ir.ForNode; 67 import jdk.nashorn.internal.ir.FunctionNode; 68 import jdk.nashorn.internal.ir.FunctionNode.CompilationState; 69 import jdk.nashorn.internal.ir.IdentNode; 70 import jdk.nashorn.internal.ir.IndexNode; 71 import jdk.nashorn.internal.ir.LexicalContext; 72 import jdk.nashorn.internal.ir.LexicalContextNode; 73 import jdk.nashorn.internal.ir.LiteralNode; 74 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; 75 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; 76 import jdk.nashorn.internal.ir.Node; 77 import jdk.nashorn.internal.ir.RuntimeNode; 78 import jdk.nashorn.internal.ir.RuntimeNode.Request; 79 import jdk.nashorn.internal.ir.Statement; 80 import jdk.nashorn.internal.ir.SwitchNode; 81 import jdk.nashorn.internal.ir.Symbol; 82 import jdk.nashorn.internal.ir.TryNode; 83 import jdk.nashorn.internal.ir.UnaryNode; 84 import jdk.nashorn.internal.ir.VarNode; 85 import jdk.nashorn.internal.ir.WithNode; 86 import jdk.nashorn.internal.ir.visitor.NodeVisitor; 87 import jdk.nashorn.internal.runtime.Context; 88 import jdk.nashorn.internal.runtime.ECMAErrors; 89 import jdk.nashorn.internal.runtime.ErrorManager; 90 import jdk.nashorn.internal.runtime.JSErrorType; 91 import jdk.nashorn.internal.runtime.ParserException; 92 import jdk.nashorn.internal.runtime.Source; 93 import jdk.nashorn.internal.runtime.logging.DebugLogger; 94 import jdk.nashorn.internal.runtime.logging.Loggable; 95 import jdk.nashorn.internal.runtime.logging.Logger; 96 97 /** 98 * This visitor assigns symbols to identifiers denoting variables. It does few more minor calculations that are only 99 * possible after symbols have been assigned; such is the transformation of "delete" and "typeof" operators into runtime 100 * nodes and counting of number of properties assigned to "this" in constructor functions. This visitor is also notable 101 * for what it doesn't do, most significantly it does no type calculations as in JavaScript variables can change types 102 * during runtime and as such symbols don't have types. Calculation of expression types is performed by a separate 103 * visitor. 104 */ 105 @Logger(name="symbols") 106 final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggable { 107 private final DebugLogger log; 108 private final boolean debug; 109 110 private static boolean isParamOrVar(final IdentNode identNode) { 111 final Symbol symbol = identNode.getSymbol(); 112 return symbol.isParam() || symbol.isVar(); 113 } 114 115 private static String name(final Node node) { 116 final String cn = node.getClass().getName(); 117 final int lastDot = cn.lastIndexOf('.'); 118 if (lastDot == -1) { 119 return cn; 120 } 121 return cn.substring(lastDot + 1); 122 } 123 124 /** 125 * Checks if various symbols that were provisionally marked as needing a slot ended up unused, and marks them as not 126 * needing a slot after all. 127 * @param functionNode the function node 128 * @return the passed in node, for easy chaining 129 */ 130 private static FunctionNode removeUnusedSlots(final FunctionNode functionNode) { 131 if (!functionNode.needsCallee()) { 132 functionNode.compilerConstant(CALLEE).setNeedsSlot(false); 133 } 134 if (!(functionNode.hasScopeBlock() || functionNode.needsParentScope())) { 135 functionNode.compilerConstant(SCOPE).setNeedsSlot(false); 136 } 137 // Named function expressions that end up not referencing themselves won't need a local slot for the self symbol. 138 if(!functionNode.isDeclared() && !functionNode.usesSelfSymbol() && !functionNode.isAnonymous()) { 139 final Symbol selfSymbol = functionNode.getBody().getExistingSymbol(functionNode.getIdent().getName()); 140 if(selfSymbol != null) { 141 if(selfSymbol.isFunctionSelf()) { 142 selfSymbol.setNeedsSlot(false); 143 selfSymbol.clearFlag(Symbol.IS_VAR); 144 } 145 } else { 146 assert functionNode.isProgram(); 147 } 148 } 149 return functionNode; 150 } 151 152 private final Deque<Set<String>> thisProperties = new ArrayDeque<>(); 153 private final Map<String, Symbol> globalSymbols = new HashMap<>(); //reuse the same global symbol 154 private final Compiler compiler; 155 156 public AssignSymbols(final Compiler compiler) { 157 super(new LexicalContext()); 158 this.compiler = compiler; 159 this.log = initLogger(compiler.getContext()); 160 this.debug = log.isEnabled(); 161 } 162 163 @Override 164 public DebugLogger getLogger() { 165 return log; 166 } 167 168 @Override 169 public DebugLogger initLogger(final Context context) { 170 return context.getLogger(this.getClass()); 171 } 172 173 /** 174 * Define symbols for all variable declarations at the top of the function scope. This way we can get around 175 * problems like 176 * 177 * while (true) { 178 * break; 179 * if (true) { 180 * var s; 181 * } 182 * } 183 * 184 * to an arbitrary nesting depth. 185 * 186 * see NASHORN-73 187 * 188 * @param functionNode the FunctionNode we are entering 189 * @param body the body of the FunctionNode we are entering 190 */ 191 private void acceptDeclarations(final FunctionNode functionNode, final Block body) { 192 // This visitor will assign symbol to all declared variables. 193 body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 194 @Override 195 protected boolean enterDefault(final Node node) { 196 // Don't bother visiting expressions; var is a statement, it can't be inside an expression. 197 // This will also prevent visiting nested functions (as FunctionNode is an expression). 198 return !(node instanceof Expression); 199 } 200 201 @Override 202 public Node leaveVarNode(final VarNode varNode) { 203 final IdentNode ident = varNode.getName(); 204 final boolean blockScoped = varNode.isBlockScoped(); 205 if (blockScoped && lc.inUnprotectedSwitchContext()) { 206 throwUnprotectedSwitchError(varNode); 207 } 208 final Block block = blockScoped ? lc.getCurrentBlock() : body; 209 final Symbol symbol = defineSymbol(block, ident.getName(), ident, varNode.getSymbolFlags()); 210 if (varNode.isFunctionDeclaration()) { 211 symbol.setIsFunctionDeclaration(); 212 } 213 return varNode.setName(ident.setSymbol(symbol)); 214 } 215 }); 216 } 217 218 private IdentNode compilerConstantIdentifier(final CompilerConstants cc) { 219 return createImplicitIdentifier(cc.symbolName()).setSymbol(lc.getCurrentFunction().compilerConstant(cc)); 220 } 221 222 /** 223 * Creates an ident node for an implicit identifier within the function (one not declared in the script source 224 * code). These identifiers are defined with function's token and finish. 225 * @param name the name of the identifier 226 * @return an ident node representing the implicit identifier. 227 */ 228 private IdentNode createImplicitIdentifier(final String name) { 229 final FunctionNode fn = lc.getCurrentFunction(); 230 return new IdentNode(fn.getToken(), fn.getFinish(), name); 231 } 232 233 private Symbol createSymbol(final String name, final int flags) { 234 if ((flags & Symbol.KINDMASK) == IS_GLOBAL) { 235 //reuse global symbols so they can be hashed 236 Symbol global = globalSymbols.get(name); 237 if (global == null) { 238 global = new Symbol(name, flags); 239 globalSymbols.put(name, global); 240 } 241 return global; 242 } 243 return new Symbol(name, flags); 244 } 245 246 /** 247 * Creates a synthetic initializer for a variable (a var statement that doesn't occur in the source code). Typically 248 * used to create assignmnent of {@code :callee} to the function name symbol in self-referential function 249 * expressions as well as for assignment of {@code :arguments} to {@code arguments}. 250 * 251 * @param name the ident node identifying the variable to initialize 252 * @param initConstant the compiler constant it is initialized to 253 * @param fn the function node the assignment is for 254 * @return a var node with the appropriate assignment 255 */ 256 private VarNode createSyntheticInitializer(final IdentNode name, final CompilerConstants initConstant, final FunctionNode fn) { 257 final IdentNode init = compilerConstantIdentifier(initConstant); 258 assert init.getSymbol() != null && init.getSymbol().isBytecodeLocal(); 259 260 final VarNode synthVar = new VarNode(fn.getLineNumber(), fn.getToken(), fn.getFinish(), name, init); 261 262 final Symbol nameSymbol = fn.getBody().getExistingSymbol(name.getName()); 263 assert nameSymbol != null; 264 265 return (VarNode)synthVar.setName(name.setSymbol(nameSymbol)).accept(this); 266 } 267 268 private FunctionNode createSyntheticInitializers(final FunctionNode functionNode) { 269 final List<VarNode> syntheticInitializers = new ArrayList<>(2); 270 271 // Must visit the new var nodes in the context of the body. We could also just set the new statements into the 272 // block and then revisit the entire block, but that seems to be too much double work. 273 final Block body = functionNode.getBody(); 274 lc.push(body); 275 try { 276 if (functionNode.usesSelfSymbol()) { 277 // "var fn = :callee" 278 syntheticInitializers.add(createSyntheticInitializer(functionNode.getIdent(), CALLEE, functionNode)); 279 } 280 281 if (functionNode.needsArguments()) { 282 // "var arguments = :arguments" 283 syntheticInitializers.add(createSyntheticInitializer(createImplicitIdentifier(ARGUMENTS_VAR.symbolName()), 284 ARGUMENTS, functionNode)); 285 } 286 287 if (syntheticInitializers.isEmpty()) { 288 return functionNode; 289 } 290 291 for(final ListIterator<VarNode> it = syntheticInitializers.listIterator(); it.hasNext();) { 292 it.set((VarNode)it.next().accept(this)); 293 } 294 } finally { 295 lc.pop(body); 296 } 297 298 final List<Statement> stmts = body.getStatements(); 299 final List<Statement> newStatements = new ArrayList<>(stmts.size() + syntheticInitializers.size()); 300 newStatements.addAll(syntheticInitializers); 301 newStatements.addAll(stmts); 302 return functionNode.setBody(lc, body.setStatements(lc, newStatements)); 303 } 304 305 /** 306 * Defines a new symbol in the given block. 307 * 308 * @param block the block in which to define the symbol 309 * @param name name of symbol. 310 * @param origin origin node 311 * @param symbolFlags Symbol flags. 312 * 313 * @return Symbol for given name or null for redefinition. 314 */ 315 private Symbol defineSymbol(final Block block, final String name, final Node origin, final int symbolFlags) { 316 int flags = symbolFlags; 317 final boolean isBlockScope = (flags & IS_LET) != 0 || (flags & IS_CONST) != 0; 318 final boolean isGlobal = (flags & KINDMASK) == IS_GLOBAL; 319 320 Symbol symbol; 321 final FunctionNode function; 322 if (isBlockScope) { 323 // block scoped variables always live in current block, no need to look for existing symbols in parent blocks. 324 symbol = block.getExistingSymbol(name); 325 function = lc.getCurrentFunction(); 326 } else { 327 symbol = findSymbol(block, name); 328 function = lc.getFunction(block); 329 } 330 331 // Global variables are implicitly always scope variables too. 332 if (isGlobal) { 333 flags |= IS_SCOPE; 334 } 335 336 if (lc.getCurrentFunction().isProgram()) { 337 flags |= IS_PROGRAM_LEVEL; 338 } 339 340 final boolean isParam = (flags & KINDMASK) == IS_PARAM; 341 final boolean isVar = (flags & KINDMASK) == IS_VAR; 342 343 if (symbol != null) { 344 // Symbol was already defined. Check if it needs to be redefined. 345 if (isParam) { 346 if (!isLocal(function, symbol)) { 347 // Not defined in this function. Create a new definition. 348 symbol = null; 349 } else if (symbol.isParam()) { 350 // Duplicate parameter. Null return will force an error. 351 throw new AssertionError("duplicate parameter"); 352 } 353 } else if (isVar) { 354 if (isBlockScope) { 355 // Check redeclaration in same block 356 if (symbol.hasBeenDeclared()) { 357 throwParserException(ECMAErrors.getMessage("syntax.error.redeclare.variable", name), origin); 358 } else { 359 symbol.setHasBeenDeclared(); 360 // Set scope flag on top-level block scoped symbols 361 if (function.isProgram() && function.getBody() == block) { 362 symbol.setIsScope(); 363 } 364 } 365 } else if ((flags & IS_INTERNAL) != 0) { 366 // Always create a new definition. 367 symbol = null; 368 } else { 369 // Found LET or CONST in parent scope of same function - s SyntaxError 370 if (symbol.isBlockScoped() && isLocal(lc.getCurrentFunction(), symbol)) { 371 throwParserException(ECMAErrors.getMessage("syntax.error.redeclare.variable", name), origin); 372 } 373 // Not defined in this function. Create a new definition. 374 if (!isLocal(function, symbol) || symbol.less(IS_VAR)) { 375 symbol = null; 376 } 377 } 378 } 379 } 380 381 if (symbol == null) { 382 // If not found, then create a new one. 383 final Block symbolBlock; 384 385 // Determine where to create it. 386 if (isVar && ((flags & IS_INTERNAL) != 0 || isBlockScope)) { 387 symbolBlock = block; //internal vars are always defined in the block closest to them 388 } else if (isGlobal) { 389 symbolBlock = lc.getOutermostFunction().getBody(); 390 } else { 391 symbolBlock = lc.getFunctionBody(function); 392 } 393 394 // Create and add to appropriate block. 395 symbol = createSymbol(name, flags); 396 symbolBlock.putSymbol(lc, symbol); 397 398 if ((flags & IS_SCOPE) == 0) { 399 // Initial assumption; symbol can lose its slot later 400 symbol.setNeedsSlot(true); 401 } 402 } else if (symbol.less(flags)) { 403 symbol.setFlags(flags); 404 } 405 406 return symbol; 407 } 408 409 private <T extends Node> T end(final T node) { 410 return end(node, true); 411 } 412 413 private <T extends Node> T end(final T node, final boolean printNode) { 414 if (debug) { 415 final StringBuilder sb = new StringBuilder(); 416 417 sb.append("[LEAVE "). 418 append(name(node)). 419 append("] "). 420 append(printNode ? node.toString() : ""). 421 append(" in '"). 422 append(lc.getCurrentFunction().getName()). 423 append('\''); 424 425 if (node instanceof IdentNode) { 426 final Symbol symbol = ((IdentNode)node).getSymbol(); 427 if (symbol == null) { 428 sb.append(" <NO SYMBOL>"); 429 } else { 430 sb.append(" <symbol=").append(symbol).append('>'); 431 } 432 } 433 434 log.unindent(); 435 log.info(sb); 436 } 437 438 return node; 439 } 440 441 @Override 442 public boolean enterBlock(final Block block) { 443 start(block); 444 445 if (lc.isFunctionBody()) { 446 block.clearSymbols(); 447 final FunctionNode fn = lc.getCurrentFunction(); 448 if (isUnparsedFunction(fn)) { 449 // It's a skipped nested function. Just mark the symbols being used by it as being in use. 450 for(final String name: compiler.getScriptFunctionData(fn.getId()).getExternalSymbolNames()) { 451 nameIsUsed(name, null); 452 } 453 // Don't bother descending into it, it must be empty anyway. 454 assert block.getStatements().isEmpty(); 455 return false; 456 } 457 458 enterFunctionBody(); 459 } 460 461 return true; 462 } 463 464 private boolean isUnparsedFunction(final FunctionNode fn) { 465 return compiler.isOnDemandCompilation() && fn != lc.getOutermostFunction(); 466 } 467 468 @Override 469 public boolean enterCatchNode(final CatchNode catchNode) { 470 final IdentNode exception = catchNode.getException(); 471 final Block block = lc.getCurrentBlock(); 472 473 start(catchNode); 474 475 // define block-local exception variable 476 final String exname = exception.getName(); 477 // If the name of the exception starts with ":e", this is a synthetic catch block, likely a catch-all. Its 478 // symbol is naturally internal, and should be treated as such. 479 final boolean isInternal = exname.startsWith(EXCEPTION_PREFIX.symbolName()); 480 // IS_LET flag is required to make sure symbol is not visible outside catch block. However, we need to 481 // clear the IS_LET flag after creation to allow redefinition of symbol inside the catch block. 482 final Symbol symbol = defineSymbol(block, exname, catchNode, IS_VAR | IS_LET | (isInternal ? IS_INTERNAL : 0) | HAS_OBJECT_VALUE); 483 symbol.clearFlag(IS_LET); 484 485 return true; 486 } 487 488 private void enterFunctionBody() { 489 final FunctionNode functionNode = lc.getCurrentFunction(); 490 final Block body = lc.getCurrentBlock(); 491 492 initFunctionWideVariables(functionNode, body); 493 494 if (!functionNode.isProgram() && !functionNode.isDeclared() && !functionNode.isAnonymous()) { 495 // It's neither declared nor program - it's a function expression then; assign it a self-symbol unless it's 496 // anonymous. 497 final String name = functionNode.getIdent().getName(); 498 assert name != null; 499 assert body.getExistingSymbol(name) == null; 500 defineSymbol(body, name, functionNode, IS_VAR | IS_FUNCTION_SELF | HAS_OBJECT_VALUE); 501 if(functionNode.allVarsInScope()) { // basically, has deep eval 502 lc.setFlag(functionNode, FunctionNode.USES_SELF_SYMBOL); 503 } 504 } 505 506 acceptDeclarations(functionNode, body); 507 } 508 509 @Override 510 public boolean enterFunctionNode(final FunctionNode functionNode) { 511 start(functionNode, false); 512 513 thisProperties.push(new HashSet<String>()); 514 515 // Every function has a body, even the ones skipped on reparse (they have an empty one). We're 516 // asserting this as even for those, enterBlock() must be invoked to correctly process symbols that 517 // are used in them. 518 assert functionNode.getBody() != null; 519 520 return true; 521 } 522 523 @Override 524 public boolean enterVarNode(final VarNode varNode) { 525 start(varNode); 526 // Normally, a symbol assigned in a var statement is not live for its RHS. Since we also represent function 527 // declarations as VarNodes, they are exception to the rule, as they need to have the symbol visible to the 528 // body of the declared function for self-reference. 529 if (varNode.isFunctionDeclaration()) { 530 defineVarIdent(varNode); 531 } 532 return true; 533 } 534 535 @Override 536 public Node leaveVarNode(final VarNode varNode) { 537 if (!varNode.isFunctionDeclaration()) { 538 defineVarIdent(varNode); 539 } 540 return super.leaveVarNode(varNode); 541 } 542 543 private void defineVarIdent(final VarNode varNode) { 544 final IdentNode ident = varNode.getName(); 545 final int flags; 546 if (varNode.isAnonymousFunctionDeclaration()) { 547 flags = IS_INTERNAL; 548 } else if (!varNode.isBlockScoped() && lc.getCurrentFunction().isProgram()) { 549 flags = IS_SCOPE; 550 } else { 551 flags = 0; 552 } 553 defineSymbol(lc.getCurrentBlock(), ident.getName(), ident, varNode.getSymbolFlags() | flags); 554 } 555 556 private Symbol exceptionSymbol() { 557 return newObjectInternal(EXCEPTION_PREFIX); 558 } 559 560 /** 561 * This has to run before fix assignment types, store any type specializations for 562 * parameters, then turn them into objects for the generic version of this method. 563 * 564 * @param functionNode functionNode 565 */ 566 private FunctionNode finalizeParameters(final FunctionNode functionNode) { 567 final List<IdentNode> newParams = new ArrayList<>(); 568 final boolean isVarArg = functionNode.isVarArg(); 569 570 final Block body = functionNode.getBody(); 571 for (final IdentNode param : functionNode.getParameters()) { 572 final Symbol paramSymbol = body.getExistingSymbol(param.getName()); 573 assert paramSymbol != null; 574 assert paramSymbol.isParam() : paramSymbol + " " + paramSymbol.getFlags(); 575 newParams.add(param.setSymbol(paramSymbol)); 576 577 // parameters should not be slots for a function that uses variable arity signature 578 if (isVarArg) { 579 paramSymbol.setNeedsSlot(false); 580 } 581 } 582 583 return functionNode.setParameters(lc, newParams); 584 } 585 586 /** 587 * Search for symbol in the lexical context starting from the given block. 588 * @param name Symbol name. 589 * @return Found symbol or null if not found. 590 */ 591 private Symbol findSymbol(final Block block, final String name) { 592 for (final Iterator<Block> blocks = lc.getBlocks(block); blocks.hasNext();) { 593 final Symbol symbol = blocks.next().getExistingSymbol(name); 594 if (symbol != null) { 595 return symbol; 596 } 597 } 598 return null; 599 } 600 601 /** 602 * Marks the current function as one using any global symbol. The function and all its parent functions will all be 603 * marked as needing parent scope. 604 * @see FunctionNode#needsParentScope() 605 */ 606 private void functionUsesGlobalSymbol() { 607 for (final Iterator<FunctionNode> fns = lc.getFunctions(); fns.hasNext();) { 608 lc.setFlag(fns.next(), FunctionNode.USES_ANCESTOR_SCOPE); 609 } 610 } 611 612 /** 613 * Marks the current function as one using a scoped symbol. The block defining the symbol will be marked as needing 614 * its own scope to hold the variable. If the symbol is defined outside of the current function, it and all 615 * functions up to (but not including) the function containing the defining block will be marked as needing parent 616 * function scope. 617 * @see FunctionNode#needsParentScope() 618 */ 619 private void functionUsesScopeSymbol(final Symbol symbol) { 620 final String name = symbol.getName(); 621 for (final Iterator<LexicalContextNode> contextNodeIter = lc.getAllNodes(); contextNodeIter.hasNext(); ) { 622 final LexicalContextNode node = contextNodeIter.next(); 623 if (node instanceof Block) { 624 final Block block = (Block)node; 625 if (block.getExistingSymbol(name) != null) { 626 assert lc.contains(block); 627 lc.setBlockNeedsScope(block); 628 break; 629 } 630 } else if (node instanceof FunctionNode) { 631 lc.setFlag(node, FunctionNode.USES_ANCESTOR_SCOPE); 632 } 633 } 634 } 635 636 /** 637 * Declares that the current function is using the symbol. 638 * @param symbol the symbol used by the current function. 639 */ 640 private void functionUsesSymbol(final Symbol symbol) { 641 assert symbol != null; 642 if (symbol.isScope()) { 643 if (symbol.isGlobal()) { 644 functionUsesGlobalSymbol(); 645 } else { 646 functionUsesScopeSymbol(symbol); 647 } 648 } else { 649 assert !symbol.isGlobal(); // Every global is also scope 650 } 651 } 652 653 private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags) { 654 defineSymbol(block, cc.symbolName(), null, flags).setNeedsSlot(true); 655 } 656 657 private void initFunctionWideVariables(final FunctionNode functionNode, final Block body) { 658 initCompileConstant(CALLEE, body, IS_PARAM | IS_INTERNAL | HAS_OBJECT_VALUE); 659 initCompileConstant(THIS, body, IS_PARAM | IS_THIS | HAS_OBJECT_VALUE); 660 661 if (functionNode.isVarArg()) { 662 initCompileConstant(VARARGS, body, IS_PARAM | IS_INTERNAL | HAS_OBJECT_VALUE); 663 if (functionNode.needsArguments()) { 664 initCompileConstant(ARGUMENTS, body, IS_VAR | IS_INTERNAL | HAS_OBJECT_VALUE); 665 defineSymbol(body, ARGUMENTS_VAR.symbolName(), null, IS_VAR | HAS_OBJECT_VALUE); 666 } 667 } 668 669 initParameters(functionNode, body); 670 initCompileConstant(SCOPE, body, IS_VAR | IS_INTERNAL | HAS_OBJECT_VALUE); 671 initCompileConstant(RETURN, body, IS_VAR | IS_INTERNAL); 672 } 673 674 /** 675 * Initialize parameters for function node. 676 * @param functionNode the function node 677 */ 678 private void initParameters(final FunctionNode functionNode, final Block body) { 679 final boolean isVarArg = functionNode.isVarArg(); 680 final boolean scopeParams = functionNode.allVarsInScope() || isVarArg; 681 for (final IdentNode param : functionNode.getParameters()) { 682 final Symbol symbol = defineSymbol(body, param.getName(), param, IS_PARAM); 683 if(scopeParams) { 684 // NOTE: this "set is scope" is a poor substitute for clear expression of where the symbol is stored. 685 // It will force creation of scopes where they would otherwise not necessarily be needed (functions 686 // using arguments object and other variable arity functions). Tracked by JDK-8038942. 687 symbol.setIsScope(); 688 assert symbol.hasSlot(); 689 if(isVarArg) { 690 symbol.setNeedsSlot(false); 691 } 692 } 693 } 694 } 695 696 /** 697 * Is the symbol local to (that is, defined in) the specified function? 698 * @param function the function 699 * @param symbol the symbol 700 * @return true if the symbol is defined in the specified function 701 */ 702 private boolean isLocal(final FunctionNode function, final Symbol symbol) { 703 final FunctionNode definingFn = lc.getDefiningFunction(symbol); 704 assert definingFn != null; 705 return definingFn == function; 706 } 707 708 @Override 709 public Node leaveBinaryNode(final BinaryNode binaryNode) { 710 switch (binaryNode.tokenType()) { 711 case ASSIGN: 712 return leaveASSIGN(binaryNode); 713 default: 714 return super.leaveBinaryNode(binaryNode); 715 } 716 } 717 718 private Node leaveASSIGN(final BinaryNode binaryNode) { 719 // If we're assigning a property of the this object ("this.foo = ..."), record it. 720 final Expression lhs = binaryNode.lhs(); 721 if (lhs instanceof AccessNode) { 722 final AccessNode accessNode = (AccessNode) lhs; 723 final Expression base = accessNode.getBase(); 724 if (base instanceof IdentNode) { 725 final Symbol symbol = ((IdentNode)base).getSymbol(); 726 if(symbol.isThis()) { 727 thisProperties.peek().add(accessNode.getProperty()); 728 } 729 } 730 } 731 return binaryNode; 732 } 733 734 @Override 735 public Node leaveUnaryNode(final UnaryNode unaryNode) { 736 switch (unaryNode.tokenType()) { 737 case DELETE: 738 return leaveDELETE(unaryNode); 739 case TYPEOF: 740 return leaveTYPEOF(unaryNode); 741 default: 742 return super.leaveUnaryNode(unaryNode); 743 } 744 } 745 746 @Override 747 public Node leaveBlock(final Block block) { 748 // It's not necessary to guard the marking of symbols as locals with this "if" condition for 749 // correctness, it's just an optimization -- runtime type calculation is not used when the compilation 750 // is not an on-demand optimistic compilation, so we can skip locals marking then. 751 if (compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) { 752 // OTOH, we must not declare symbols from nested functions to be locals. As we're doing on-demand 753 // compilation, and we're skipping parsing the function bodies for nested functions, this 754 // basically only means their parameters. It'd be enough to mistakenly declare to be a local a 755 // symbol in the outer function named the same as one of the parameters, though. 756 if (lc.getFunction(block) == lc.getOutermostFunction()) { 757 for (final Symbol symbol: block.getSymbols()) { 758 if (!symbol.isScope()) { 759 assert symbol.isVar() || symbol.isParam(); 760 compiler.declareLocalSymbol(symbol.getName()); 761 } 762 } 763 } 764 } 765 return block; 766 } 767 768 private Node leaveDELETE(final UnaryNode unaryNode) { 769 final FunctionNode currentFunctionNode = lc.getCurrentFunction(); 770 final boolean strictMode = currentFunctionNode.isStrict(); 771 final Expression rhs = unaryNode.getExpression(); 772 final Expression strictFlagNode = (Expression)LiteralNode.newInstance(unaryNode, strictMode).accept(this); 773 774 Request request = Request.DELETE; 775 final List<Expression> args = new ArrayList<>(); 776 777 if (rhs instanceof IdentNode) { 778 final IdentNode ident = (IdentNode)rhs; 779 // If this is a declared variable or a function parameter, delete always fails (except for globals). 780 final String name = ident.getName(); 781 final Symbol symbol = ident.getSymbol(); 782 final boolean failDelete = strictMode || (!symbol.isScope() && (symbol.isParam() || (symbol.isVar() && !symbol.isProgramLevel()))); 783 784 if (failDelete && symbol.isThis()) { 785 return LiteralNode.newInstance(unaryNode, true).accept(this); 786 } 787 final Expression literalNode = (Expression)LiteralNode.newInstance(unaryNode, name).accept(this); 788 789 if (!failDelete) { 790 args.add(compilerConstantIdentifier(SCOPE)); 791 } 792 args.add(literalNode); 793 args.add(strictFlagNode); 794 795 if (failDelete) { 796 request = Request.FAIL_DELETE; 797 } 798 } else if (rhs instanceof AccessNode) { 799 final Expression base = ((AccessNode)rhs).getBase(); 800 final String property = ((AccessNode)rhs).getProperty(); 801 802 args.add(base); 803 args.add((Expression)LiteralNode.newInstance(unaryNode, property).accept(this)); 804 args.add(strictFlagNode); 805 806 } else if (rhs instanceof IndexNode) { 807 final IndexNode indexNode = (IndexNode)rhs; 808 final Expression base = indexNode.getBase(); 809 final Expression index = indexNode.getIndex(); 810 811 args.add(base); 812 args.add(index); 813 args.add(strictFlagNode); 814 815 } else { 816 return LiteralNode.newInstance(unaryNode, true).accept(this); 817 } 818 return new RuntimeNode(unaryNode, request, args).accept(this); 819 } 820 821 @Override 822 public Node leaveForNode(final ForNode forNode) { 823 if (forNode.isForIn()) { 824 forNode.setIterator(newObjectInternal(ITERATOR_PREFIX)); //NASHORN-73 825 } 826 827 return end(forNode); 828 } 829 830 @Override 831 public Node leaveFunctionNode(final FunctionNode functionNode) { 832 final FunctionNode finalizedFunction; 833 if (isUnparsedFunction(functionNode)) { 834 finalizedFunction = functionNode; 835 } else { 836 finalizedFunction = 837 markProgramBlock( 838 removeUnusedSlots( 839 createSyntheticInitializers( 840 finalizeParameters( 841 lc.applyTopFlags(functionNode)))) 842 .setThisProperties(lc, thisProperties.pop().size())); 843 } 844 return finalizedFunction.setState(lc, CompilationState.SYMBOLS_ASSIGNED); 845 } 846 847 @Override 848 public Node leaveIdentNode(final IdentNode identNode) { 849 if (identNode.isPropertyName()) { 850 return identNode; 851 } 852 853 final Symbol symbol = nameIsUsed(identNode.getName(), identNode); 854 855 if (!identNode.isInitializedHere()) { 856 symbol.increaseUseCount(); 857 } 858 859 IdentNode newIdentNode = identNode.setSymbol(symbol); 860 861 // If a block-scoped var is used before its declaration mark it as dead. 862 // We can only statically detect this for local vars, cross-function symbols require runtime checks. 863 if (symbol.isBlockScoped() && !symbol.hasBeenDeclared() && !identNode.isDeclaredHere() && isLocal(lc.getCurrentFunction(), symbol)) { 864 newIdentNode = newIdentNode.markDead(); 865 } 866 867 return end(newIdentNode); 868 } 869 870 private Symbol nameIsUsed(final String name, final IdentNode origin) { 871 final Block block = lc.getCurrentBlock(); 872 873 Symbol symbol = findSymbol(block, name); 874 875 //If an existing symbol with the name is found, use that otherwise, declare a new one 876 if (symbol != null) { 877 log.info("Existing symbol = ", symbol); 878 if (symbol.isFunctionSelf()) { 879 final FunctionNode functionNode = lc.getDefiningFunction(symbol); 880 assert functionNode != null; 881 assert lc.getFunctionBody(functionNode).getExistingSymbol(CALLEE.symbolName()) != null; 882 lc.setFlag(functionNode, FunctionNode.USES_SELF_SYMBOL); 883 } 884 885 // if symbol is non-local or we're in a with block, we need to put symbol in scope (if it isn't already) 886 maybeForceScope(symbol); 887 } else { 888 log.info("No symbol exists. Declare as global: ", name); 889 symbol = defineSymbol(block, name, origin, IS_GLOBAL | IS_SCOPE); 890 } 891 892 functionUsesSymbol(symbol); 893 return symbol; 894 } 895 896 @Override 897 public Node leaveSwitchNode(final SwitchNode switchNode) { 898 // We only need a symbol for the tag if it's not an integer switch node 899 if(!switchNode.isUniqueInteger()) { 900 switchNode.setTag(newObjectInternal(SWITCH_TAG_PREFIX)); 901 } 902 return switchNode; 903 } 904 905 @Override 906 public Node leaveTryNode(final TryNode tryNode) { 907 tryNode.setException(exceptionSymbol()); 908 assert tryNode.getFinallyBody() == null; 909 910 end(tryNode); 911 912 return tryNode; 913 } 914 915 private Node leaveTYPEOF(final UnaryNode unaryNode) { 916 final Expression rhs = unaryNode.getExpression(); 917 918 final List<Expression> args = new ArrayList<>(); 919 if (rhs instanceof IdentNode && !isParamOrVar((IdentNode)rhs)) { 920 args.add(compilerConstantIdentifier(SCOPE)); 921 args.add((Expression)LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null 922 } else { 923 args.add(rhs); 924 args.add((Expression)LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this' 925 } 926 927 final Node runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args).accept(this); 928 929 end(unaryNode); 930 931 return runtimeNode; 932 } 933 934 private FunctionNode markProgramBlock(final FunctionNode functionNode) { 935 if (compiler.isOnDemandCompilation() || !functionNode.isProgram()) { 936 return functionNode; 937 } 938 939 return functionNode.setBody(lc, functionNode.getBody().setFlag(lc, Block.IS_GLOBAL_SCOPE)); 940 } 941 942 /** 943 * If the symbol isn't already a scope symbol, but it needs to be (see {@link #symbolNeedsToBeScope(Symbol)}, it is 944 * promoted to a scope symbol and its block marked as needing a scope. 945 * @param symbol the symbol that might be scoped 946 */ 947 private void maybeForceScope(final Symbol symbol) { 948 if (!symbol.isScope() && symbolNeedsToBeScope(symbol)) { 949 Symbol.setSymbolIsScope(lc, symbol); 950 } 951 } 952 953 private Symbol newInternal(final CompilerConstants cc, final int flags) { 954 return defineSymbol(lc.getCurrentBlock(), lc.getCurrentFunction().uniqueName(cc.symbolName()), null, IS_VAR | IS_INTERNAL | flags); //NASHORN-73 955 } 956 957 private Symbol newObjectInternal(final CompilerConstants cc) { 958 return newInternal(cc, HAS_OBJECT_VALUE); 959 } 960 961 private boolean start(final Node node) { 962 return start(node, true); 963 } 964 965 private boolean start(final Node node, final boolean printNode) { 966 if (debug) { 967 final StringBuilder sb = new StringBuilder(); 968 969 sb.append("[ENTER "). 970 append(name(node)). 971 append("] "). 972 append(printNode ? node.toString() : ""). 973 append(" in '"). 974 append(lc.getCurrentFunction().getName()). 975 append("'"); 976 log.info(sb); 977 log.indent(); 978 } 979 980 return true; 981 } 982 983 /** 984 * Determines if the symbol has to be a scope symbol. In general terms, it has to be a scope symbol if it can only 985 * be reached from the current block by traversing a function node, a split node, or a with node. 986 * @param symbol the symbol checked for needing to be a scope symbol 987 * @return true if the symbol has to be a scope symbol. 988 */ 989 private boolean symbolNeedsToBeScope(final Symbol symbol) { 990 if (symbol.isThis() || symbol.isInternal()) { 991 return false; 992 } 993 994 final FunctionNode func = lc.getCurrentFunction(); 995 if ( func.allVarsInScope() || (!symbol.isBlockScoped() && func.isProgram())) { 996 return true; 997 } 998 999 boolean previousWasBlock = false; 1000 for (final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) { 1001 final LexicalContextNode node = it.next(); 1002 if (node instanceof FunctionNode || isSplitArray(node)) { 1003 // We reached the function boundary or a splitting boundary without seeing a definition for the symbol. 1004 // It needs to be in scope. 1005 return true; 1006 } else if (node instanceof WithNode) { 1007 if (previousWasBlock) { 1008 // We reached a WithNode; the symbol must be scoped. Note that if the WithNode was not immediately 1009 // preceded by a block, this means we're currently processing its expression, not its body, 1010 // therefore it doesn't count. 1011 return true; 1012 } 1013 previousWasBlock = false; 1014 } else if (node instanceof Block) { 1015 if (((Block)node).getExistingSymbol(symbol.getName()) == symbol) { 1016 // We reached the block that defines the symbol without reaching either the function boundary, or a 1017 // WithNode. The symbol need not be scoped. 1018 return false; 1019 } 1020 previousWasBlock = true; 1021 } else { 1022 previousWasBlock = false; 1023 } 1024 } 1025 throw new AssertionError(); 1026 } 1027 1028 private static boolean isSplitArray(final LexicalContextNode expr) { 1029 if(!(expr instanceof ArrayLiteralNode)) { 1030 return false; 1031 } 1032 final List<ArrayUnit> units = ((ArrayLiteralNode)expr).getUnits(); 1033 return !(units == null || units.isEmpty()); 1034 } 1035 1036 private void throwUnprotectedSwitchError(final VarNode varNode) { 1037 // Block scoped declarations in switch statements without explicit blocks should be declared 1038 // in a common block that contains all the case clauses. We cannot support this without a 1039 // fundamental rewrite of how switch statements are handled (case nodes contain blocks and are 1040 // directly contained by switch node). As a temporary solution we throw a reference error here. 1041 final String msg = ECMAErrors.getMessage("syntax.error.unprotected.switch.declaration", varNode.isLet() ? "let" : "const"); 1042 throwParserException(msg, varNode); 1043 } 1044 1045 private void throwParserException(final String message, final Node origin) { 1046 if (origin == null) { 1047 throw new ParserException(message); 1048 } 1049 final Source source = compiler.getSource(); 1050 final long token = origin.getToken(); 1051 final int line = source.getLine(origin.getStart()); 1052 final int column = source.getColumn(origin.getStart()); 1053 final String formatted = ErrorManager.format(message, source, line, column, token); 1054 throw new ParserException(JSErrorType.SYNTAX_ERROR, formatted, source, line, column, token); 1055 } 1056 }