@Override public boolean enterVarNode(VarNode varNode) { if (!inSplitNode()) { return super.enterVarNode(varNode); } assert !varNode.isBlockScoped(); //TODO: we must handle these too, but we currently don't final Expression init = varNode.getInit(); if (varNode.isAnonymousFunctionDeclaration()) { // We ain't moving anonymous function declarations. return super.enterVarNode(varNode); } // Move a declaration-only var statement to the top of the outermost function. getCurrentFunctionState().varStatements.add(varNode.setInit(null)); // If it had an initializer, replace it with an assignment expression statement. Note that "var" is a // statement, so it doesn't contribute to :return of the programs, therefore we are _not_ adding a // ":return = ..." assignment around the original assignment. if (init != null) { final long token = Token.recast(varNode.getToken(), TokenType.ASSIGN); new ExpressionStatement(varNode.getLineNumber(), token, varNode.getFinish(), new BinaryNode(token, varNode.getName(), varNode.getInit())).accept(this); } return false; }
@Override public boolean enterVarNode(final VarNode varNode) { final Expression initNode = varNode.getInit(); if (initNode instanceof FunctionNode && ((FunctionNode)initNode).isDeclared()) { final FunctionNode funcNode = (FunctionNode) initNode; final List<? extends ExpressionTree> paramTrees = translateParameters(funcNode); final BlockTree blockTree = (BlockTree) translateBlock(funcNode.getBody(), true); curStat = new FunctionDeclarationTreeImpl(varNode, paramTrees, blockTree); } else if (initNode instanceof ClassNode && ((ClassNode)initNode).isStatement()) { final ClassNode classNode = (ClassNode) initNode; curStat = new ClassDeclarationTreeImpl(varNode, translateIdent(classNode.getIdent()), translateExpr(classNode.getClassHeritage()), translateProperty(classNode.getConstructor()), translateProperties(classNode.getClassElements())); } else { curStat = new VariableTreeImpl(varNode, translateIdent(varNode.getName()), translateExpr(initNode)); } return false; }
private void verifyDestructuringParameterBindingPattern(final Expression pattern, final long paramToken, final int paramLine, final String contextString) { verifyDestructuringBindingPattern(pattern, new Consumer<IdentNode>() { public void accept(final IdentNode identNode) { verifyIdent(identNode, contextString); final ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); if (currentFunction != null) { // declare function-scope variables for destructuring bindings if (!env._parse_only) { lc.getFunctionBody(currentFunction).appendStatement(new VarNode(paramLine, Token.recast(paramToken, VAR), pattern.getFinish(), identNode, null)); } // detect duplicate bounds names in parameter list currentFunction.addParameterBinding(identNode); currentFunction.setSimpleParameterList(false); } } }); }
/** * When we eliminate dead code, we must preserve var declarations as they are scoped to the whole * function. This method gathers var nodes from code passed to it, removing their initializers. * * @param deadCodeRoot the root node of eliminated dead code * @param statements a list that will be receiving the var nodes from the dead code, with their * initializers removed. */ static void extractVarNodesFromDeadCode(final Node deadCodeRoot, final List<Statement> statements) { deadCodeRoot.accept(new SimpleNodeVisitor() { @Override public boolean enterVarNode(final VarNode varNode) { statements.add(varNode.setInit(null)); return false; } @Override public boolean enterFunctionNode(final FunctionNode functionNode) { // Don't descend into nested functions return false; } }); }
@Override public boolean enterVarNode(final VarNode varNode) { if (!inSplitNode()) { return super.enterVarNode(varNode); } assert !varNode.isBlockScoped(); //TODO: we must handle these too, but we currently don't final Expression init = varNode.getInit(); // Move a declaration-only var statement to the top of the outermost function. getCurrentFunctionState().varStatements.add(varNode.setInit(null)); // If it had an initializer, replace it with an assignment expression statement. Note that "var" is a // statement, so it doesn't contribute to :return of the programs, therefore we are _not_ adding a // ":return = ..." assignment around the original assignment. if (init != null) { final long token = Token.recast(varNode.getToken(), TokenType.ASSIGN); new ExpressionStatement(varNode.getLineNumber(), token, varNode.getFinish(), new BinaryNode(token, varNode.getName(), varNode.getInit())).accept(this); } return false; }
@Override public boolean enterVarNode(final VarNode varNode) { final Expression initNode = varNode.getInit(); if (initNode instanceof FunctionNode && ((FunctionNode)initNode).isDeclared()) { final FunctionNode funcNode = (FunctionNode) initNode; final List<? extends ExpressionTree> paramTrees = translateExprs(funcNode.getParameters()); final BlockTree blockTree = (BlockTree) translateBlock(funcNode.getBody(), true); curStat = new FunctionDeclarationTreeImpl(varNode, paramTrees, blockTree); } else { curStat = new VariableTreeImpl(varNode, translateExpr(initNode)); } return false; }
private void verifyDestructuringParameterBindingPattern(final Expression pattern, final long paramToken, final int paramLine, final String contextString) { verifyDestructuringBindingPattern(pattern, new Consumer<IdentNode>() { public void accept(IdentNode identNode) { verifyIdent(identNode, contextString); ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); if (currentFunction != null) { // declare function-scope variables for destructuring bindings lc.getFunctionBody(currentFunction).appendStatement(new VarNode(paramLine, Token.recast(paramToken, VAR), pattern.getFinish(), identNode, null)); // detect duplicate bounds names in parameter list currentFunction.addParameterBinding(identNode); currentFunction.setSimpleParameterList(false); } } }); }
@Override public boolean enterVarNode(final VarNode varNode) { if (!inSplitNode()) { return super.enterVarNode(varNode); } assert !varNode.isBlockScoped(); //TODO: we must handle these too, but we currently don't final Expression init = varNode.getInit(); if (varNode.isAnonymousFunctionDeclaration()) { // We ain't moving anonymous function declarations. return super.enterVarNode(varNode); } // Move a declaration-only var statement to the top of the outermost function. getCurrentFunctionState().varStatements.add(varNode.setInit(null)); // If it had an initializer, replace it with an assignment expression statement. Note that "var" is a // statement, so it doesn't contribute to :return of the programs, therefore we are _not_ adding a // ":return = ..." assignment around the original assignment. if (init != null) { final long token = Token.recast(varNode.getToken(), TokenType.ASSIGN); new ExpressionStatement(varNode.getLineNumber(), token, varNode.getFinish(), new BinaryNode(token, varNode.getName(), varNode.getInit())).accept(this); } return false; }
@Override public boolean enterVarNode(final VarNode varNode) { start(varNode); final IdentNode ident = varNode.getName(); final String name = ident.getName(); final Symbol symbol = defineSymbol(lc.getCurrentBlock(), name, IS_VAR); assert symbol != null; // NASHORN-467 - use before definition of vars - conservative if (isLocalUse(ident.getName())) { newType(symbol, Type.OBJECT); symbol.setCanBeUndefined(); } return true; }
@Override public Node leaveVarNode(final VarNode varNode) { final Expression init = varNode.getInit(); if (init != null) { final SpecializedNode specialized = specialize(varNode); final VarNode specVarNode = (VarNode)specialized.node; Type destType = specialized.type; if (destType == null) { destType = specVarNode.getName().getType(); } assert specVarNode.getName().hasType() : specVarNode + " doesn't have a type"; final Expression convertedInit = convert(init, destType); temporarySymbols.reuse(); return specVarNode.setInit(convertedInit); } temporarySymbols.reuse(); return varNode; }
private void addFunctionDeclarations(final FunctionNode functionNode) { VarNode lastDecl = null; for (int i = functionDeclarations.size() - 1; i >= 0; i--) { Statement decl = functionDeclarations.get(i); if (lastDecl == null && decl instanceof VarNode) { decl = lastDecl = ((VarNode)decl).setFlag(VarNode.IS_LAST_FUNCTION_DECLARATION); lc.setFlag(functionNode, FunctionNode.HAS_FUNCTION_DECLARATIONS); } prependStatement(decl); } }
@Override public boolean enterVarNode(final VarNode varNode) { sb.append("var "); varNode.getName().toString(sb, printTypes); printLocalVariableConversion(varNode.getName()); final Node init = varNode.getInit(); if (init != null) { sb.append(" = "); init.accept(this); } return false; }
/** * Define symbols for all variable declarations at the top of the function scope. This way we can get around * problems like * * while (true) { * break; * if (true) { * var s; * } * } * * to an arbitrary nesting depth. * * see NASHORN-73 * * @param functionNode the FunctionNode we are entering * @param body the body of the FunctionNode we are entering */ private void acceptDeclarations(final FunctionNode functionNode, final Block body) { // This visitor will assign symbol to all declared variables. body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { @Override protected boolean enterDefault(final Node node) { // Don't bother visiting expressions; var is a statement, it can't be inside an expression. // This will also prevent visiting nested functions (as FunctionNode is an expression). return !(node instanceof Expression); } @Override public Node leaveVarNode(final VarNode varNode) { final IdentNode ident = varNode.getName(); final boolean blockScoped = varNode.isBlockScoped(); if (blockScoped && lc.inUnprotectedSwitchContext()) { throwUnprotectedSwitchError(varNode); } final Block block = blockScoped ? lc.getCurrentBlock() : body; final Symbol symbol = defineSymbol(block, ident.getName(), ident, varNode.getSymbolFlags()); if (varNode.isFunctionDeclaration()) { symbol.setIsFunctionDeclaration(); } return varNode.setName(ident.setSymbol(symbol)); } }); }
/** * Creates a synthetic initializer for a variable (a var statement that doesn't occur in the source code). Typically * used to create assignmnent of {@code :callee} to the function name symbol in self-referential function * expressions as well as for assignment of {@code :arguments} to {@code arguments}. * * @param name the ident node identifying the variable to initialize * @param initConstant the compiler constant it is initialized to * @param fn the function node the assignment is for * @return a var node with the appropriate assignment */ private VarNode createSyntheticInitializer(final IdentNode name, final CompilerConstants initConstant, final FunctionNode fn) { final IdentNode init = compilerConstantIdentifier(initConstant); assert init.getSymbol() != null && init.getSymbol().isBytecodeLocal(); final VarNode synthVar = new VarNode(fn.getLineNumber(), fn.getToken(), fn.getFinish(), name, init); final Symbol nameSymbol = fn.getBody().getExistingSymbol(name.getName()); assert nameSymbol != null; return (VarNode)synthVar.setName(name.setSymbol(nameSymbol)).accept(this); }
private FunctionNode createSyntheticInitializers(final FunctionNode functionNode) { final List<VarNode> syntheticInitializers = new ArrayList<>(2); // Must visit the new var nodes in the context of the body. We could also just set the new statements into the // block and then revisit the entire block, but that seems to be too much double work. final Block body = functionNode.getBody(); lc.push(body); try { if (functionNode.usesSelfSymbol()) { // "var fn = :callee" syntheticInitializers.add(createSyntheticInitializer(functionNode.getIdent(), CALLEE, functionNode)); } if (functionNode.needsArguments()) { // "var arguments = :arguments" syntheticInitializers.add(createSyntheticInitializer(createImplicitIdentifier(ARGUMENTS_VAR.symbolName()), ARGUMENTS, functionNode)); } if (syntheticInitializers.isEmpty()) { return functionNode; } for(final ListIterator<VarNode> it = syntheticInitializers.listIterator(); it.hasNext();) { it.set((VarNode)it.next().accept(this)); } } finally { lc.pop(body); } final List<Statement> stmts = body.getStatements(); final List<Statement> newStatements = new ArrayList<>(stmts.size() + syntheticInitializers.size()); newStatements.addAll(syntheticInitializers); newStatements.addAll(stmts); return functionNode.setBody(lc, body.setStatements(lc, newStatements)); }
@Override public boolean enterVarNode(final VarNode varNode) { start(varNode); // Normally, a symbol assigned in a var statement is not live for its RHS. Since we also represent function // declarations as VarNodes, they are exception to the rule, as they need to have the symbol visible to the // body of the declared function for self-reference. if (varNode.isFunctionDeclaration()) { defineVarIdent(varNode); } return true; }
@Override public Node leaveVarNode(final VarNode varNode) { if (!varNode.isFunctionDeclaration()) { defineVarIdent(varNode); } return super.leaveVarNode(varNode); }
private void defineVarIdent(final VarNode varNode) { final IdentNode ident = varNode.getName(); final int flags; if (varNode.isAnonymousFunctionDeclaration()) { flags = IS_INTERNAL; } else if (!varNode.isBlockScoped() && lc.getCurrentFunction().isProgram()) { flags = IS_SCOPE; } else { flags = 0; } defineSymbol(lc.getCurrentBlock(), ident.getName(), ident, varNode.getSymbolFlags() | flags); }
private void throwUnprotectedSwitchError(final VarNode varNode) { // Block scoped declarations in switch statements without explicit blocks should be declared // in a common block that contains all the case clauses. We cannot support this without a // fundamental rewrite of how switch statements are handled (case nodes contain blocks and are // directly contained by switch node). As a temporary solution we throw a reference error here. final String msg = ECMAErrors.getMessage("syntax.error.unprotected.switch.declaration", varNode.isLet() ? "let" : "const"); throwParserException(msg, varNode); }
private static void extractVarNodes(final Block block, final List<Statement> statements) { final LexicalContext lc = new LexicalContext(); block.accept(lc, new NodeVisitor<LexicalContext>(lc) { @Override public boolean enterVarNode(final VarNode varNode) { statements.add(varNode.setInit(null)); return false; } }); }
@Override public Node leaveVarNode(final VarNode varNode) { addStatement(varNode); if (varNode.getFlag(VarNode.IS_LAST_FUNCTION_DECLARATION) && lc.getCurrentFunction().isProgram()) { new ExpressionStatement(varNode.getLineNumber(), varNode.getToken(), varNode.getFinish(), new IdentNode(varNode.getName())).accept(this); } return varNode; }
@Override public boolean enterVarNode(final VarNode varNode) { if (!reachable) { return false; } final Expression init = varNode.getInit(); if(init != null) { init.accept(this); onAssignment(varNode.getName(), init); } return false; }
ClassDeclarationTreeImpl(final VarNode node, final IdentifierTree name, final ExpressionTree classHeritage, final PropertyTree constructor, final List<? extends PropertyTree> classElements) { super(node); this.name = name; this.classHeritage = classHeritage; this.constructor = constructor; this.classElements = classElements; }
FunctionDeclarationTreeImpl(final VarNode node, final List<? extends ExpressionTree> params, final BlockTree body) { super(node); assert node.getInit() instanceof FunctionNode : "function expected"; funcNode = (FunctionNode)node.getInit(); assert funcNode.isDeclared() : "function declaration expected"; funcName = funcNode.isAnonymous()? null : new IdentifierTreeImpl(node.getName()); this.params = params; this.body = body; }
/** * ClassDeclaration[Yield, Default] : * class BindingIdentifier[?Yield] ClassTail[?Yield] * [+Default] class ClassTail[?Yield] */ private ClassNode classDeclaration(final boolean isDefault) { final int classLineNumber = line; final ClassNode classExpression = classExpression(!isDefault); if (!isDefault) { final VarNode classVar = new VarNode(classLineNumber, classExpression.getToken(), classExpression.getIdent().getFinish(), classExpression.getIdent(), classExpression, VarNode.IS_CONST); appendStatement(classVar); } return classExpression; }
private void addFunctionDeclarations(final ParserContextFunctionNode functionNode) { VarNode lastDecl = null; for (int i = functionDeclarations.size() - 1; i >= 0; i--) { Statement decl = functionDeclarations.get(i); if (lastDecl == null && decl instanceof VarNode) { decl = lastDecl = ((VarNode)decl).setFlag(VarNode.IS_LAST_FUNCTION_DECLARATION); functionNode.setFlag(FunctionNode.HAS_FUNCTION_DECLARATIONS); } prependStatement(decl); } }
@Override public boolean enterVarNode(final VarNode varNode) { sb.append(varNode.isConst() ? "const " : varNode.isLet() ? "let " : "var "); varNode.getName().toString(sb, printTypes); printLocalVariableConversion(varNode.getName()); final Node init = varNode.getInit(); if (init != null) { sb.append(" = "); init.accept(this); } return false; }
/** * Define symbols for all variable declarations at the top of the function scope. This way we can get around * problems like * * while (true) { * break; * if (true) { * var s; * } * } * * to an arbitrary nesting depth. * * see NASHORN-73 * * @param functionNode the FunctionNode we are entering * @param body the body of the FunctionNode we are entering */ private void acceptDeclarations(final FunctionNode functionNode, final Block body) { // This visitor will assign symbol to all declared variables. body.accept(new SimpleNodeVisitor() { @Override protected boolean enterDefault(final Node node) { // Don't bother visiting expressions; var is a statement, it can't be inside an expression. // This will also prevent visiting nested functions (as FunctionNode is an expression). return !(node instanceof Expression); } @Override public Node leaveVarNode(final VarNode varNode) { final IdentNode ident = varNode.getName(); final boolean blockScoped = varNode.isBlockScoped(); if (blockScoped && lc.inUnprotectedSwitchContext()) { throwUnprotectedSwitchError(varNode); } final Block block = blockScoped ? lc.getCurrentBlock() : body; final Symbol symbol = defineSymbol(block, ident.getName(), ident, varNode.getSymbolFlags()); if (varNode.isFunctionDeclaration()) { symbol.setIsFunctionDeclaration(); } return varNode.setName(ident.setSymbol(symbol)); } }); }
/** * Creates a synthetic initializer for a variable (a var statement that doesn't occur in the source code). Typically * used to create assignment of {@code :callee} to the function name symbol in self-referential function * expressions as well as for assignment of {@code :arguments} to {@code arguments}. * * @param name the ident node identifying the variable to initialize * @param initConstant the compiler constant it is initialized to * @param fn the function node the assignment is for * @return a var node with the appropriate assignment */ private VarNode createSyntheticInitializer(final IdentNode name, final CompilerConstants initConstant, final FunctionNode fn) { final IdentNode init = compilerConstantIdentifier(initConstant); assert init.getSymbol() != null && init.getSymbol().isBytecodeLocal(); final VarNode synthVar = new VarNode(fn.getLineNumber(), fn.getToken(), fn.getFinish(), name, init); final Symbol nameSymbol = fn.getBody().getExistingSymbol(name.getName()); assert nameSymbol != null; return (VarNode)synthVar.setName(name.setSymbol(nameSymbol)).accept(this); }