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