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