AssignSymbols.java revision 1118:e26843ca558b
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.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 private void checkConstAssignment(final IdentNode ident) { 709 // Check for reassignment of constant 710 final Symbol symbol = ident.getSymbol(); 711 if (symbol.isConst()) { 712 throwParserException(ECMAErrors.getMessage("syntax.error.assign.constant", symbol.getName()), ident); 713 } 714 } 715 716 @Override 717 public Node leaveBinaryNode(final BinaryNode binaryNode) { 718 if (binaryNode.isAssignment() && binaryNode.lhs() instanceof IdentNode) { 719 checkConstAssignment((IdentNode) binaryNode.lhs()); 720 } 721 switch (binaryNode.tokenType()) { 722 case ASSIGN: 723 return leaveASSIGN(binaryNode); 724 default: 725 return super.leaveBinaryNode(binaryNode); 726 } 727 } 728 729 private Node leaveASSIGN(final BinaryNode binaryNode) { 730 // If we're assigning a property of the this object ("this.foo = ..."), record it. 731 final Expression lhs = binaryNode.lhs(); 732 if (lhs instanceof AccessNode) { 733 final AccessNode accessNode = (AccessNode) lhs; 734 final Expression base = accessNode.getBase(); 735 if (base instanceof IdentNode) { 736 final Symbol symbol = ((IdentNode)base).getSymbol(); 737 if(symbol.isThis()) { 738 thisProperties.peek().add(accessNode.getProperty()); 739 } 740 } 741 } 742 return binaryNode; 743 } 744 745 @Override 746 public Node leaveUnaryNode(final UnaryNode unaryNode) { 747 if (unaryNode.isAssignment() && unaryNode.getExpression() instanceof IdentNode) { 748 checkConstAssignment((IdentNode) unaryNode.getExpression()); 749 } 750 switch (unaryNode.tokenType()) { 751 case DELETE: 752 return leaveDELETE(unaryNode); 753 case TYPEOF: 754 return leaveTYPEOF(unaryNode); 755 default: 756 return super.leaveUnaryNode(unaryNode); 757 } 758 } 759 760 @Override 761 public Node leaveBlock(final Block block) { 762 // It's not necessary to guard the marking of symbols as locals with this "if" condition for 763 // correctness, it's just an optimization -- runtime type calculation is not used when the compilation 764 // is not an on-demand optimistic compilation, so we can skip locals marking then. 765 if (compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) { 766 // OTOH, we must not declare symbols from nested functions to be locals. As we're doing on-demand 767 // compilation, and we're skipping parsing the function bodies for nested functions, this 768 // basically only means their parameters. It'd be enough to mistakenly declare to be a local a 769 // symbol in the outer function named the same as one of the parameters, though. 770 if (lc.getFunction(block) == lc.getOutermostFunction()) { 771 for (final Symbol symbol: block.getSymbols()) { 772 if (!symbol.isScope()) { 773 assert symbol.isVar() || symbol.isParam(); 774 compiler.declareLocalSymbol(symbol.getName()); 775 } 776 } 777 } 778 } 779 return block; 780 } 781 782 private Node leaveDELETE(final UnaryNode unaryNode) { 783 final FunctionNode currentFunctionNode = lc.getCurrentFunction(); 784 final boolean strictMode = currentFunctionNode.isStrict(); 785 final Expression rhs = unaryNode.getExpression(); 786 final Expression strictFlagNode = (Expression)LiteralNode.newInstance(unaryNode, strictMode).accept(this); 787 788 Request request = Request.DELETE; 789 final List<Expression> args = new ArrayList<>(); 790 791 if (rhs instanceof IdentNode) { 792 final IdentNode ident = (IdentNode)rhs; 793 // If this is a declared variable or a function parameter, delete always fails (except for globals). 794 final String name = ident.getName(); 795 final Symbol symbol = ident.getSymbol(); 796 final boolean failDelete = strictMode || (!symbol.isScope() && (symbol.isParam() || (symbol.isVar() && !symbol.isProgramLevel()))); 797 798 if (failDelete && symbol.isThis()) { 799 return LiteralNode.newInstance(unaryNode, true).accept(this); 800 } 801 final Expression literalNode = (Expression)LiteralNode.newInstance(unaryNode, name).accept(this); 802 803 if (!failDelete) { 804 args.add(compilerConstantIdentifier(SCOPE)); 805 } 806 args.add(literalNode); 807 args.add(strictFlagNode); 808 809 if (failDelete) { 810 request = Request.FAIL_DELETE; 811 } 812 } else if (rhs instanceof AccessNode) { 813 final Expression base = ((AccessNode)rhs).getBase(); 814 final String property = ((AccessNode)rhs).getProperty(); 815 816 args.add(base); 817 args.add((Expression)LiteralNode.newInstance(unaryNode, property).accept(this)); 818 args.add(strictFlagNode); 819 820 } else if (rhs instanceof IndexNode) { 821 final IndexNode indexNode = (IndexNode)rhs; 822 final Expression base = indexNode.getBase(); 823 final Expression index = indexNode.getIndex(); 824 825 args.add(base); 826 args.add(index); 827 args.add(strictFlagNode); 828 829 } else { 830 return LiteralNode.newInstance(unaryNode, true).accept(this); 831 } 832 return new RuntimeNode(unaryNode, request, args).accept(this); 833 } 834 835 @Override 836 public Node leaveForNode(final ForNode forNode) { 837 if (forNode.isForIn()) { 838 forNode.setIterator(newObjectInternal(ITERATOR_PREFIX)); //NASHORN-73 839 } 840 841 return end(forNode); 842 } 843 844 @Override 845 public Node leaveFunctionNode(final FunctionNode functionNode) { 846 final FunctionNode finalizedFunction; 847 if (isUnparsedFunction(functionNode)) { 848 finalizedFunction = functionNode; 849 } else { 850 finalizedFunction = 851 markProgramBlock( 852 removeUnusedSlots( 853 createSyntheticInitializers( 854 finalizeParameters( 855 lc.applyTopFlags(functionNode)))) 856 .setThisProperties(lc, thisProperties.pop().size())); 857 } 858 return finalizedFunction.setState(lc, CompilationState.SYMBOLS_ASSIGNED); 859 } 860 861 @Override 862 public Node leaveIdentNode(final IdentNode identNode) { 863 if (identNode.isPropertyName()) { 864 return identNode; 865 } 866 867 final Symbol symbol = nameIsUsed(identNode.getName(), identNode); 868 869 if (!identNode.isInitializedHere()) { 870 symbol.increaseUseCount(); 871 } 872 873 IdentNode newIdentNode = identNode.setSymbol(symbol); 874 875 // If a block-scoped var is used before its declaration mark it as dead. 876 // We can only statically detect this for local vars, cross-function symbols require runtime checks. 877 if (symbol.isBlockScoped() && !symbol.hasBeenDeclared() && !identNode.isDeclaredHere() && isLocal(lc.getCurrentFunction(), symbol)) { 878 newIdentNode = newIdentNode.markDead(); 879 } 880 881 return end(newIdentNode); 882 } 883 884 private Symbol nameIsUsed(final String name, final IdentNode origin) { 885 final Block block = lc.getCurrentBlock(); 886 887 Symbol symbol = findSymbol(block, name); 888 889 //If an existing symbol with the name is found, use that otherwise, declare a new one 890 if (symbol != null) { 891 log.info("Existing symbol = ", symbol); 892 if (symbol.isFunctionSelf()) { 893 final FunctionNode functionNode = lc.getDefiningFunction(symbol); 894 assert functionNode != null; 895 assert lc.getFunctionBody(functionNode).getExistingSymbol(CALLEE.symbolName()) != null; 896 lc.setFlag(functionNode, FunctionNode.USES_SELF_SYMBOL); 897 } 898 899 // if symbol is non-local or we're in a with block, we need to put symbol in scope (if it isn't already) 900 maybeForceScope(symbol); 901 } else { 902 log.info("No symbol exists. Declare as global: ", name); 903 symbol = defineSymbol(block, name, origin, IS_GLOBAL | IS_SCOPE); 904 } 905 906 functionUsesSymbol(symbol); 907 return symbol; 908 } 909 910 @Override 911 public Node leaveSwitchNode(final SwitchNode switchNode) { 912 // We only need a symbol for the tag if it's not an integer switch node 913 if(!switchNode.isInteger()) { 914 switchNode.setTag(newObjectInternal(SWITCH_TAG_PREFIX)); 915 } 916 return switchNode; 917 } 918 919 @Override 920 public Node leaveTryNode(final TryNode tryNode) { 921 tryNode.setException(exceptionSymbol()); 922 if (tryNode.getFinallyBody() != null) { 923 tryNode.setFinallyCatchAll(exceptionSymbol()); 924 } 925 926 end(tryNode); 927 928 return tryNode; 929 } 930 931 private Node leaveTYPEOF(final UnaryNode unaryNode) { 932 final Expression rhs = unaryNode.getExpression(); 933 934 final List<Expression> args = new ArrayList<>(); 935 if (rhs instanceof IdentNode && !isParamOrVar((IdentNode)rhs)) { 936 args.add(compilerConstantIdentifier(SCOPE)); 937 args.add((Expression)LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null 938 } else { 939 args.add(rhs); 940 args.add((Expression)LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this' 941 } 942 943 final Node runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args).accept(this); 944 945 end(unaryNode); 946 947 return runtimeNode; 948 } 949 950 private FunctionNode markProgramBlock(final FunctionNode functionNode) { 951 if (compiler.isOnDemandCompilation() || !functionNode.isProgram()) { 952 return functionNode; 953 } 954 955 return functionNode.setBody(lc, functionNode.getBody().setFlag(lc, Block.IS_GLOBAL_SCOPE)); 956 } 957 958 /** 959 * If the symbol isn't already a scope symbol, but it needs to be (see {@link #symbolNeedsToBeScope(Symbol)}, it is 960 * promoted to a scope symbol and its block marked as needing a scope. 961 * @param symbol the symbol that might be scoped 962 */ 963 private void maybeForceScope(final Symbol symbol) { 964 if (!symbol.isScope() && symbolNeedsToBeScope(symbol)) { 965 Symbol.setSymbolIsScope(lc, symbol); 966 } 967 } 968 969 private Symbol newInternal(final CompilerConstants cc, final int flags) { 970 return defineSymbol(lc.getCurrentBlock(), lc.getCurrentFunction().uniqueName(cc.symbolName()), null, IS_VAR | IS_INTERNAL | flags); //NASHORN-73 971 } 972 973 private Symbol newObjectInternal(final CompilerConstants cc) { 974 return newInternal(cc, HAS_OBJECT_VALUE); 975 } 976 977 private boolean start(final Node node) { 978 return start(node, true); 979 } 980 981 private boolean start(final Node node, final boolean printNode) { 982 if (debug) { 983 final StringBuilder sb = new StringBuilder(); 984 985 sb.append("[ENTER "). 986 append(name(node)). 987 append("] "). 988 append(printNode ? node.toString() : ""). 989 append(" in '"). 990 append(lc.getCurrentFunction().getName()). 991 append("'"); 992 log.info(sb); 993 log.indent(); 994 } 995 996 return true; 997 } 998 999 /** 1000 * Determines if the symbol has to be a scope symbol. In general terms, it has to be a scope symbol if it can only 1001 * be reached from the current block by traversing a function node, a split node, or a with node. 1002 * @param symbol the symbol checked for needing to be a scope symbol 1003 * @return true if the symbol has to be a scope symbol. 1004 */ 1005 private boolean symbolNeedsToBeScope(final Symbol symbol) { 1006 if (symbol.isThis() || symbol.isInternal()) { 1007 return false; 1008 } 1009 1010 final FunctionNode func = lc.getCurrentFunction(); 1011 if ( func.allVarsInScope() || (!symbol.isBlockScoped() && func.isProgram())) { 1012 return true; 1013 } 1014 1015 boolean previousWasBlock = false; 1016 for (final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) { 1017 final LexicalContextNode node = it.next(); 1018 if (node instanceof FunctionNode || isSplitArray(node)) { 1019 // We reached the function boundary or a splitting boundary without seeing a definition for the symbol. 1020 // It needs to be in scope. 1021 return true; 1022 } else if (node instanceof WithNode) { 1023 if (previousWasBlock) { 1024 // We reached a WithNode; the symbol must be scoped. Note that if the WithNode was not immediately 1025 // preceded by a block, this means we're currently processing its expression, not its body, 1026 // therefore it doesn't count. 1027 return true; 1028 } 1029 previousWasBlock = false; 1030 } else if (node instanceof Block) { 1031 if (((Block)node).getExistingSymbol(symbol.getName()) == symbol) { 1032 // We reached the block that defines the symbol without reaching either the function boundary, or a 1033 // WithNode. The symbol need not be scoped. 1034 return false; 1035 } 1036 previousWasBlock = true; 1037 } else { 1038 previousWasBlock = false; 1039 } 1040 } 1041 throw new AssertionError(); 1042 } 1043 1044 private static boolean isSplitArray(final LexicalContextNode expr) { 1045 if(!(expr instanceof ArrayLiteralNode)) { 1046 return false; 1047 } 1048 final List<ArrayUnit> units = ((ArrayLiteralNode)expr).getUnits(); 1049 return !(units == null || units.isEmpty()); 1050 } 1051 1052 private void throwUnprotectedSwitchError(final VarNode varNode) { 1053 // Block scoped declarations in switch statements without explicit blocks should be declared 1054 // in a common block that contains all the case clauses. We cannot support this without a 1055 // fundamental rewrite of how switch statements are handled (case nodes contain blocks and are 1056 // directly contained by switch node). As a temporary solution we throw a reference error here. 1057 final String msg = ECMAErrors.getMessage("syntax.error.unprotected.switch.declaration", varNode.isLet() ? "let" : "const"); 1058 throwParserException(msg, varNode); 1059 } 1060 1061 private void throwParserException(final String message, final Node origin) { 1062 if (origin == null) { 1063 throw new ParserException(message); 1064 } 1065 final Source source = compiler.getSource(); 1066 final long token = origin.getToken(); 1067 final int line = source.getLine(origin.getStart()); 1068 final int column = source.getColumn(origin.getStart()); 1069 final String formatted = ErrorManager.format(message, source, line, column, token); 1070 throw new ParserException(JSErrorType.SYNTAX_ERROR, formatted, source, line, column, token); 1071 } 1072} 1073