private void TransformCompilationUnit_r(ScriptNode tree, Node parent, Scope scope, bool createScopeObjects, bool inStrictMode)
{
Node node = null;
for (; ; )
{
Node previous = null;
if (node == null)
{
node = parent.GetFirstChild();
}
else
{
previous = node;
node = node.GetNext();
}
if (node == null)
{
break;
}
int type = node.GetType();
if (createScopeObjects && (type == Token.BLOCK || type == Token.LOOP || type == Token.ARRAYCOMP) && (node is Scope))
{
Scope newScope = (Scope)node;
if (newScope.GetSymbolTable() != null)
{
// transform to let statement so we get a with statement
// created to contain scoped let variables
Node let = new Node(type == Token.ARRAYCOMP ? Token.LETEXPR : Token.LET);
Node innerLet = new Node(Token.LET);
let.AddChildToBack(innerLet);
foreach (string name in newScope.GetSymbolTable().Keys)
{
innerLet.AddChildToBack(Node.NewString(Token.NAME, name));
}
newScope.SetSymbolTable(null);
// so we don't transform again
Node oldNode = node;
node = ReplaceCurrent(parent, previous, node, let);
type = node.GetType();
let.AddChildToBack(oldNode);
}
}
switch (type)
{
case Token.LABEL:
case Token.SWITCH:
case Token.LOOP:
{
loops.Push(node);
loopEnds.Push(((Jump)node).target);
break;
}
case Token.WITH:
{
loops.Push(node);
Node leave = node.GetNext();
if (leave.GetType() != Token.LEAVEWITH)
{
Kit.CodeBug();
}
loopEnds.Push(leave);
break;
}
case Token.TRY:
{
Jump jump = (Jump)node;
Node finallytarget = jump.GetFinally();
if (finallytarget != null)
{
hasFinally = true;
loops.Push(node);
loopEnds.Push(finallytarget);
}
break;
}
case Token.TARGET:
case Token.LEAVEWITH:
{
if (!loopEnds.IsEmpty() && loopEnds.Peek() == node)
{
loopEnds.Pop();
loops.Pop();
}
break;
}
case Token.YIELD:
{
((FunctionNode)tree).AddResumptionPoint(node);
break;
}
case Token.RETURN:
{
bool isGenerator = tree.GetType() == Token.FUNCTION && ((FunctionNode)tree).IsGenerator();
if (isGenerator)
{
node.PutIntProp(Node.GENERATOR_END_PROP, 1);
}
if (!hasFinally)
{
break;
}
// skip the whole mess.
Node unwindBlock = null;
for (int i = loops.Size() - 1; i >= 0; i--)
{
Node n = (Node)loops.Get(i);
int elemtype = n.GetType();
if (elemtype == Token.TRY || elemtype == Token.WITH)
{
Node unwind;
if (elemtype == Token.TRY)
{
Jump jsrnode = new Jump(Token.JSR);
Node jsrtarget = ((Jump)n).GetFinally();
jsrnode.target = jsrtarget;
unwind = jsrnode;
}
else
{
unwind = new Node(Token.LEAVEWITH);
}
if (unwindBlock == null)
{
unwindBlock = new Node(Token.BLOCK, node.GetLineno());
}
unwindBlock.AddChildToBack(unwind);
}
}
if (unwindBlock != null)
{
Node returnNode = node;
Node returnExpr = returnNode.GetFirstChild();
node = ReplaceCurrent(parent, previous, node, unwindBlock);
if (returnExpr == null || isGenerator)
{
unwindBlock.AddChildToBack(returnNode);
}
else
{
Node store = new Node(Token.EXPR_RESULT, returnExpr);
unwindBlock.AddChildToFront(store);
returnNode = new Node(Token.RETURN_RESULT);
unwindBlock.AddChildToBack(returnNode);
// transform return expression
TransformCompilationUnit_r(tree, store, scope, createScopeObjects, inStrictMode);
}
// skip transformCompilationUnit_r to avoid infinite loop
goto siblingLoop_continue;
}
break;
}
case Token.BREAK:
case Token.CONTINUE:
{
Jump jump = (Jump)node;
Jump jumpStatement = jump.GetJumpStatement();
if (jumpStatement == null)
{
Kit.CodeBug();
}
for (int i = loops.Size(); ; )
{
if (i == 0)
{
// Parser/IRFactory ensure that break/continue
// always has a jump statement associated with it
// which should be found
throw Kit.CodeBug();
}
--i;
Node n = (Node)loops.Get(i);
if (n == jumpStatement)
{
break;
}
int elemtype = n.GetType();
if (elemtype == Token.WITH)
{
Node leave = new Node(Token.LEAVEWITH);
previous = AddBeforeCurrent(parent, previous, node, leave);
}
else
{
if (elemtype == Token.TRY)
{
Jump tryNode = (Jump)n;
Jump jsrFinally = new Jump(Token.JSR);
jsrFinally.target = tryNode.GetFinally();
previous = AddBeforeCurrent(parent, previous, node, jsrFinally);
}
}
}
if (type == Token.BREAK)
{
jump.target = jumpStatement.target;
}
else
{
jump.target = jumpStatement.GetContinue();
}
jump.SetType(Token.GOTO);
break;
}
case Token.CALL:
{
VisitCall(node, tree);
break;
}
case Token.NEW:
{
VisitNew(node, tree);
break;
}
case Token.LETEXPR:
case Token.LET:
{
Node child = node.GetFirstChild();
if (child.GetType() == Token.LET)
{
// We have a let statement or expression rather than a
// let declaration
bool createWith = tree.GetType() != Token.FUNCTION || ((FunctionNode)tree).RequiresActivation();
node = VisitLet(createWith, parent, previous, node);
break;
}
goto case Token.CONST;
}
case Token.CONST:
case Token.VAR:
{
// fall through to process let declaration...
Node result = new Node(Token.BLOCK);
for (Node cursor = node.GetFirstChild(); cursor != null; )
{
// Move cursor to next before createAssignment gets chance
// to change n.next
Node n = cursor;
cursor = cursor.GetNext();
if (n.GetType() == Token.NAME)
{
if (!n.HasChildren())
{
continue;
}
Node init = n.GetFirstChild();
n.RemoveChild(init);
n.SetType(Token.BINDNAME);
n = new Node(type == Token.CONST ? Token.SETCONST : Token.SETNAME, n, init);
}
else
{
// May be a destructuring assignment already transformed
// to a LETEXPR
if (n.GetType() != Token.LETEXPR)
{
throw Kit.CodeBug();
}
}
Node pop = new Node(Token.EXPR_VOID, n, node.GetLineno());
result.AddChildToBack(pop);
}
node = ReplaceCurrent(parent, previous, node, result);
break;
}
case Token.TYPEOFNAME:
{
Scope defining = scope.GetDefiningScope(node.GetString());
if (defining != null)
{
node.SetScope(defining);
}
break;
}
case Token.TYPEOF:
case Token.IFNE:
{
Node child = node.GetFirstChild();
if (type == Token.IFNE)
{
while (child.GetType() == Token.NOT)
{
child = child.GetFirstChild();
}
if (child.GetType() == Token.EQ || child.GetType() == Token.NE)
{
Node first = child.GetFirstChild();
Node last = child.GetLastChild();
if (first.GetType() == Token.NAME && first.GetString().Equals("undefined"))
{
child = last;
}
else
{
if (last.GetType() == Token.NAME && last.GetString().Equals("undefined"))
{
child = first;
}
}
}
}
if (child.GetType() == Token.GETPROP)
{
child.SetType(Token.GETPROPNOWARN);
}
break;
}
case Token.SETNAME:
{
if (inStrictMode)
{
node.SetType(Token.STRICT_SETNAME);
}
goto case Token.NAME;
}
case Token.NAME:
case Token.SETCONST:
case Token.DELPROP:
{
// Turn name to var for faster access if possible
if (createScopeObjects)
{
break;
}
Node nameSource;
if (type == Token.NAME)
{
nameSource = node;
}
else
{
nameSource = node.GetFirstChild();
if (nameSource.GetType() != Token.BINDNAME)
{
if (type == Token.DELPROP)
{
break;
}
throw Kit.CodeBug();
}
}
if (nameSource.GetScope() != null)
{
break;
}
// already have a scope set
string name = nameSource.GetString();
Scope defining = scope.GetDefiningScope(name);
if (defining != null)
{
nameSource.SetScope(defining);
if (type == Token.NAME)
{
node.SetType(Token.GETVAR);
}
else
{
if (type == Token.SETNAME || type == Token.STRICT_SETNAME)
{
node.SetType(Token.SETVAR);
nameSource.SetType(Token.STRING);
}
else
{
if (type == Token.SETCONST)
{
node.SetType(Token.SETCONSTVAR);
nameSource.SetType(Token.STRING);
}
else
{
if (type == Token.DELPROP)
{
// Local variables are by definition permanent
Node n = new Node(Token.FALSE);
node = ReplaceCurrent(parent, previous, node, n);
}
else
{
throw Kit.CodeBug();
}
}
}
}
}
break;
}
}
TransformCompilationUnit_r(tree, node, node is Scope ? (Scope)node : scope, createScopeObjects, inStrictMode);
siblingLoop_continue: ;
}
siblingLoop_break: ;
}