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