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