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