org.mozilla.javascript.IRFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rhino Show documentation
Show all versions of rhino Show documentation
Rhino is an open-source implementation of JavaScript written entirely in Java. It is typically
embedded into Java applications to provide scripting to end users.
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.javascript;
import java.util.ArrayList;
import java.util.List;
import org.mozilla.javascript.ast.ArrayComprehension;
import org.mozilla.javascript.ast.ArrayComprehensionLoop;
import org.mozilla.javascript.ast.ArrayLiteral;
import org.mozilla.javascript.ast.Assignment;
import org.mozilla.javascript.ast.AstNode;
import org.mozilla.javascript.ast.AstRoot;
import org.mozilla.javascript.ast.Block;
import org.mozilla.javascript.ast.BreakStatement;
import org.mozilla.javascript.ast.CatchClause;
import org.mozilla.javascript.ast.ConditionalExpression;
import org.mozilla.javascript.ast.ContinueStatement;
import org.mozilla.javascript.ast.DestructuringForm;
import org.mozilla.javascript.ast.DoLoop;
import org.mozilla.javascript.ast.ElementGet;
import org.mozilla.javascript.ast.EmptyExpression;
import org.mozilla.javascript.ast.ExpressionStatement;
import org.mozilla.javascript.ast.ForInLoop;
import org.mozilla.javascript.ast.ForLoop;
import org.mozilla.javascript.ast.FunctionCall;
import org.mozilla.javascript.ast.FunctionNode;
import org.mozilla.javascript.ast.GeneratorExpression;
import org.mozilla.javascript.ast.GeneratorExpressionLoop;
import org.mozilla.javascript.ast.IfStatement;
import org.mozilla.javascript.ast.InfixExpression;
import org.mozilla.javascript.ast.Jump;
import org.mozilla.javascript.ast.Label;
import org.mozilla.javascript.ast.LabeledStatement;
import org.mozilla.javascript.ast.LetNode;
import org.mozilla.javascript.ast.Loop;
import org.mozilla.javascript.ast.Name;
import org.mozilla.javascript.ast.NewExpression;
import org.mozilla.javascript.ast.NumberLiteral;
import org.mozilla.javascript.ast.ObjectLiteral;
import org.mozilla.javascript.ast.ObjectProperty;
import org.mozilla.javascript.ast.ParenthesizedExpression;
import org.mozilla.javascript.ast.PropertyGet;
import org.mozilla.javascript.ast.RegExpLiteral;
import org.mozilla.javascript.ast.ReturnStatement;
import org.mozilla.javascript.ast.Scope;
import org.mozilla.javascript.ast.ScriptNode;
import org.mozilla.javascript.ast.StringLiteral;
import org.mozilla.javascript.ast.SwitchCase;
import org.mozilla.javascript.ast.SwitchStatement;
import org.mozilla.javascript.ast.Symbol;
import org.mozilla.javascript.ast.ThrowStatement;
import org.mozilla.javascript.ast.TryStatement;
import org.mozilla.javascript.ast.UnaryExpression;
import org.mozilla.javascript.ast.VariableDeclaration;
import org.mozilla.javascript.ast.VariableInitializer;
import org.mozilla.javascript.ast.WhileLoop;
import org.mozilla.javascript.ast.WithStatement;
import org.mozilla.javascript.ast.XmlDotQuery;
import org.mozilla.javascript.ast.XmlElemRef;
import org.mozilla.javascript.ast.XmlExpression;
import org.mozilla.javascript.ast.XmlFragment;
import org.mozilla.javascript.ast.XmlLiteral;
import org.mozilla.javascript.ast.XmlMemberGet;
import org.mozilla.javascript.ast.XmlPropRef;
import org.mozilla.javascript.ast.XmlRef;
import org.mozilla.javascript.ast.XmlString;
import org.mozilla.javascript.ast.Yield;
/**
* This class rewrites the parse tree into an IR suitable for codegen.
*
* @see Node
* @author Mike McCabe
* @author Norris Boyd
*/
public final class IRFactory extends Parser
{
private static final int LOOP_DO_WHILE = 0;
private static final int LOOP_WHILE = 1;
private static final int LOOP_FOR = 2;
private static final int ALWAYS_TRUE_BOOLEAN = 1;
private static final int ALWAYS_FALSE_BOOLEAN = -1;
private Decompiler decompiler = new Decompiler();
public IRFactory() {
super();
}
public IRFactory(CompilerEnvirons env) {
this(env, env.getErrorReporter());
}
public IRFactory(CompilerEnvirons env, ErrorReporter errorReporter) {
super(env, errorReporter);
}
/**
* Transforms the tree into a lower-level IR suitable for codegen.
* Optionally generates the encoded source.
*/
public ScriptNode transformTree(AstRoot root) {
currentScriptOrFn = root;
this.inUseStrictDirective = root.isInStrictMode();
int sourceStartOffset = decompiler.getCurrentOffset();
if (Token.printTrees) {
System.out.println("IRFactory.transformTree");
System.out.println(root.debugPrint());
}
ScriptNode script = (ScriptNode)transform(root);
int sourceEndOffset = decompiler.getCurrentOffset();
script.setEncodedSourceBounds(sourceStartOffset,
sourceEndOffset);
if (compilerEnv.isGeneratingSource()) {
script.setEncodedSource(decompiler.getEncodedSource());
}
decompiler = null;
return script;
}
// Might want to convert this to polymorphism - move transform*
// functions into the AstNode subclasses. OTOH that would make
// IR transformation part of the public AST API - desirable?
// Another possibility: create AstTransformer interface and adapter.
public Node transform(AstNode node) {
switch (node.getType()) {
case Token.ARRAYCOMP:
return transformArrayComp((ArrayComprehension)node);
case Token.ARRAYLIT:
return transformArrayLiteral((ArrayLiteral)node);
case Token.BLOCK:
return transformBlock(node);
case Token.BREAK:
return transformBreak((BreakStatement)node);
case Token.CALL:
return transformFunctionCall((FunctionCall)node);
case Token.CONTINUE:
return transformContinue((ContinueStatement)node);
case Token.DO:
return transformDoLoop((DoLoop)node);
case Token.EMPTY:
return node;
case Token.FOR:
if (node instanceof ForInLoop) {
return transformForInLoop((ForInLoop)node);
}
return transformForLoop((ForLoop)node);
case Token.FUNCTION:
return transformFunction((FunctionNode)node);
case Token.GENEXPR:
return transformGenExpr((GeneratorExpression)node);
case Token.GETELEM:
return transformElementGet((ElementGet)node);
case Token.GETPROP:
return transformPropertyGet((PropertyGet)node);
case Token.HOOK:
return transformCondExpr((ConditionalExpression)node);
case Token.IF:
return transformIf((IfStatement)node);
case Token.TRUE:
case Token.FALSE:
case Token.THIS:
case Token.NULL:
case Token.DEBUGGER:
return transformLiteral(node);
case Token.NAME:
return transformName((Name)node);
case Token.NUMBER:
return transformNumber((NumberLiteral)node);
case Token.NEW:
return transformNewExpr((NewExpression)node);
case Token.OBJECTLIT:
return transformObjectLiteral((ObjectLiteral)node);
case Token.REGEXP:
return transformRegExp((RegExpLiteral)node);
case Token.RETURN:
return transformReturn((ReturnStatement)node);
case Token.SCRIPT:
return transformScript((ScriptNode)node);
case Token.STRING:
return transformString((StringLiteral)node);
case Token.SWITCH:
return transformSwitch((SwitchStatement)node);
case Token.THROW:
return transformThrow((ThrowStatement)node);
case Token.TRY:
return transformTry((TryStatement)node);
case Token.WHILE:
return transformWhileLoop((WhileLoop)node);
case Token.WITH:
return transformWith((WithStatement)node);
case Token.YIELD:
case Token.YIELD_STAR:
return transformYield((Yield)node);
default:
if (node instanceof ExpressionStatement) {
return transformExprStmt((ExpressionStatement)node);
}
if (node instanceof Assignment) {
return transformAssignment((Assignment)node);
}
if (node instanceof UnaryExpression) {
return transformUnary((UnaryExpression)node);
}
if (node instanceof XmlMemberGet) {
return transformXmlMemberGet((XmlMemberGet)node);
}
if (node instanceof InfixExpression) {
return transformInfix((InfixExpression)node);
}
if (node instanceof VariableDeclaration) {
return transformVariables((VariableDeclaration)node);
}
if (node instanceof ParenthesizedExpression) {
return transformParenExpr((ParenthesizedExpression)node);
}
if (node instanceof LabeledStatement) {
return transformLabeledStatement((LabeledStatement)node);
}
if (node instanceof LetNode) {
return transformLetNode((LetNode)node);
}
if (node instanceof XmlRef) {
return transformXmlRef((XmlRef)node);
}
if (node instanceof XmlLiteral) {
return transformXmlLiteral((XmlLiteral)node);
}
throw new IllegalArgumentException("Can't transform: " + node);
}
}
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();
}
}
private Node arrayCompTransformHelper(ArrayComprehension node,
String arrayName) {
decompiler.addToken(Token.LB);
int lineno = node.getLineno();
Node expr = transform(node.getResult());
List loops = node.getLoops();
int numLoops = loops.size();
// Walk through loops, collecting and defining their iterator symbols.
Node[] iterators = new Node[numLoops];
Node[] iteratedObjs = new Node[numLoops];
for (int i = 0; i < numLoops; i++) {
ArrayComprehensionLoop acl = loops.get(i);
decompiler.addName(" ");
decompiler.addToken(Token.FOR);
if (acl.isForEach()) {
decompiler.addName("each ");
}
decompiler.addToken(Token.LP);
AstNode iter = acl.getIterator();
String name = null;
if (iter.getType() == Token.NAME) {
name = iter.getString();
decompiler.addName(name);
} else {
// destructuring assignment
decompile(iter);
name = currentScriptOrFn.getNextTempName();
defineSymbol(Token.LP, name, false);
expr = createBinary(Token.COMMA,
createAssignment(Token.ASSIGN,
iter,
createName(name)),
expr);
}
Node init = createName(name);
// Define as a let since we want the scope of the variable to
// be restricted to the array comprehension
defineSymbol(Token.LET, name, false);
iterators[i] = init;
if (acl.isForOf()) {
decompiler.addName("of ");
} else {
decompiler.addToken(Token.IN);
}
iteratedObjs[i] = transform(acl.getIteratedObject());
decompiler.addToken(Token.RP);
}
// generate code for tmpArray.push(body)
Node call = createCallOrNew(Token.CALL,
createPropertyGet(createName(arrayName),
null,
"push", 0));
Node body = new Node(Token.EXPR_VOID, call, lineno);
if (node.getFilter() != null) {
decompiler.addName(" ");
decompiler.addToken(Token.IF);
decompiler.addToken(Token.LP);
body = createIf(transform(node.getFilter()), body, null, lineno);
decompiler.addToken(Token.RP);
}
// Now walk loops in reverse to build up the body statement.
int pushed = 0;
try {
for (int i = numLoops-1; i >= 0; i--) {
ArrayComprehensionLoop acl = loops.get(i);
Scope loop = createLoopNode(null, // no label
acl.getLineno());
pushScope(loop);
pushed++;
body = createForIn(Token.LET,
loop,
iterators[i],
iteratedObjs[i],
body,
acl.isForEach(),
acl.isForOf());
}
} finally {
for (int i = 0; i < pushed; i++) {
popScope();
}
}
decompiler.addToken(Token.RB);
// Now that we've accumulated any destructuring forms,
// add expr to the call node; it's pushed on each iteration.
call.addChildToBack(expr);
return body;
}
private Node transformArrayLiteral(ArrayLiteral node) {
if (node.isDestructuring()) {
return node;
}
decompiler.addToken(Token.LB);
List elems = node.getElements();
Node array = new Node(Token.ARRAYLIT);
List skipIndexes = null;
for (int i = 0; i < elems.size(); ++i) {
AstNode elem = elems.get(i);
if (elem.getType() != Token.EMPTY) {
array.addChildToBack(transform(elem));
} else {
if (skipIndexes == null) {
skipIndexes = new ArrayList();
}
skipIndexes.add(Integer.valueOf(i));
}
if (i < elems.size() - 1)
decompiler.addToken(Token.COMMA);
}
decompiler.addToken(Token.RB);
array.putIntProp(Node.DESTRUCTURING_ARRAY_LENGTH,
node.getDestructuringLength());
if (skipIndexes != null) {
int[] skips = new int[skipIndexes.size()];
for (int i = 0; i < skipIndexes.size(); i++)
skips[i] = skipIndexes.get(i).intValue();
array.putProp(Node.SKIP_INDEXES_PROP, skips);
}
return array;
}
private Node transformAssignment(Assignment node) {
AstNode left = removeParens(node.getLeft());
Node target = null;
if (isDestructuring(left)) {
decompile(left);
target = left;
} else {
target = transform(left);
}
decompiler.addToken(node.getType());
return createAssignment(node.getType(),
target,
transform(node.getRight()));
}
private Node transformBlock(AstNode node) {
if (node instanceof Scope) {
pushScope((Scope)node);
}
try {
List kids = new ArrayList();
for (Node kid : node) {
kids.add(transform((AstNode)kid));
}
node.removeChildren();
for (Node kid : kids) {
node.addChildToBack(kid);
}
return node;
} finally {
if (node instanceof Scope) {
popScope();
}
}
}
private Node transformBreak(BreakStatement node) {
decompiler.addToken(Token.BREAK);
if (node.getBreakLabel() != null) {
decompiler.addName(node.getBreakLabel().getIdentifier());
}
decompiler.addEOL(Token.SEMI);
return node;
}
private Node transformCondExpr(ConditionalExpression node) {
Node test = transform(node.getTestExpression());
decompiler.addToken(Token.HOOK);
Node ifTrue = transform(node.getTrueExpression());
decompiler.addToken(Token.COLON);
Node ifFalse = transform(node.getFalseExpression());
return createCondExpr(test, ifTrue, ifFalse);
}
private Node transformContinue(ContinueStatement node) {
decompiler.addToken(Token.CONTINUE);
if (node.getLabel() != null) {
decompiler.addName(node.getLabel().getIdentifier());
}
decompiler.addEOL(Token.SEMI);
return node;
}
private Node transformDoLoop(DoLoop loop) {
loop.setType(Token.LOOP);
pushScope(loop);
try {
decompiler.addToken(Token.DO);
decompiler.addEOL(Token.LC);
Node body = transform(loop.getBody());
decompiler.addToken(Token.RC);
decompiler.addToken(Token.WHILE);
decompiler.addToken(Token.LP);
Node cond = transform(loop.getCondition());
decompiler.addToken(Token.RP);
decompiler.addEOL(Token.SEMI);
return createLoop(loop, LOOP_DO_WHILE,
body, cond, null, null);
} finally {
popScope();
}
}
private Node transformElementGet(ElementGet node) {
// OPT: could optimize to createPropertyGet
// iff elem is string that can not be number
Node target = transform(node.getTarget());
decompiler.addToken(Token.LB);
Node element = transform(node.getElement());
decompiler.addToken(Token.RB);
return new Node(Token.GETELEM, target, element);
}
private Node transformExprStmt(ExpressionStatement node) {
Node expr = transform(node.getExpression());
decompiler.addEOL(Token.SEMI);
return new Node(node.getType(), expr, node.getLineno());
}
private Node transformForInLoop(ForInLoop loop) {
decompiler.addToken(Token.FOR);
if (loop.isForEach())
decompiler.addName("each ");
decompiler.addToken(Token.LP);
loop.setType(Token.LOOP);
pushScope(loop);
try {
int declType = -1;
AstNode iter = loop.getIterator();
if (iter instanceof VariableDeclaration) {
declType = ((VariableDeclaration)iter).getType();
}
Node lhs = transform(iter);
if (loop.isForOf()) {
decompiler.addName("of ");
} else {
decompiler.addToken(Token.IN);
}
Node obj = transform(loop.getIteratedObject());
decompiler.addToken(Token.RP);
decompiler.addEOL(Token.LC);
Node body = transform(loop.getBody());
decompiler.addEOL(Token.RC);
return createForIn(declType, loop, lhs, obj, body,
loop.isForEach(), loop.isForOf());
} finally {
popScope();
}
}
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;
}
}
private Node transformFunction(FunctionNode fn) {
int functionType = fn.getFunctionType();
int start = decompiler.markFunctionStart(functionType);
Node mexpr = decompileFunctionHeader(fn);
int index = currentScriptOrFn.addFunction(fn);
PerFunctionVariables savedVars = new PerFunctionVariables(fn);
try {
// If we start needing to record much more codegen metadata during
// function parsing, we should lump it all into a helper class.
Node destructuring = (Node)fn.getProp(Node.DESTRUCTURING_PARAMS);
fn.removeProp(Node.DESTRUCTURING_PARAMS);
int lineno = fn.getBody().getLineno();
++nestingOfFunction; // only for body, not params
Node body = transform(fn.getBody());
if (!fn.isExpressionClosure()) {
decompiler.addToken(Token.RC);
}
fn.setEncodedSourceBounds(start, decompiler.markFunctionEnd(start));
if (functionType != FunctionNode.FUNCTION_EXPRESSION && !fn.isExpressionClosure()) {
// Add EOL only if function is not part of expression
// since it gets SEMI + EOL from Statement in that case
decompiler.addToken(Token.EOL);
}
if (destructuring != null) {
body.addChildToFront(new Node(Token.EXPR_VOID,
destructuring, lineno));
}
int syntheticType = fn.getFunctionType();
Node pn = initFunction(fn, index, body, syntheticType);
if (mexpr != null) {
pn = createAssignment(Token.ASSIGN, mexpr, pn);
if (syntheticType != FunctionNode.FUNCTION_EXPRESSION) {
pn = createExprStatementNoReturn(pn, fn.getLineno());
}
}
return pn;
} finally {
--nestingOfFunction;
savedVars.restore();
}
}
private Node transformFunctionCall(FunctionCall node) {
Node call = createCallOrNew(Token.CALL, transform(node.getTarget()));
call.setLineno(node.getLineno());
decompiler.addToken(Token.LP);
List args = node.getArguments();
for (int i = 0; i < args.size(); i++) {
AstNode arg = args.get(i);
call.addChildToBack(transform(arg));
if (i < args.size() - 1) {
decompiler.addToken(Token.COMMA);
}
}
decompiler.addToken(Token.RP);
return call;
}
private Node transformGenExpr(GeneratorExpression node) {
Node pn;
FunctionNode fn = new FunctionNode();
fn.setSourceName(currentScriptOrFn.getNextTempName());
fn.setIsGenerator();
fn.setFunctionType(FunctionNode.FUNCTION_EXPRESSION);
fn.setRequiresActivation();
int functionType = fn.getFunctionType();
int start = decompiler.markFunctionStart(functionType);
Node mexpr = decompileFunctionHeader(fn);
int index = currentScriptOrFn.addFunction(fn);
PerFunctionVariables savedVars = new PerFunctionVariables(fn);
try {
// If we start needing to record much more codegen metadata during
// function parsing, we should lump it all into a helper class.
Node destructuring = (Node)fn.getProp(Node.DESTRUCTURING_PARAMS);
fn.removeProp(Node.DESTRUCTURING_PARAMS);
int lineno = node.lineno;
++nestingOfFunction; // only for body, not params
Node body = genExprTransformHelper(node);
if (!fn.isExpressionClosure()) {
decompiler.addToken(Token.RC);
}
fn.setEncodedSourceBounds(start, decompiler.markFunctionEnd(start));
if (functionType != FunctionNode.FUNCTION_EXPRESSION && !fn.isExpressionClosure()) {
// Add EOL only if function is not part of expression
// since it gets SEMI + EOL from Statement in that case
decompiler.addToken(Token.EOL);
}
if (destructuring != null) {
body.addChildToFront(new Node(Token.EXPR_VOID,
destructuring, lineno));
}
int syntheticType = fn.getFunctionType();
pn = initFunction(fn, index, body, syntheticType);
if (mexpr != null) {
pn = createAssignment(Token.ASSIGN, mexpr, pn);
if (syntheticType != FunctionNode.FUNCTION_EXPRESSION) {
pn = createExprStatementNoReturn(pn, fn.getLineno());
}
}
} finally {
--nestingOfFunction;
savedVars.restore();
}
Node call = createCallOrNew(Token.CALL, pn);
call.setLineno(node.getLineno());
decompiler.addToken(Token.LP);
decompiler.addToken(Token.RP);
return call;
}
private Node genExprTransformHelper(GeneratorExpression node) {
decompiler.addToken(Token.LP);
int lineno = node.getLineno();
Node expr = transform(node.getResult());
List loops = node.getLoops();
int numLoops = loops.size();
// Walk through loops, collecting and defining their iterator symbols.
Node[] iterators = new Node[numLoops];
Node[] iteratedObjs = new Node[numLoops];
for (int i = 0; i < numLoops; i++) {
GeneratorExpressionLoop acl = loops.get(i);
decompiler.addName(" ");
decompiler.addToken(Token.FOR);
decompiler.addToken(Token.LP);
AstNode iter = acl.getIterator();
String name = null;
if (iter.getType() == Token.NAME) {
name = iter.getString();
decompiler.addName(name);
} else {
// destructuring assignment
decompile(iter);
name = currentScriptOrFn.getNextTempName();
defineSymbol(Token.LP, name, false);
expr = createBinary(Token.COMMA,
createAssignment(Token.ASSIGN,
iter,
createName(name)),
expr);
}
Node init = createName(name);
// Define as a let since we want the scope of the variable to
// be restricted to the array comprehension
defineSymbol(Token.LET, name, false);
iterators[i] = init;
if (acl.isForOf()) {
decompiler.addName("of ");
} else {
decompiler.addToken(Token.IN);
}
iteratedObjs[i] = transform(acl.getIteratedObject());
decompiler.addToken(Token.RP);
}
// generate code for tmpArray.push(body)
Node yield = new Node(Token.YIELD, expr, node.getLineno());
Node body = new Node(Token.EXPR_VOID, yield, lineno);
if (node.getFilter() != null) {
decompiler.addName(" ");
decompiler.addToken(Token.IF);
decompiler.addToken(Token.LP);
body = createIf(transform(node.getFilter()), body, null, lineno);
decompiler.addToken(Token.RP);
}
// Now walk loops in reverse to build up the body statement.
int pushed = 0;
try {
for (int i = numLoops-1; i >= 0; i--) {
GeneratorExpressionLoop acl = loops.get(i);
Scope loop = createLoopNode(null, // no label
acl.getLineno());
pushScope(loop);
pushed++;
body = createForIn(Token.LET,
loop,
iterators[i],
iteratedObjs[i],
body,
acl.isForEach(),
acl.isForOf());
}
} finally {
for (int i = 0; i < pushed; i++) {
popScope();
}
}
decompiler.addToken(Token.RP);
return body;
}
private Node transformIf(IfStatement n) {
decompiler.addToken(Token.IF);
decompiler.addToken(Token.LP);
Node cond = transform(n.getCondition());
decompiler.addToken(Token.RP);
decompiler.addEOL(Token.LC);
Node ifTrue = transform(n.getThenPart());
Node ifFalse = null;
if (n.getElsePart() != null) {
decompiler.addToken(Token.RC);
decompiler.addToken(Token.ELSE);
decompiler.addEOL(Token.LC);
ifFalse = transform(n.getElsePart());
}
decompiler.addEOL(Token.RC);
return createIf(cond, ifTrue, ifFalse, n.getLineno());
}
private Node transformInfix(InfixExpression node) {
Node left = transform(node.getLeft());
decompiler.addToken(node.getType());
Node right = transform(node.getRight());
if (node instanceof XmlDotQuery) {
decompiler.addToken(Token.RP);
}
return createBinary(node.getType(), left, right);
}
private Node transformLabeledStatement(LabeledStatement ls) {
Label label = ls.getFirstLabel();
List