private Node transformForLoop(ForLoop loop) { decompiler.addToken(Token.FOR); decompiler.addToken(Token.LP); loop.setType(Token.LOOP); // XXX: Can't use pushScope/popScope here since 'createFor' may split // the scope Scope savedScope = currentScope; currentScope = loop; try { Node init = transform(loop.getInitializer()); decompiler.addToken(Token.SEMI); Node test = transform(loop.getCondition()); decompiler.addToken(Token.SEMI); Node incr = transform(loop.getIncrement()); decompiler.addToken(Token.RP); decompiler.addEOL(Token.LC); Node body = transform(loop.getBody()); decompiler.addEOL(Token.RC); return createFor(loop, init, test, incr, body); } finally { currentScope = savedScope; } }
/** * Create loop node. The code generator will later call * createWhile|createDoWhile|createFor|createForIn * to finish loop generation. */ private Scope createLoopNode(Node loopLabel, int lineno) { Scope result = createScopeNode(Token.LOOP, lineno); if (loopLabel != null) { ((Jump)loopLabel).setLoop(result); } return result; }
private Node createFor(Scope loop, Node init, Node test, Node incr, Node body) { if (init.getType() == Token.LET) { // rewrite "for (let i=s; i < N; i++)..." as // "let (i=s) { for (; i < N; i++)..." so that "s" is evaluated // outside the scope of the for. Scope let = Scope.splitScope(loop); let.setType(Token.LET); let.addChildrenToBack(init); let.addChildToBack(createLoop(loop, LOOP_FOR, body, test, new Node(Token.EMPTY), incr)); return let; } return createLoop(loop, LOOP_FOR, body, test, init, incr); }
void pushScope(Scope scope) { Scope parent = scope.getParentScope(); // During codegen, parent scope chain may already be initialized, // in which case we just need to set currentScope variable. if (parent != null) { if (parent != currentScope) codeBug(); } else { currentScope.addChildScope(scope); } currentScope = scope; }
/** Can only be called when node has String context. */ public void setScope(Scope s) { if (s == null) Kit.codeBug(); if (!(this instanceof Name)) { throw Kit.codeBug(); } ((Name)this).setScope(s); }
public void hoist(AstNode node) { node.visit(n -> { if (n instanceof Scope) { Scope scope = (Scope) n; if (scope.getSymbolTable() != null) { for (String name : scope.getSymbolTable().keySet()) { declareName(name); } } } return true; }); }
private void print(Scope node) throws IOException { writer.append('{').softNewLine().indent(); for (Node child = node.getFirstChild(); child != null; child = child.getNext()) { print((AstNode) child); writer.softNewLine(); } writer.outdent().append('}'); }
private Node transformArrayComp(ArrayComprehension node) { // An array comprehension expression such as // // [expr for (x in foo) for each ([y, z] in bar) if (cond)] // // is rewritten approximately as // // new Scope(ARRAYCOMP) { // new Node(BLOCK) { // let tmp1 = new Array; // for (let x in foo) { // for each (let tmp2 in bar) { // if (cond) { // tmp1.push([y, z] = tmp2, expr); // } // } // } // } // createName(tmp1) // } int lineno = node.getLineno(); Scope scopeNode = createScopeNode(Token.ARRAYCOMP, lineno); String arrayName = currentScriptOrFn.getNextTempName(); pushScope(scopeNode); try { defineSymbol(Token.LET, arrayName, false); Node block = new Node(Token.BLOCK, lineno); Node newArray = createCallOrNew(Token.NEW, createName("Array")); Node init = new Node(Token.EXPR_VOID, createAssignment(Token.ASSIGN, createName(arrayName), newArray), lineno); block.addChildToBack(init); block.addChildToBack(arrayCompTransformHelper(node, arrayName)); scopeNode.addChildToBack(block); scopeNode.addChildToBack(createName(arrayName)); return scopeNode; } finally { popScope(); } }
void defineSymbol(int declType, String name, boolean ignoreNotInBlock) { if (name == null) { if (compilerEnv.isIdeMode()) { // be robust in IDE-mode return; } else { codeBug(); } } Scope definingScope = currentScope.getDefiningScope(name); Symbol symbol = definingScope != null ? definingScope.getSymbol(name) : null; int symDeclType = symbol != null ? symbol.getDeclType() : -1; if (symbol != null && (symDeclType == Token.CONST || declType == Token.CONST || (definingScope == currentScope && symDeclType == Token.LET))) { addError(symDeclType == Token.CONST ? "msg.const.redecl" : symDeclType == Token.LET ? "msg.let.redecl" : symDeclType == Token.VAR ? "msg.var.redecl" : symDeclType == Token.FUNCTION ? "msg.fn.redecl" : "msg.parm.redecl", name); return; } switch (declType) { case Token.LET: if (!ignoreNotInBlock && ((currentScope.getType() == Token.IF) || currentScope instanceof Loop)) { addError("msg.let.decl.not.in.block"); return; } currentScope.putSymbol(new Symbol(declType, name)); return; case Token.VAR: case Token.CONST: case Token.FUNCTION: if (symbol != null) { if (symDeclType == Token.VAR) addStrictWarning("msg.var.redecl", name); else if (symDeclType == Token.LP) { addStrictWarning("msg.var.hides.arg", name); } } else { currentScriptOrFn.putSymbol(new Symbol(declType, name)); } return; case Token.LP: if (symbol != null) { // must be duplicate parameter. Second parameter hides the // first, so go ahead and add the second parameter addWarning("msg.dup.parms", name); } currentScriptOrFn.putSymbol(new Symbol(declType, name)); return; default: throw codeBug(); } }
Node destructuringAssignmentHelper(int variableType, Node left, Node right, String tempName) { Scope result = createScopeNode(Token.LETEXPR, left.getLineno()); result.addChildToFront(new Node(Token.LET, createName(Token.NAME, tempName, right))); try { pushScope(result); defineSymbol(Token.LET, tempName, true); } finally { popScope(); } Node comma = new Node(Token.COMMA); result.addChildToBack(comma); List<String> destructuringNames = new ArrayList<String>(); boolean empty = true; switch (left.getType()) { case Token.ARRAYLIT: empty = destructuringArray((ArrayLiteral)left, variableType, tempName, comma, destructuringNames); break; case Token.OBJECTLIT: empty = destructuringObject((ObjectLiteral)left, variableType, tempName, comma, destructuringNames); break; case Token.GETPROP: case Token.GETELEM: switch (variableType) { case Token.CONST: case Token.LET: case Token.VAR: reportError("msg.bad.assign.left"); } comma.addChildToBack(simpleAssignment(left, createName(tempName))); break; default: reportError("msg.bad.assign.left"); } if (empty) { // Don't want a COMMA node with no children. Just add a zero. comma.addChildToBack(createNumber(0)); } result.putProp(Node.DESTRUCTURING_NAMES, destructuringNames); return result; }
/** Can only be called when node has String context. */ public Scope getScope() { return ((Name)this).getScope(); }
/** * This method generates constraints for all relevant AstNodes. It delegates its work to various * processXXX() methods that handle AstNodes of type XXX. */ @Override public boolean visit(AstNode node) { if (node instanceof VariableInitializer){ processVariableInitializer(node); } else if (node instanceof ReturnStatement){ processReturnStatement((ReturnStatement)node); } else if (node instanceof ExpressionStatement){ processExpressionStatement((ExpressionStatement)node); } else if (node instanceof ForLoop){ processForLoop((ForLoop)node); } else if (node instanceof ForInLoop){ processForInLoop((ForInLoop)node); }else if (node instanceof WhileLoop){ processWhileLoop((WhileLoop)node); } else if (node instanceof DoLoop){ processDoLoop((DoLoop)node); } else if (node instanceof NewExpression){ processNewExpression((NewExpression)node); } else if (node instanceof FunctionCall){ processFunctionCall((FunctionCall)node); } else if (node instanceof ElementGet){ processElementGet((ElementGet)node); } else if (node instanceof FunctionNode){ processFunctionNode((FunctionNode)node); } else if (node instanceof IfStatement){ processIfStatement((IfStatement)node); } else if (node instanceof KeywordLiteral){ processKeywordLiteral((KeywordLiteral)node); } else if (node instanceof SwitchStatement){ processSwitchStatement((SwitchStatement)node); } else if (node instanceof SwitchCase){ processSwitchCase((SwitchCase)node); } else if ((node instanceof AstRoot) || //AstRoot: no constraints need to be generated (node instanceof BreakStatement) || //BreakStatement: no constraints need to be generated (node instanceof VariableDeclaration) || //VariableDeclaration: we generate constraints for its constituent VariableInitializer nodes (node instanceof Name) || //Name: generate constraints for complex expressions that refer to names (node instanceof NumberLiteral) || //NumberLiteral: generate constraints for complex expressions that refer to names (node instanceof StringLiteral) || //StringLiteral: generate constraints for complex expressions that refer to names (node instanceof Assignment) || // Assignment is a special case of InfixExpression (node instanceof ArrayLiteral) || (node instanceof UnaryExpression) || (node instanceof InfixExpression) || (node instanceof ConditionalExpression) || (node instanceof ParenthesizedExpression) || (node instanceof EmptyExpression) || (node instanceof ObjectLiteral) || (node instanceof EmptyStatement) || (node instanceof ContinueStatement) || (node instanceof Scope) || (node instanceof Block)){ // // occurs in programs with for loops -- nothing to be done here? /* nothing */ } else { error("unsupported node " + node.toSource().trim() + " of type: " + node.getClass().getName(), node); } return true; }
/** * Create a node that can be used to hold lexically scoped variable * definitions (via let declarations). * * @param token the token of the node to create * @param lineno line number of source * @return the created node */ protected Scope createScopeNode(int token, int lineno) { Scope scope =new Scope(); scope.setType(token); scope.setLineno(lineno); return scope; }