AssignSymbols.java revision 1336:efb5f54092ed
1139749Simp/* 276479Swpaul * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 376479Swpaul * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 476479Swpaul * 576479Swpaul * This code is free software; you can redistribute it and/or modify it 676479Swpaul * under the terms of the GNU General Public License version 2 only, as 776479Swpaul * published by the Free Software Foundation. Oracle designates this 876479Swpaul * particular file as subject to the "Classpath" exception as provided 976479Swpaul * by Oracle in the LICENSE file that accompanied this code. 1076479Swpaul * 1176479Swpaul * This code is distributed in the hope that it will be useful, but WITHOUT 1276479Swpaul * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1376479Swpaul * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1476479Swpaul * version 2 for more details (a copy is included in the LICENSE file that 1576479Swpaul * accompanied this code). 1676479Swpaul * 1776479Swpaul * You should have received a copy of the GNU General Public License version 1876479Swpaul * 2 along with this work; if not, write to the Free Software Foundation, 1976479Swpaul * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2076479Swpaul * 2176479Swpaul * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2276479Swpaul * or visit www.oracle.com if you need additional information or have any 2376479Swpaul * questions. 2476479Swpaul */ 2576479Swpaul 2676479Swpaulpackage jdk.nashorn.internal.codegen; 2776479Swpaul 2876479Swpaulimport static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS; 2976479Swpaulimport static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS_VAR; 3076479Swpaulimport static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE; 3176479Swpaulimport static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX; 3276479Swpaulimport static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX; 3376479Swpaulimport static jdk.nashorn.internal.codegen.CompilerConstants.RETURN; 34119418Sobrienimport static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; 35119418Sobrienimport static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX; 36119418Sobrienimport static jdk.nashorn.internal.codegen.CompilerConstants.THIS; 3776479Swpaulimport static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS; 3876479Swpaulimport static jdk.nashorn.internal.ir.Symbol.HAS_OBJECT_VALUE; 3976479Swpaulimport static jdk.nashorn.internal.ir.Symbol.IS_CONST; 4076479Swpaulimport static jdk.nashorn.internal.ir.Symbol.IS_FUNCTION_SELF; 4176479Swpaulimport static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL; 4276479Swpaulimport static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL; 4376479Swpaulimport static jdk.nashorn.internal.ir.Symbol.IS_LET; 4476479Swpaulimport static jdk.nashorn.internal.ir.Symbol.IS_PARAM; 4576479Swpaulimport static jdk.nashorn.internal.ir.Symbol.IS_PROGRAM_LEVEL; 4676479Swpaulimport static jdk.nashorn.internal.ir.Symbol.IS_SCOPE; 4776479Swpaulimport static jdk.nashorn.internal.ir.Symbol.IS_THIS; 4876479Swpaulimport static jdk.nashorn.internal.ir.Symbol.IS_VAR; 4976479Swpaulimport static jdk.nashorn.internal.ir.Symbol.KINDMASK; 5076479Swpaul 5176479Swpaulimport java.util.ArrayDeque; 5276479Swpaulimport java.util.ArrayList; 5376479Swpaulimport java.util.Deque; 5476479Swpaulimport java.util.HashMap; 5576479Swpaulimport java.util.HashSet; 5676479Swpaulimport java.util.Iterator; 5776479Swpaulimport java.util.List; 5876479Swpaulimport java.util.ListIterator; 5976479Swpaulimport java.util.Map; 6076479Swpaulimport java.util.Set; 6176479Swpaulimport jdk.nashorn.internal.ir.AccessNode; 6276479Swpaulimport jdk.nashorn.internal.ir.BinaryNode; 6376479Swpaulimport jdk.nashorn.internal.ir.Block; 6476479Swpaulimport jdk.nashorn.internal.ir.CatchNode; 6576479Swpaulimport jdk.nashorn.internal.ir.Expression; 6676479Swpaulimport jdk.nashorn.internal.ir.ForNode; 6776479Swpaulimport jdk.nashorn.internal.ir.FunctionNode; 6876479Swpaulimport jdk.nashorn.internal.ir.FunctionNode.CompilationState; 6976479Swpaulimport jdk.nashorn.internal.ir.IdentNode; 7076479Swpaulimport jdk.nashorn.internal.ir.IndexNode; 7176479Swpaulimport jdk.nashorn.internal.ir.LexicalContext; 7276479Swpaulimport jdk.nashorn.internal.ir.LexicalContextNode; 7378323Swpaulimport jdk.nashorn.internal.ir.LiteralNode; 7478323Swpaulimport jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; 7578323Swpaulimport jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; 7678323Swpaulimport jdk.nashorn.internal.ir.Node; 7778323Swpaulimport jdk.nashorn.internal.ir.RuntimeNode; 7878323Swpaulimport jdk.nashorn.internal.ir.RuntimeNode.Request; 7978323Swpaulimport jdk.nashorn.internal.ir.Statement; 8078323Swpaulimport jdk.nashorn.internal.ir.SwitchNode; 8178323Swpaulimport jdk.nashorn.internal.ir.Symbol; 8278323Swpaulimport jdk.nashorn.internal.ir.TryNode; 8378323Swpaulimport jdk.nashorn.internal.ir.UnaryNode; 8478323Swpaulimport jdk.nashorn.internal.ir.VarNode; 8578323Swpaulimport jdk.nashorn.internal.ir.WithNode; 8678323Swpaulimport jdk.nashorn.internal.ir.visitor.NodeVisitor; 8778323Swpaulimport jdk.nashorn.internal.parser.TokenType; 8878323Swpaulimport jdk.nashorn.internal.runtime.Context; 8976479Swpaulimport jdk.nashorn.internal.runtime.ECMAErrors; 9076479Swpaulimport jdk.nashorn.internal.runtime.ErrorManager; 91150968Sglebiusimport jdk.nashorn.internal.runtime.JSErrorType; 92150968Sglebiusimport jdk.nashorn.internal.runtime.ParserException; 93150968Sglebiusimport jdk.nashorn.internal.runtime.Source; 94150968Sglebiusimport jdk.nashorn.internal.runtime.logging.DebugLogger; 9576479Swpaulimport jdk.nashorn.internal.runtime.logging.Loggable; 9676479Swpaulimport jdk.nashorn.internal.runtime.logging.Logger; 97192506Syongari 98192506Syongari/** 99192506Syongari * This visitor assigns symbols to identifiers denoting variables. It does few more minor calculations that are only 100192506Syongari * possible after symbols have been assigned; such is the transformation of "delete" and "typeof" operators into runtime 101192506Syongari * nodes and counting of number of properties assigned to "this" in constructor functions. This visitor is also notable 10276479Swpaul * for what it doesn't do, most significantly it does no type calculations as in JavaScript variables can change types 103129879Sphk * during runtime and as such symbols don't have types. Calculation of expression types is performed by a separate 104192506Syongari * visitor. 105192506Syongari */ 10676479Swpaul@Logger(name="symbols") 107192506Syongarifinal class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggable { 108192506Syongari private final DebugLogger log; 10976479Swpaul private final boolean debug; 110192506Syongari 11176479Swpaul private static boolean isParamOrVar(final IdentNode identNode) { 11276479Swpaul final Symbol symbol = identNode.getSymbol(); 11376479Swpaul return symbol.isParam() || symbol.isVar(); 11476479Swpaul } 11576479Swpaul 11676479Swpaul private static String name(final Node node) { 11776479Swpaul final String cn = node.getClass().getName(); 11876479Swpaul final int lastDot = cn.lastIndexOf('.'); 11976479Swpaul if (lastDot == -1) { 12076479Swpaul return cn; 12176479Swpaul } 122119285Simp return cn.substring(lastDot + 1); 123119285Simp } 12476479Swpaul 125192506Syongari /** 12676479Swpaul * Checks if various symbols that were provisionally marked as needing a slot ended up unused, and marks them as not 12776522Swpaul * needing a slot after all. 12876479Swpaul * @param functionNode the function node 129192506Syongari * @return the passed in node, for easy chaining 130192506Syongari */ 131192506Syongari private static FunctionNode removeUnusedSlots(final FunctionNode functionNode) { 132113506Smdodd if (!functionNode.needsCallee()) { 133113506Smdodd functionNode.compilerConstant(CALLEE).setNeedsSlot(false); 13476479Swpaul } 13576479Swpaul if (!(functionNode.hasScopeBlock() || functionNode.needsParentScope())) { 13676479Swpaul functionNode.compilerConstant(SCOPE).setNeedsSlot(false); 13776479Swpaul } 13876479Swpaul // Named function expressions that end up not referencing themselves won't need a local slot for the self symbol. 13976479Swpaul if(functionNode.isNamedFunctionExpression() && !functionNode.usesSelfSymbol()) { 14076479Swpaul final Symbol selfSymbol = functionNode.getBody().getExistingSymbol(functionNode.getIdent().getName()); 14176479Swpaul if(selfSymbol != null && selfSymbol.isFunctionSelf()) { 14276479Swpaul selfSymbol.setNeedsSlot(false); 14376479Swpaul selfSymbol.clearFlag(Symbol.IS_VAR); 14476479Swpaul } 14576479Swpaul } 14676479Swpaul return functionNode; 14799497Salfred } 14899497Salfred 14999497Salfred private final Deque<Set<String>> thisProperties = new ArrayDeque<>(); 150192506Syongari private final Map<String, Symbol> globalSymbols = new HashMap<>(); //reuse the same global symbol 151192506Syongari private final Compiler compiler; 152192506Syongari 15376479Swpaul public AssignSymbols(final Compiler compiler) { 154192506Syongari super(new LexicalContext()); 155192506Syongari this.compiler = compiler; 156192506Syongari this.log = initLogger(compiler.getContext()); 157192506Syongari this.debug = log.isEnabled(); 158192506Syongari } 159135250Swpaul 160193105Sattilio @Override 16199497Salfred public DebugLogger getLogger() { 16299497Salfred return log; 16399497Salfred } 164192506Syongari 16599497Salfred @Override 166135250Swpaul public DebugLogger initLogger(final Context context) { 16799497Salfred return context.getLogger(this.getClass()); 16899497Salfred } 169135250Swpaul 170192506Syongari /** 17199497Salfred * Define symbols for all variable declarations at the top of the function scope. This way we can get around 172192506Syongari * problems like 173192506Syongari * 174192506Syongari * while (true) { 175192506Syongari * break; 17676479Swpaul * if (true) { 17799497Salfred * var s; 17899497Salfred * } 17999497Salfred * } 180192294Syongari * 181192506Syongari * to an arbitrary nesting depth. 18276479Swpaul * 18399497Salfred * see NASHORN-73 184192294Syongari * 18599497Salfred * @param functionNode the FunctionNode we are entering 18699497Salfred * @param body the body of the FunctionNode we are entering 18776479Swpaul */ 18899497Salfred private void acceptDeclarations(final FunctionNode functionNode, final Block body) { 18999497Salfred // This visitor will assign symbol to all declared variables. 19099497Salfred body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 19176479Swpaul @Override 192192506Syongari protected boolean enterDefault(final Node node) { 19399497Salfred // Don't bother visiting expressions; var is a statement, it can't be inside an expression. 194192506Syongari // This will also prevent visiting nested functions (as FunctionNode is an expression). 195192506Syongari return !(node instanceof Expression); 196192506Syongari } 19799497Salfred 19899497Salfred @Override 199192506Syongari public Node leaveVarNode(final VarNode varNode) { 200192506Syongari final IdentNode ident = varNode.getName(); 201192506Syongari final boolean blockScoped = varNode.isBlockScoped(); 20276479Swpaul if (blockScoped && lc.inUnprotectedSwitchContext()) { 20376479Swpaul throwUnprotectedSwitchError(varNode); 20476479Swpaul } 20576479Swpaul final Block block = blockScoped ? lc.getCurrentBlock() : body; 20676479Swpaul final Symbol symbol = defineSymbol(block, ident.getName(), ident, varNode.getSymbolFlags()); 20776479Swpaul if (varNode.isFunctionDeclaration()) { 20876479Swpaul symbol.setIsFunctionDeclaration(); 209192506Syongari } 210192506Syongari return varNode.setName(ident.setSymbol(symbol)); 21176479Swpaul } 21276479Swpaul }); 21376479Swpaul } 21476479Swpaul 21576479Swpaul private IdentNode compilerConstantIdentifier(final CompilerConstants cc) { 21676479Swpaul return createImplicitIdentifier(cc.symbolName()).setSymbol(lc.getCurrentFunction().compilerConstant(cc)); 21776479Swpaul } 21876479Swpaul 21976479Swpaul /** 22076479Swpaul * Creates an ident node for an implicit identifier within the function (one not declared in the script source 221192506Syongari * code). These identifiers are defined with function's token and finish. 22276479Swpaul * @param name the name of the identifier 22376479Swpaul * @return an ident node representing the implicit identifier. 22476479Swpaul */ 22576479Swpaul private IdentNode createImplicitIdentifier(final String name) { 22676479Swpaul final FunctionNode fn = lc.getCurrentFunction(); 22776479Swpaul return new IdentNode(fn.getToken(), fn.getFinish(), name); 22876479Swpaul } 22976479Swpaul 23076479Swpaul private Symbol createSymbol(final String name, final int flags) { 23176479Swpaul if ((flags & Symbol.KINDMASK) == IS_GLOBAL) { 232113506Smdodd //reuse global symbols so they can be hashed 23376479Swpaul Symbol global = globalSymbols.get(name); 23476479Swpaul if (global == null) { 23576479Swpaul global = new Symbol(name, flags); 23676479Swpaul globalSymbols.put(name, global); 23776479Swpaul } 23876479Swpaul return global; 23976479Swpaul } 24076479Swpaul return new Symbol(name, flags); 24176479Swpaul } 24276479Swpaul 24376479Swpaul /** 244106696Salfred * Creates a synthetic initializer for a variable (a var statement that doesn't occur in the source code). Typically 24576479Swpaul * used to create assignmnent of {@code :callee} to the function name symbol in self-referential function 24676479Swpaul * expressions as well as for assignment of {@code :arguments} to {@code arguments}. 247106696Salfred * 24876479Swpaul * @param name the ident node identifying the variable to initialize 24999497Salfred * @param initConstant the compiler constant it is initialized to 250192288Syongari * @param fn the function node the assignment is for 25176479Swpaul * @return a var node with the appropriate assignment 252192297Syongari */ 25376479Swpaul private VarNode createSyntheticInitializer(final IdentNode name, final CompilerConstants initConstant, final FunctionNode fn) { 25476479Swpaul final IdentNode init = compilerConstantIdentifier(initConstant); 25576479Swpaul assert init.getSymbol() != null && init.getSymbol().isBytecodeLocal(); 25676479Swpaul 25776479Swpaul final VarNode synthVar = new VarNode(fn.getLineNumber(), fn.getToken(), fn.getFinish(), name, init); 25899497Salfred 259192288Syongari final Symbol nameSymbol = fn.getBody().getExistingSymbol(name.getName()); 26076479Swpaul assert nameSymbol != null; 261192297Syongari 26276479Swpaul return (VarNode)synthVar.setName(name.setSymbol(nameSymbol)).accept(this); 26376479Swpaul } 26476479Swpaul 26576479Swpaul private FunctionNode createSyntheticInitializers(final FunctionNode functionNode) { 26676479Swpaul final List<VarNode> syntheticInitializers = new ArrayList<>(2); 26776479Swpaul 26876479Swpaul // Must visit the new var nodes in the context of the body. We could also just set the new statements into the 26976479Swpaul // block and then revisit the entire block, but that seems to be too much double work. 27076479Swpaul final Block body = functionNode.getBody(); 27176479Swpaul lc.push(body); 27276479Swpaul try { 27376479Swpaul if (functionNode.usesSelfSymbol()) { 27476479Swpaul // "var fn = :callee" 27576479Swpaul syntheticInitializers.add(createSyntheticInitializer(functionNode.getIdent(), CALLEE, functionNode)); 27676479Swpaul } 27776479Swpaul 27876479Swpaul if (functionNode.needsArguments()) { 27976479Swpaul // "var arguments = :arguments" 28076479Swpaul syntheticInitializers.add(createSyntheticInitializer(createImplicitIdentifier(ARGUMENTS_VAR.symbolName()), 28176479Swpaul ARGUMENTS, functionNode)); 28276479Swpaul } 28376479Swpaul 28476479Swpaul if (syntheticInitializers.isEmpty()) { 28599497Salfred return functionNode; 286192288Syongari } 28776479Swpaul 288192297Syongari for(final ListIterator<VarNode> it = syntheticInitializers.listIterator(); it.hasNext();) { 28976479Swpaul it.set((VarNode)it.next().accept(this)); 29076479Swpaul } 29176479Swpaul } finally { 29276479Swpaul lc.pop(body); 29376479Swpaul } 29476479Swpaul 29576479Swpaul final List<Statement> stmts = body.getStatements(); 29676479Swpaul final List<Statement> newStatements = new ArrayList<>(stmts.size() + syntheticInitializers.size()); 29776479Swpaul newStatements.addAll(syntheticInitializers); 29876479Swpaul newStatements.addAll(stmts); 29976479Swpaul return functionNode.setBody(lc, body.setStatements(lc, newStatements)); 30076479Swpaul } 30176479Swpaul 30276479Swpaul /** 30376479Swpaul * Defines a new symbol in the given block. 30476479Swpaul * 30576479Swpaul * @param block the block in which to define the symbol 30676479Swpaul * @param name name of symbol. 30776479Swpaul * @param origin origin node 30876479Swpaul * @param symbolFlags Symbol flags. 30976479Swpaul * 31076479Swpaul * @return Symbol for given name or null for redefinition. 31176479Swpaul */ 31299497Salfred private Symbol defineSymbol(final Block block, final String name, final Node origin, final int symbolFlags) { 313192294Syongari int flags = symbolFlags; 31476479Swpaul final boolean isBlockScope = (flags & IS_LET) != 0 || (flags & IS_CONST) != 0; 315192297Syongari final boolean isGlobal = (flags & KINDMASK) == IS_GLOBAL; 316192297Syongari 31776479Swpaul Symbol symbol; 31876479Swpaul final FunctionNode function; 31976479Swpaul if (isBlockScope) { 32076479Swpaul // block scoped variables always live in current block, no need to look for existing symbols in parent blocks. 32176479Swpaul symbol = block.getExistingSymbol(name); 32276479Swpaul function = lc.getCurrentFunction(); 32376479Swpaul } else { 32476479Swpaul symbol = findSymbol(block, name); 32576479Swpaul function = lc.getFunction(block); 32676479Swpaul } 32776479Swpaul 32876479Swpaul // Global variables are implicitly always scope variables too. 32976479Swpaul if (isGlobal) { 33076479Swpaul flags |= IS_SCOPE; 33176479Swpaul } 33276479Swpaul 33376479Swpaul if (lc.getCurrentFunction().isProgram()) { 33476479Swpaul flags |= IS_PROGRAM_LEVEL; 33576479Swpaul } 33676479Swpaul 33776479Swpaul final boolean isParam = (flags & KINDMASK) == IS_PARAM; 33876479Swpaul final boolean isVar = (flags & KINDMASK) == IS_VAR; 33976479Swpaul 34076479Swpaul if (symbol != null) { 34176479Swpaul // Symbol was already defined. Check if it needs to be redefined. 34276479Swpaul if (isParam) { 34376479Swpaul if (!isLocal(function, symbol)) { 34476479Swpaul // Not defined in this function. Create a new definition. 34576479Swpaul symbol = null; 34676479Swpaul } else if (symbol.isParam()) { 34776479Swpaul // Duplicate parameter. Null return will force an error. 34876479Swpaul throw new AssertionError("duplicate parameter"); 34976479Swpaul } 35076479Swpaul } else if (isVar) { 35176479Swpaul if (isBlockScope) { 35276479Swpaul // Check redeclaration in same block 35376479Swpaul if (symbol.hasBeenDeclared()) { 35476479Swpaul throwParserException(ECMAErrors.getMessage("syntax.error.redeclare.variable", name), origin); 35599497Salfred } else { 356192506Syongari symbol.setHasBeenDeclared(); 35776479Swpaul // Set scope flag on top-level block scoped symbols 358192297Syongari if (function.isProgram() && function.getBody() == block) { 359192297Syongari symbol.setIsScope(); 36076479Swpaul } 36176479Swpaul } 36276479Swpaul } else if ((flags & IS_INTERNAL) != 0) { 363192294Syongari // Always create a new definition. 364192506Syongari symbol = null; 36576479Swpaul } else { 36676479Swpaul // Found LET or CONST in parent scope of same function - s SyntaxError 36776479Swpaul if (symbol.isBlockScoped() && isLocal(lc.getCurrentFunction(), symbol)) { 36876479Swpaul throwParserException(ECMAErrors.getMessage("syntax.error.redeclare.variable", name), origin); 36976479Swpaul } 37076479Swpaul // Not defined in this function. Create a new definition. 37199497Salfred if (!isLocal(function, symbol) || symbol.less(IS_VAR)) { 372192288Syongari symbol = null; 37376479Swpaul } 374192297Syongari } 37576479Swpaul } 37676479Swpaul } 37776479Swpaul 37876479Swpaul if (symbol == null) { 37976479Swpaul // If not found, then create a new one. 38076479Swpaul final Block symbolBlock; 38176479Swpaul 38276479Swpaul // Determine where to create it. 38376479Swpaul if (isVar && ((flags & IS_INTERNAL) != 0 || isBlockScope)) { 38476479Swpaul symbolBlock = block; //internal vars are always defined in the block closest to them 38576479Swpaul } else if (isGlobal) { 38676479Swpaul symbolBlock = lc.getOutermostFunction().getBody(); 38776479Swpaul } else { 38876479Swpaul symbolBlock = lc.getFunctionBody(function); 38999497Salfred } 390192294Syongari 39176479Swpaul // Create and add to appropriate block. 392192297Syongari symbol = createSymbol(name, flags); 39376479Swpaul symbolBlock.putSymbol(lc, symbol); 39476479Swpaul 39576479Swpaul if ((flags & IS_SCOPE) == 0) { 39676479Swpaul // Initial assumption; symbol can lose its slot later 397192298Syongari symbol.setNeedsSlot(true); 39876479Swpaul } 399192298Syongari } else if (symbol.less(flags)) { 40076479Swpaul symbol.setFlags(flags); 401192298Syongari } 40276479Swpaul 40376479Swpaul return symbol; 40476479Swpaul } 40576479Swpaul 40676479Swpaul private <T extends Node> T end(final T node) { 40776479Swpaul return end(node, true); 40876479Swpaul } 40976479Swpaul 41076479Swpaul private <T extends Node> T end(final T node, final boolean printNode) { 41176479Swpaul if (debug) { 41299497Salfred final StringBuilder sb = new StringBuilder(); 413192288Syongari 41476479Swpaul sb.append("[LEAVE "). 415192297Syongari append(name(node)). 41676479Swpaul append("] "). 41776479Swpaul append(printNode ? node.toString() : ""). 41876479Swpaul append(" in '"). 41976479Swpaul append(lc.getCurrentFunction().getName()). 42076479Swpaul append('\''); 42176479Swpaul 42276479Swpaul if (node instanceof IdentNode) { 42376479Swpaul final Symbol symbol = ((IdentNode)node).getSymbol(); 424192290Syongari if (symbol == null) { 42576479Swpaul sb.append(" <NO SYMBOL>"); 42676479Swpaul } else { 42776479Swpaul sb.append(" <symbol=").append(symbol).append('>'); 42876479Swpaul } 42976479Swpaul } 43076479Swpaul 43176479Swpaul log.unindent(); 43276479Swpaul log.info(sb); 43376479Swpaul } 43476479Swpaul 43576479Swpaul return node; 43676479Swpaul } 43776479Swpaul 43876479Swpaul @Override 43976479Swpaul public boolean enterBlock(final Block block) { 44076479Swpaul start(block); 44176479Swpaul 44276479Swpaul if (lc.isFunctionBody()) { 44376479Swpaul block.clearSymbols(); 44476479Swpaul final FunctionNode fn = lc.getCurrentFunction(); 44576479Swpaul if (isUnparsedFunction(fn)) { 44676479Swpaul // It's a skipped nested function. Just mark the symbols being used by it as being in use. 44776479Swpaul for(final String name: compiler.getScriptFunctionData(fn.getId()).getExternalSymbolNames()) { 44876479Swpaul nameIsUsed(name, null); 44976479Swpaul } 45076479Swpaul // Don't bother descending into it, it must be empty anyway. 45176479Swpaul assert block.getStatements().isEmpty(); 45276479Swpaul return false; 453109058Smbr } 45476479Swpaul 45576479Swpaul enterFunctionBody(); 45676479Swpaul } 45776479Swpaul 45876479Swpaul return true; 45976479Swpaul } 46076479Swpaul 46176479Swpaul private boolean isUnparsedFunction(final FunctionNode fn) { 462192292Syongari return compiler.isOnDemandCompilation() && fn != lc.getOutermostFunction(); 46376479Swpaul } 46476479Swpaul 46576479Swpaul @Override 46676479Swpaul public boolean enterCatchNode(final CatchNode catchNode) { 46776479Swpaul final IdentNode exception = catchNode.getException(); 46876479Swpaul final Block block = lc.getCurrentBlock(); 46976479Swpaul 47076479Swpaul start(catchNode); 47176479Swpaul 47276479Swpaul // define block-local exception variable 47376479Swpaul final String exname = exception.getName(); 47476479Swpaul // If the name of the exception starts with ":e", this is a synthetic catch block, likely a catch-all. Its 47576479Swpaul // symbol is naturally internal, and should be treated as such. 47676479Swpaul final boolean isInternal = exname.startsWith(EXCEPTION_PREFIX.symbolName()); 47776479Swpaul // IS_LET flag is required to make sure symbol is not visible outside catch block. However, we need to 47876479Swpaul // clear the IS_LET flag after creation to allow redefinition of symbol inside the catch block. 47976479Swpaul final Symbol symbol = defineSymbol(block, exname, catchNode, IS_VAR | IS_LET | (isInternal ? IS_INTERNAL : 0) | HAS_OBJECT_VALUE); 48076479Swpaul symbol.clearFlag(IS_LET); 48176479Swpaul 48276479Swpaul return true; 48376479Swpaul } 48476479Swpaul 48576479Swpaul private void enterFunctionBody() { 48676479Swpaul final FunctionNode functionNode = lc.getCurrentFunction(); 48776479Swpaul final Block body = lc.getCurrentBlock(); 48876479Swpaul 48976479Swpaul initFunctionWideVariables(functionNode, body); 49076479Swpaul acceptDeclarations(functionNode, body); 491192292Syongari defineFunctionSelfSymbol(functionNode, body); 492192292Syongari } 49376479Swpaul 49476479Swpaul private void defineFunctionSelfSymbol(final FunctionNode functionNode, final Block body) { 49576479Swpaul // Function self-symbol is only declared as a local variable for named function expressions. Declared functions 49676479Swpaul // don't need it as they are local variables in their declaring scope. 49776479Swpaul if (!functionNode.isNamedFunctionExpression()) { 49899497Salfred return; 499192288Syongari } 50076479Swpaul 50176479Swpaul final String name = functionNode.getIdent().getName(); 50276479Swpaul assert name != null; // As it's a named function expression. 50376479Swpaul 50476479Swpaul if (body.getExistingSymbol(name) != null) { 50576479Swpaul // Body already has a declaration for the name. It's either a parameter "function x(x)" or a 50676479Swpaul // top-level variable "function x() { ... var x; ... }". 50776479Swpaul return; 50876479Swpaul } 509192290Syongari 51076479Swpaul defineSymbol(body, name, functionNode, IS_VAR | IS_FUNCTION_SELF | HAS_OBJECT_VALUE); 51176479Swpaul if(functionNode.allVarsInScope()) { // basically, has deep eval 51276479Swpaul // We must conservatively presume that eval'd code can dynamically use the function symbol. 51376479Swpaul lc.setFlag(functionNode, FunctionNode.USES_SELF_SYMBOL); 51476479Swpaul } 51576479Swpaul } 51676479Swpaul 51776479Swpaul @Override 51876479Swpaul public boolean enterFunctionNode(final FunctionNode functionNode) { 51976479Swpaul start(functionNode, false); 52076479Swpaul 52176479Swpaul thisProperties.push(new HashSet<String>()); 52276479Swpaul 52376479Swpaul // Every function has a body, even the ones skipped on reparse (they have an empty one). We're 52476479Swpaul // asserting this as even for those, enterBlock() must be invoked to correctly process symbols that 52576479Swpaul // are used in them. 52676479Swpaul assert functionNode.getBody() != null; 52776479Swpaul 52876479Swpaul return true; 52976479Swpaul } 53076479Swpaul 53176479Swpaul @Override 53276479Swpaul public boolean enterVarNode(final VarNode varNode) { 53376479Swpaul start(varNode); 53476479Swpaul // Normally, a symbol assigned in a var statement is not live for its RHS. Since we also represent function 535192292Syongari // declarations as VarNodes, they are exception to the rule, as they need to have the symbol visible to the 53676479Swpaul // body of the declared function for self-reference. 53776479Swpaul if (varNode.isFunctionDeclaration()) { 53899497Salfred defineVarIdent(varNode); 539192288Syongari } 54076479Swpaul return true; 541192297Syongari } 542192297Syongari 543192506Syongari @Override 54476479Swpaul public Node leaveVarNode(final VarNode varNode) { 54576479Swpaul if (!varNode.isFunctionDeclaration()) { 546192506Syongari defineVarIdent(varNode); 547192506Syongari } 548192506Syongari return super.leaveVarNode(varNode); 549192506Syongari } 550192506Syongari 551192506Syongari private void defineVarIdent(final VarNode varNode) { 552192506Syongari final IdentNode ident = varNode.getName(); 553192506Syongari final int flags; 554192506Syongari if (!varNode.isBlockScoped() && lc.getCurrentFunction().isProgram()) { 555192506Syongari flags = IS_SCOPE; 556192506Syongari } else { 557192506Syongari flags = 0; 558192506Syongari } 559192506Syongari defineSymbol(lc.getCurrentBlock(), ident.getName(), ident, varNode.getSymbolFlags() | flags); 560192506Syongari } 561192506Syongari 562192506Syongari private Symbol exceptionSymbol() { 563192506Syongari return newObjectInternal(EXCEPTION_PREFIX); 564192506Syongari } 565192506Syongari 566192506Syongari /** 567192506Syongari * This has to run before fix assignment types, store any type specializations for 568192506Syongari * parameters, then turn them into objects for the generic version of this method. 569192506Syongari * 570192506Syongari * @param functionNode functionNode 571192506Syongari */ 572192506Syongari private FunctionNode finalizeParameters(final FunctionNode functionNode) { 573192506Syongari final List<IdentNode> newParams = new ArrayList<>(); 574192506Syongari final boolean isVarArg = functionNode.isVarArg(); 575192506Syongari 576192506Syongari final Block body = functionNode.getBody(); 577192506Syongari for (final IdentNode param : functionNode.getParameters()) { 578192506Syongari final Symbol paramSymbol = body.getExistingSymbol(param.getName()); 579192506Syongari assert paramSymbol != null; 580192506Syongari assert paramSymbol.isParam() : paramSymbol + " " + paramSymbol.getFlags(); 581192506Syongari newParams.add(param.setSymbol(paramSymbol)); 582192506Syongari 583192506Syongari // parameters should not be slots for a function that uses variable arity signature 584192506Syongari if (isVarArg) { 58576479Swpaul paramSymbol.setNeedsSlot(false); 58676479Swpaul } 58776479Swpaul } 58876479Swpaul 58976479Swpaul return functionNode.setParameters(lc, newParams); 59076479Swpaul } 59176479Swpaul 592192292Syongari /** 59376479Swpaul * Search for symbol in the lexical context starting from the given block. 59476479Swpaul * @param name Symbol name. 59599497Salfred * @return Found symbol or null if not found. 596192288Syongari */ 59776479Swpaul private Symbol findSymbol(final Block block, final String name) { 598192297Syongari for (final Iterator<Block> blocks = lc.getBlocks(block); blocks.hasNext();) { 599192297Syongari final Symbol symbol = blocks.next().getExistingSymbol(name); 60076479Swpaul if (symbol != null) { 60176479Swpaul return symbol; 602192506Syongari } 603192506Syongari } 604192506Syongari return null; 605192506Syongari } 606192506Syongari 607192506Syongari /** 608192506Syongari * Marks the current function as one using any global symbol. The function and all its parent functions will all be 609192506Syongari * marked as needing parent scope. 610192506Syongari * @see FunctionNode#needsParentScope() 611192506Syongari */ 612192506Syongari private void functionUsesGlobalSymbol() { 613192506Syongari for (final Iterator<FunctionNode> fns = lc.getFunctions(); fns.hasNext();) { 614192506Syongari lc.setFlag(fns.next(), FunctionNode.USES_ANCESTOR_SCOPE); 615192506Syongari } 616192506Syongari } 617192506Syongari 618192506Syongari /** 619192506Syongari * Marks the current function as one using a scoped symbol. The block defining the symbol will be marked as needing 620192506Syongari * its own scope to hold the variable. If the symbol is defined outside of the current function, it and all 621192506Syongari * functions up to (but not including) the function containing the defining block will be marked as needing parent 622192506Syongari * function scope. 623192506Syongari * @see FunctionNode#needsParentScope() 624192506Syongari */ 625192506Syongari private void functionUsesScopeSymbol(final Symbol symbol) { 626192506Syongari final String name = symbol.getName(); 627192506Syongari for (final Iterator<LexicalContextNode> contextNodeIter = lc.getAllNodes(); contextNodeIter.hasNext(); ) { 628192506Syongari final LexicalContextNode node = contextNodeIter.next(); 629192506Syongari if (node instanceof Block) { 630192506Syongari final Block block = (Block)node; 631192506Syongari if (block.getExistingSymbol(name) != null) { 632192506Syongari assert lc.contains(block); 633192506Syongari lc.setBlockNeedsScope(block); 634192506Syongari break; 63576479Swpaul } 63676479Swpaul } else if (node instanceof FunctionNode) { 63776479Swpaul lc.setFlag(node, FunctionNode.USES_ANCESTOR_SCOPE); 63876479Swpaul } 63976479Swpaul } 64076479Swpaul } 64176479Swpaul 64276479Swpaul /** 643192292Syongari * Declares that the current function is using the symbol. 64476479Swpaul * @param symbol the symbol used by the current function. 64576479Swpaul */ 646192506Syongari private void functionUsesSymbol(final Symbol symbol) { 647192506Syongari assert symbol != null; 648192506Syongari if (symbol.isScope()) { 64999497Salfred if (symbol.isGlobal()) { 650192288Syongari functionUsesGlobalSymbol(); 65176479Swpaul } else { 652192297Syongari functionUsesScopeSymbol(symbol); 653192297Syongari } 654192506Syongari } else { 655192506Syongari assert !symbol.isGlobal(); // Every global is also scope 656192506Syongari } 657192506Syongari } 65876479Swpaul 65976479Swpaul private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags) { 660192506Syongari defineSymbol(block, cc.symbolName(), null, flags).setNeedsSlot(true); 66176479Swpaul } 662192506Syongari 663192506Syongari private void initFunctionWideVariables(final FunctionNode functionNode, final Block body) { 664192506Syongari initCompileConstant(CALLEE, body, IS_PARAM | IS_INTERNAL | HAS_OBJECT_VALUE); 665192506Syongari initCompileConstant(THIS, body, IS_PARAM | IS_THIS | HAS_OBJECT_VALUE); 666192506Syongari 667192506Syongari if (functionNode.isVarArg()) { 668192506Syongari initCompileConstant(VARARGS, body, IS_PARAM | IS_INTERNAL | HAS_OBJECT_VALUE); 669192506Syongari if (functionNode.needsArguments()) { 670192506Syongari initCompileConstant(ARGUMENTS, body, IS_VAR | IS_INTERNAL | HAS_OBJECT_VALUE); 671192506Syongari defineSymbol(body, ARGUMENTS_VAR.symbolName(), null, IS_VAR | HAS_OBJECT_VALUE); 672192506Syongari } 673192506Syongari } 674192506Syongari 675192506Syongari initParameters(functionNode, body); 676192506Syongari initCompileConstant(SCOPE, body, IS_VAR | IS_INTERNAL | HAS_OBJECT_VALUE); 677192506Syongari initCompileConstant(RETURN, body, IS_VAR | IS_INTERNAL); 678192506Syongari } 679192506Syongari 680192506Syongari /** 681192506Syongari * Initialize parameters for function node. 682101540Sambrisko * @param functionNode the function node 683192506Syongari */ 68476479Swpaul private void initParameters(final FunctionNode functionNode, final Block body) { 685192506Syongari final boolean isVarArg = functionNode.isVarArg(); 686192506Syongari final boolean scopeParams = functionNode.allVarsInScope() || isVarArg; 687192506Syongari for (final IdentNode param : functionNode.getParameters()) { 688192506Syongari final Symbol symbol = defineSymbol(body, param.getName(), param, IS_PARAM); 689192506Syongari if(scopeParams) { 690192506Syongari // NOTE: this "set is scope" is a poor substitute for clear expression of where the symbol is stored. 691192506Syongari // It will force creation of scopes where they would otherwise not necessarily be needed (functions 692192506Syongari // using arguments object and other variable arity functions). Tracked by JDK-8038942. 693192506Syongari symbol.setIsScope(); 694192506Syongari assert symbol.hasSlot(); 695192506Syongari if(isVarArg) { 696192506Syongari symbol.setNeedsSlot(false); 697192506Syongari } 698192506Syongari } 699192506Syongari } 700192506Syongari } 701192506Syongari 702192506Syongari /** 703192506Syongari * Is the symbol local to (that is, defined in) the specified function? 704192506Syongari * @param function the function 705192506Syongari * @param symbol the symbol 706192506Syongari * @return true if the symbol is defined in the specified function 707192506Syongari */ 708192506Syongari private boolean isLocal(final FunctionNode function, final Symbol symbol) { 709192506Syongari final FunctionNode definingFn = lc.getDefiningFunction(symbol); 710192506Syongari assert definingFn != null; 711192506Syongari return definingFn == function; 712192506Syongari } 713192506Syongari 714101540Sambrisko @Override 715192506Syongari public Node leaveBinaryNode(final BinaryNode binaryNode) { 716192506Syongari if (binaryNode.isTokenType(TokenType.ASSIGN)) { 717192506Syongari return leaveASSIGN(binaryNode); 718192506Syongari } 719192506Syongari return super.leaveBinaryNode(binaryNode); 720192506Syongari } 721192506Syongari 722101540Sambrisko private Node leaveASSIGN(final BinaryNode binaryNode) { 723101540Sambrisko // If we're assigning a property of the this object ("this.foo = ..."), record it. 724192506Syongari final Expression lhs = binaryNode.lhs(); 725101540Sambrisko if (lhs instanceof AccessNode) { 726192506Syongari final AccessNode accessNode = (AccessNode) lhs; 727101540Sambrisko final Expression base = accessNode.getBase(); 728101540Sambrisko if (base instanceof IdentNode) { 729192506Syongari final Symbol symbol = ((IdentNode)base).getSymbol(); 730192506Syongari if(symbol.isThis()) { 731192506Syongari thisProperties.peek().add(accessNode.getProperty()); 732192506Syongari } 733192506Syongari } 734192506Syongari } 735192506Syongari return binaryNode; 736192506Syongari } 737192506Syongari 738192506Syongari @Override 739192506Syongari public Node leaveUnaryNode(final UnaryNode unaryNode) { 740101540Sambrisko switch (unaryNode.tokenType()) { 741192506Syongari case DELETE: 742192506Syongari return leaveDELETE(unaryNode); 743192506Syongari case TYPEOF: 744192506Syongari return leaveTYPEOF(unaryNode); 745192506Syongari default: 746192506Syongari return super.leaveUnaryNode(unaryNode); 747192506Syongari } 748192506Syongari } 749192506Syongari 750192506Syongari @Override 751192506Syongari public Node leaveBlock(final Block block) { 752192506Syongari // It's not necessary to guard the marking of symbols as locals with this "if" condition for 753192506Syongari // correctness, it's just an optimization -- runtime type calculation is not used when the compilation 754192506Syongari // is not an on-demand optimistic compilation, so we can skip locals marking then. 755192506Syongari if (compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) { 756192506Syongari // OTOH, we must not declare symbols from nested functions to be locals. As we're doing on-demand 757192506Syongari // compilation, and we're skipping parsing the function bodies for nested functions, this 758192506Syongari // basically only means their parameters. It'd be enough to mistakenly declare to be a local a 759192506Syongari // symbol in the outer function named the same as one of the parameters, though. 760192506Syongari if (lc.getFunction(block) == lc.getOutermostFunction()) { 761192506Syongari for (final Symbol symbol: block.getSymbols()) { 762192506Syongari if (!symbol.isScope()) { 763192506Syongari assert symbol.isVar() || symbol.isParam(); 764192506Syongari compiler.declareLocalSymbol(symbol.getName()); 765192506Syongari } 766192506Syongari } 767192506Syongari } 768192506Syongari } 769192506Syongari return block; 770192506Syongari } 771192506Syongari 772192506Syongari private Node leaveDELETE(final UnaryNode unaryNode) { 773192506Syongari final FunctionNode currentFunctionNode = lc.getCurrentFunction(); 774192506Syongari final boolean strictMode = currentFunctionNode.isStrict(); 775192506Syongari final Expression rhs = unaryNode.getExpression(); 776192506Syongari final Expression strictFlagNode = (Expression)LiteralNode.newInstance(unaryNode, strictMode).accept(this); 777192506Syongari 778192506Syongari Request request = Request.DELETE; 779192506Syongari final List<Expression> args = new ArrayList<>(); 780192506Syongari 781192506Syongari if (rhs instanceof IdentNode) { 782192506Syongari final IdentNode ident = (IdentNode)rhs; 783192506Syongari // If this is a declared variable or a function parameter, delete always fails (except for globals). 784192506Syongari final String name = ident.getName(); 785192506Syongari final Symbol symbol = ident.getSymbol(); 786192506Syongari final boolean failDelete = strictMode || (!symbol.isScope() && (symbol.isParam() || (symbol.isVar() && !symbol.isProgramLevel()))); 787192506Syongari 788192506Syongari if (failDelete && symbol.isThis()) { 789192506Syongari return LiteralNode.newInstance(unaryNode, true).accept(this); 790192506Syongari } 791192506Syongari final Expression literalNode = (Expression)LiteralNode.newInstance(unaryNode, name).accept(this); 792192506Syongari 79379424Swpaul if (!failDelete) { 794192506Syongari args.add(compilerConstantIdentifier(SCOPE)); 795192506Syongari } 796192506Syongari args.add(literalNode); 797192506Syongari args.add(strictFlagNode); 798192506Syongari 79976479Swpaul if (failDelete) { 80076479Swpaul request = Request.FAIL_DELETE; 80199497Salfred } 802192506Syongari } else if (rhs instanceof AccessNode) { 80376479Swpaul final Expression base = ((AccessNode)rhs).getBase(); 804192297Syongari final String property = ((AccessNode)rhs).getProperty(); 805192297Syongari 806192506Syongari args.add(base); 807192297Syongari args.add((Expression)LiteralNode.newInstance(unaryNode, property).accept(this)); 80876479Swpaul args.add(strictFlagNode); 809135250Swpaul 810147256Sbrooks } else if (rhs instanceof IndexNode) { 81176479Swpaul final IndexNode indexNode = (IndexNode)rhs; 812192506Syongari final Expression base = indexNode.getBase(); 813192506Syongari final Expression index = indexNode.getIndex(); 814192506Syongari 815192506Syongari args.add(base); 816192506Syongari args.add(index); 817192506Syongari args.add(strictFlagNode); 818192506Syongari 819192506Syongari } else { 820192506Syongari return LiteralNode.newInstance(unaryNode, true).accept(this); 821192506Syongari } 822192506Syongari return new RuntimeNode(unaryNode, request, args).accept(this); 823192506Syongari } 824192506Syongari 825192506Syongari @Override 826192506Syongari public Node leaveForNode(final ForNode forNode) { 827192506Syongari if (forNode.isForIn()) { 828192506Syongari forNode.setIterator(newObjectInternal(ITERATOR_PREFIX)); //NASHORN-73 829192506Syongari } 830192506Syongari 831192506Syongari return end(forNode); 832192506Syongari } 833192506Syongari 834192506Syongari @Override 835192506Syongari public Node leaveFunctionNode(final FunctionNode functionNode) { 836192506Syongari final FunctionNode finalizedFunction; 837192506Syongari if (isUnparsedFunction(functionNode)) { 838192506Syongari finalizedFunction = functionNode; 839192506Syongari } else { 840192506Syongari finalizedFunction = 841192506Syongari markProgramBlock( 842192506Syongari removeUnusedSlots( 843192506Syongari createSyntheticInitializers( 844192506Syongari finalizeParameters( 845192506Syongari lc.applyTopFlags(functionNode)))) 84676479Swpaul .setThisProperties(lc, thisProperties.pop().size())); 84776479Swpaul } 84876479Swpaul return finalizedFunction.setState(lc, CompilationState.SYMBOLS_ASSIGNED); 84976479Swpaul } 85076479Swpaul 85176479Swpaul @Override 852192506Syongari public Node leaveIdentNode(final IdentNode identNode) { 85376479Swpaul if (identNode.isPropertyName()) { 85476479Swpaul return identNode; 85576479Swpaul } 85676479Swpaul 85776479Swpaul final Symbol symbol = nameIsUsed(identNode.getName(), identNode); 85876479Swpaul 85976479Swpaul if (!identNode.isInitializedHere()) { 86076479Swpaul symbol.increaseUseCount(); 86176479Swpaul } 86276479Swpaul 86376479Swpaul IdentNode newIdentNode = identNode.setSymbol(symbol); 86476479Swpaul 86576479Swpaul // If a block-scoped var is used before its declaration mark it as dead. 866195049Srwatson // We can only statically detect this for local vars, cross-function symbols require runtime checks. 86776479Swpaul if (symbol.isBlockScoped() && !symbol.hasBeenDeclared() && !identNode.isDeclaredHere() && isLocal(lc.getCurrentFunction(), symbol)) { 86876479Swpaul newIdentNode = newIdentNode.markDead(); 86976479Swpaul } 870130270Snaddy 871130270Snaddy return end(newIdentNode); 87276479Swpaul } 87376479Swpaul 87476479Swpaul private Symbol nameIsUsed(final String name, final IdentNode origin) { 87576479Swpaul final Block block = lc.getCurrentBlock(); 87676479Swpaul 87776479Swpaul Symbol symbol = findSymbol(block, name); 878195049Srwatson 87976479Swpaul //If an existing symbol with the name is found, use that otherwise, declare a new one 880192506Syongari if (symbol != null) { 881192506Syongari log.info("Existing symbol = ", symbol); 882192506Syongari if (symbol.isFunctionSelf()) { 883192506Syongari final FunctionNode functionNode = lc.getDefiningFunction(symbol); 884192506Syongari assert functionNode != null; 885192506Syongari assert lc.getFunctionBody(functionNode).getExistingSymbol(CALLEE.symbolName()) != null; 88676479Swpaul lc.setFlag(functionNode, FunctionNode.USES_SELF_SYMBOL); 88776479Swpaul } 88899497Salfred 889192288Syongari // if symbol is non-local or we're in a with block, we need to put symbol in scope (if it isn't already) 89076479Swpaul maybeForceScope(symbol); 891192506Syongari } else { 892192297Syongari log.info("No symbol exists. Declare as global: ", name); 89376479Swpaul symbol = defineSymbol(block, name, origin, IS_GLOBAL | IS_SCOPE); 89476479Swpaul } 89576479Swpaul 89676479Swpaul functionUsesSymbol(symbol); 89776479Swpaul return symbol; 89876479Swpaul } 899192506Syongari 90076479Swpaul @Override 90176479Swpaul public Node leaveSwitchNode(final SwitchNode switchNode) { 90276479Swpaul // We only need a symbol for the tag if it's not an integer switch node 903162321Sglebius if(!switchNode.isUniqueInteger()) { 90476479Swpaul switchNode.setTag(newObjectInternal(SWITCH_TAG_PREFIX)); 90576479Swpaul } 90676479Swpaul return switchNode; 90776479Swpaul } 90876479Swpaul 90976479Swpaul @Override 91076479Swpaul public Node leaveTryNode(final TryNode tryNode) { 91176479Swpaul tryNode.setException(exceptionSymbol()); 91276479Swpaul assert tryNode.getFinallyBody() == null; 91376479Swpaul 914192506Syongari end(tryNode); 915192506Syongari 916192506Syongari return tryNode; 917192506Syongari } 918192506Syongari 919192506Syongari private Node leaveTYPEOF(final UnaryNode unaryNode) { 920192506Syongari final Expression rhs = unaryNode.getExpression(); 921192506Syongari 922192506Syongari final List<Expression> args = new ArrayList<>(); 923192506Syongari if (rhs instanceof IdentNode && !isParamOrVar((IdentNode)rhs)) { 924192506Syongari args.add(compilerConstantIdentifier(SCOPE)); 925192506Syongari args.add((Expression)LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null 92676479Swpaul } else { 92776479Swpaul args.add(rhs); 92876479Swpaul args.add((Expression)LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this' 929108470Sschweikh } 93076479Swpaul 93176479Swpaul final Node runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args).accept(this); 93299497Salfred 933192288Syongari end(unaryNode); 93476479Swpaul 935192297Syongari return runtimeNode; 93676479Swpaul } 93776479Swpaul 93876479Swpaul private FunctionNode markProgramBlock(final FunctionNode functionNode) { 939192292Syongari if (compiler.isOnDemandCompilation() || !functionNode.isProgram()) { 94076479Swpaul return functionNode; 94176479Swpaul } 94276479Swpaul 943192292Syongari return functionNode.setBody(lc, functionNode.getBody().setFlag(lc, Block.IS_GLOBAL_SCOPE)); 94476479Swpaul } 94576479Swpaul 94676479Swpaul /** 94776479Swpaul * If the symbol isn't already a scope symbol, but it needs to be (see {@link #symbolNeedsToBeScope(Symbol)}, it is 948192292Syongari * promoted to a scope symbol and its block marked as needing a scope. 94976479Swpaul * @param symbol the symbol that might be scoped 95076479Swpaul */ 95176479Swpaul private void maybeForceScope(final Symbol symbol) { 95276479Swpaul if (!symbol.isScope() && symbolNeedsToBeScope(symbol)) { 95376479Swpaul Symbol.setSymbolIsScope(lc, symbol); 95476479Swpaul } 95599497Salfred } 956192288Syongari 95776479Swpaul private Symbol newInternal(final CompilerConstants cc, final int flags) { 958192506Syongari return defineSymbol(lc.getCurrentBlock(), lc.getCurrentFunction().uniqueName(cc.symbolName()), null, IS_VAR | IS_INTERNAL | flags); //NASHORN-73 959192506Syongari } 960192297Syongari 961192506Syongari private Symbol newObjectInternal(final CompilerConstants cc) { 962192506Syongari return newInternal(cc, HAS_OBJECT_VALUE); 96376479Swpaul } 964192506Syongari 96576479Swpaul private boolean start(final Node node) { 966162321Sglebius return start(node, true); 96776479Swpaul } 968135250Swpaul 969151296Sjhb private boolean start(final Node node, final boolean printNode) { 970151296Sjhb if (debug) { 97176479Swpaul final StringBuilder sb = new StringBuilder(); 97276479Swpaul 97376479Swpaul sb.append("[ENTER "). 97476479Swpaul append(name(node)). 97576479Swpaul append("] "). 976192506Syongari append(printNode ? node.toString() : ""). 977192506Syongari append(" in '"). 978192506Syongari append(lc.getCurrentFunction().getName()). 979192506Syongari append("'"); 980192506Syongari log.info(sb); 981192506Syongari log.indent(); 982192506Syongari } 983192506Syongari 984192506Syongari return true; 98576479Swpaul } 98676479Swpaul 987192506Syongari /** 988192506Syongari * Determines if the symbol has to be a scope symbol. In general terms, it has to be a scope symbol if it can only 989192506Syongari * be reached from the current block by traversing a function node, a split node, or a with node. 990192506Syongari * @param symbol the symbol checked for needing to be a scope symbol 991192506Syongari * @return true if the symbol has to be a scope symbol. 992192506Syongari */ 993192506Syongari private boolean symbolNeedsToBeScope(final Symbol symbol) { 994192506Syongari if (symbol.isThis() || symbol.isInternal()) { 995192506Syongari return false; 996192506Syongari } 997192506Syongari 998192506Syongari final FunctionNode func = lc.getCurrentFunction(); 999192506Syongari if ( func.allVarsInScope() || (!symbol.isBlockScoped() && func.isProgram())) { 1000192506Syongari return true; 1001192506Syongari } 1002192506Syongari 100376479Swpaul boolean previousWasBlock = false; 100476479Swpaul for (final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) { 100576479Swpaul final LexicalContextNode node = it.next(); 100676479Swpaul if (node instanceof FunctionNode || isSplitArray(node)) { 1007127135Snjl // We reached the function boundary or a splitting boundary without seeing a definition for the symbol. 100876479Swpaul // It needs to be in scope. 100976479Swpaul return true; 101076479Swpaul } else if (node instanceof WithNode) { 1011151296Sjhb if (previousWasBlock) { 101276479Swpaul // We reached a WithNode; the symbol must be scoped. Note that if the WithNode was not immediately 101376479Swpaul // preceded by a block, this means we're currently processing its expression, not its body, 101476479Swpaul // therefore it doesn't count. 101576479Swpaul return true; 1016192506Syongari } 1017192506Syongari previousWasBlock = false; 1018192506Syongari } else if (node instanceof Block) { 1019192506Syongari if (((Block)node).getExistingSymbol(symbol.getName()) == symbol) { 1020192506Syongari // We reached the block that defines the symbol without reaching either the function boundary, or a 102176479Swpaul // WithNode. The symbol need not be scoped. 102276479Swpaul return false; 102376479Swpaul } 102476479Swpaul previousWasBlock = true; 102576479Swpaul } else { 102676479Swpaul previousWasBlock = false; 1027192506Syongari } 1028192506Syongari } 1029192506Syongari throw new AssertionError(); 1030192506Syongari } 1031192506Syongari 1032192506Syongari private static boolean isSplitArray(final LexicalContextNode expr) { 1033192506Syongari if(!(expr instanceof ArrayLiteralNode)) { 103476479Swpaul return false; 1035192506Syongari } 103676479Swpaul final List<ArrayUnit> units = ((ArrayLiteralNode)expr).getUnits(); 103776479Swpaul return !(units == null || units.isEmpty()); 103876479Swpaul } 103976479Swpaul 1040192506Syongari private void throwUnprotectedSwitchError(final VarNode varNode) { 1041192506Syongari // Block scoped declarations in switch statements without explicit blocks should be declared 1042147256Sbrooks // in a common block that contains all the case clauses. We cannot support this without a 1043147256Sbrooks // fundamental rewrite of how switch statements are handled (case nodes contain blocks and are 1044192506Syongari // directly contained by switch node). As a temporary solution we throw a reference error here. 1045147256Sbrooks final String msg = ECMAErrors.getMessage("syntax.error.unprotected.switch.declaration", varNode.isLet() ? "let" : "const"); 1046147256Sbrooks throwParserException(msg, varNode); 1047147256Sbrooks } 104876479Swpaul 1049121816Sbrooks private void throwParserException(final String message, final Node origin) { 1050135250Swpaul if (origin == null) { 105176479Swpaul throw new ParserException(message); 105276479Swpaul } 105376479Swpaul final Source source = compiler.getSource(); 1054192506Syongari final long token = origin.getToken(); 1055192506Syongari final int line = source.getLine(origin.getStart()); 1056192506Syongari final int column = source.getColumn(origin.getStart()); 105776479Swpaul final String formatted = ErrorManager.format(message, source, line, column, token); 1058192506Syongari throw new ParserException(JSErrorType.SYNTAX_ERROR, formatted, source, line, column, token); 1059192506Syongari } 1060192506Syongari} 1061192506Syongari