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