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