internal void closeSwitch(Node switchBlock)
        {
            if (switchBlock.Type != Token.BLOCK)
                throw Context.CodeBug ();
            Node.Jump switchNode = (Node.Jump)switchBlock.FirstChild;
            if (switchNode.Type != Token.SWITCH)
                throw Context.CodeBug ();

            Node switchBreakTarget = Node.newTarget ();
            // switchNode.target is only used by NodeTransformer
            // to detect switch end
            switchNode.target = switchBreakTarget;

            Node defaultTarget = switchNode.Default;
            if (defaultTarget == null) {
                defaultTarget = switchBreakTarget;
            }

            switchBlock.addChildAfter (makeJump (Token.GOTO, defaultTarget), switchNode);
            switchBlock.addChildToBack (switchBreakTarget);
        }
        private void transformCompilationUnit_r (ScriptOrFnNode tree, Node parent)
        {
            Node node = null;

            using (Helpers.StackOverflowVerifier sov = new Helpers.StackOverflowVerifier (1024)) {
                for (; ; ) {
                    Node previous = null;
                    if (node == null) {
                        node = parent.FirstChild;
                    }
                    else {
                        previous = node;
                        node = node.Next;
                    }
                    if (node == null) {
                        break;
                    }

                    int type = node.Type;

                    switch (type) {


                        case Token.LABEL:
                        case Token.SWITCH:
                        case Token.LOOP:
                            loops.push (node);
                            loopEnds.push (((Node.Jump)node).target);
                            break;


                        case Token.WITH: {
                                loops.push (node);
                                Node leave = node.Next;
                                if (leave.Type != Token.LEAVEWITH) {
                                    Context.CodeBug ();
                                }
                                loopEnds.push (leave);
                                break;
                            }


                        case Token.TRY: {
                                Node.Jump jump = (Node.Jump)node;
                                Node finallytarget = jump.Finally;
                                if (finallytarget != null) {
                                    hasFinally = true;
                                    loops.push (node);
                                    loopEnds.push (finallytarget);
                                }
                                break;
                            }


                        case Token.TARGET:
                        case Token.LEAVEWITH:
                            if (!loopEnds.Empty && loopEnds.peek () == node) {
                                loopEnds.pop ();
                                loops.pop ();
                            }
                            break;


                        case Token.RETURN: {
                                /* If we didn't support try/finally, it wouldn't be
                                * necessary to put LEAVEWITH nodes here... but as
                                * we do need a series of JSR FINALLY nodes before
                                * each RETURN, we need to ensure that each finally
                                * block gets the correct scope... which could mean
                                * that some LEAVEWITH nodes are necessary.
                                */
                                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.Type;
                                    if (elemtype == Token.TRY || elemtype == Token.WITH) {
                                        Node unwind;
                                        if (elemtype == Token.TRY) {
                                            Node.Jump jsrnode = new Node.Jump (Token.JSR);
                                            Node jsrtarget = ((Node.Jump)n).Finally;
                                            jsrnode.target = jsrtarget;
                                            unwind = jsrnode;
                                        }
                                        else {
                                            unwind = new Node (Token.LEAVEWITH);
                                        }
                                        if (unwindBlock == null) {
                                            unwindBlock = new Node (Token.BLOCK, node.Lineno);
                                        }
                                        unwindBlock.addChildToBack (unwind);
                                    }
                                }
                                if (unwindBlock != null) {
                                    Node returnNode = node;
                                    Node returnExpr = returnNode.FirstChild;
                                    node = replaceCurrent (parent, previous, node, unwindBlock);
                                    if (returnExpr == null) {
                                        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);
                                    }
                                    // skip transformCompilationUnit_r to avoid infinite loop

                                    goto siblingLoop;
                                }
                                break;
                            }


                        case Token.BREAK:
                        case Token.CONTINUE: {
                                Node.Jump jump = (Node.Jump)node;
                                Node.Jump jumpStatement = jump.JumpStatement;
                                if (jumpStatement == null)
                                    Context.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 Context.CodeBug ();
                                    }
                                    --i;
                                    Node n = (Node)loops.Get (i);
                                    if (n == jumpStatement) {
                                        break;
                                    }

                                    int elemtype = n.Type;
                                    if (elemtype == Token.WITH) {
                                        Node leave = new Node (Token.LEAVEWITH);
                                        previous = addBeforeCurrent (parent, previous, node, leave);
                                    }
                                    else if (elemtype == Token.TRY) {
                                        Node.Jump tryNode = (Node.Jump)n;
                                        Node.Jump jsrFinally = new Node.Jump (Token.JSR);
                                        jsrFinally.target = tryNode.Finally;
                                        previous = addBeforeCurrent (parent, previous, node, jsrFinally);
                                    }
                                }

                                if (type == Token.BREAK) {
                                    jump.target = jumpStatement.target;
                                }
                                else {
                                    jump.target = jumpStatement.Continue;
                                }
                                jump.Type = Token.GOTO;

                                break;
                            }


                        case Token.CALL:
                            visitCall (node, tree);
                            break;


                        case Token.NEW:
                            visitNew (node, tree);
                            break;


                        case Token.CONST:
                        case Token.VAR: {
                                Node result = new Node (Token.BLOCK);
                                for (Node cursor = node.FirstChild; cursor != null; ) {
                                    // Move cursor to next before createAssignment get chance
                                    // to change n.next
                                    Node n = cursor;
                                    if (n.Type != Token.NAME)
                                        Context.CodeBug ();
                                    cursor = cursor.Next;
                                    if (!n.hasChildren ())
                                        continue;
                                    Node init = n.FirstChild;
                                    n.removeChild (init);
                                    n.Type = Token.BINDNAME;
                                    n = new Node ((node.Type == Token.VAR) ? Token.SETNAME : Token.SETNAME_CONST, n, init);
                                    Node pop = new Node (Token.EXPR_VOID, n, node.Lineno);
                                    result.addChildToBack (pop);
                                }
                                node = replaceCurrent (parent, previous, node, result);
                                break;
                            }


                        case Token.NAME:
                        case Token.SETNAME:
                        case Token.DELPROP: {
                                // Turn name to var for faster access if possible
                                if (tree.Type != Token.FUNCTION || ((FunctionNode)tree).RequiresActivation) {
                                    break;
                                }
                                Node nameSource;
                                if (type == Token.NAME) {
                                    nameSource = node;
                                }
                                else {
                                    nameSource = node.FirstChild;
                                    if (nameSource.Type != Token.BINDNAME) {
                                        if (type == Token.DELPROP) {
                                            break;
                                        }
                                        throw Context.CodeBug ();
                                    }
                                }
                                string name = nameSource.String;
                                if (tree.hasParamOrVar (name)) {
                                    if (type == Token.NAME) {
                                        node.Type = Token.GETVAR;
                                    }
                                    else if (type == Token.SETNAME) {
                                        node.Type = Token.SETVAR;
                                        nameSource.Type = 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 Context.CodeBug ();
                                    }
                                }
                                break;
                            }
                    }

                    transformCompilationUnit_r (tree, node);

                siblingLoop:
                    ;
                }
            }
        }
 /// <summary> Object Literals
 /// <BR> CreateObjectLiteral rewrites its argument as object
 /// creation plus object property entries, so later compiler
 /// stages don't need to know about object literals.
 /// </summary>
 internal Node CreateObjectLiteral(ObjArray elems)
 {
     int size = elems.size () / 2;
     Node obj = new Node (Token.OBJECTLIT);
     object [] properties;
     if (size == 0) {
         properties = ScriptRuntime.EmptyArgs;
     }
     else {
         properties = new object [size];
         for (int i = 0; i != size; ++i) {
             properties [i] = elems.Get (2 * i);
             Node value = (Node)elems.Get (2 * i + 1);
             obj.addChildToBack (value);
         }
     }
     obj.putProp (Node.OBJECT_IDS_PROP, properties);
     return obj;
 }
        /// <summary> Try/Catch/Finally
        /// 
        /// The IRFactory tries to express as much as possible in the tree;
        /// the responsibilities remaining for Codegen are to add the Java
        /// handlers: (Either (but not both) of TARGET and FINALLY might not
        /// be defined)
        /// - a catch handler for javascript exceptions that unwraps the
        /// exception onto the stack and GOTOes to the catch target
        /// - a finally handler
        /// ... and a goto to GOTO around these handlers.
        /// </summary>
        internal Node CreateTryCatchFinally(Node tryBlock, Node catchBlocks, Node finallyBlock, int lineno)
        {
            bool hasFinally = (finallyBlock != null) && (finallyBlock.Type != Token.BLOCK || finallyBlock.hasChildren ());

            // short circuit
            if (tryBlock.Type == Token.BLOCK && !tryBlock.hasChildren () && !hasFinally) {
                return tryBlock;
            }

            bool hasCatch = catchBlocks.hasChildren ();

            // short circuit
            if (!hasFinally && !hasCatch) {
                // bc finally might be an empty block...
                return tryBlock;
            }

            Node handlerBlock = new Node (Token.LOCAL_BLOCK);
            Node.Jump pn = new Node.Jump (Token.TRY, tryBlock, lineno);
            pn.putProp (Node.LOCAL_BLOCK_PROP, handlerBlock);

            if (hasCatch) {
                // jump around catch code
                Node endCatch = Node.newTarget ();
                pn.addChildToBack (makeJump (Token.GOTO, endCatch));

                // make a TARGET for the catch that the tcf node knows about
                Node catchTarget = Node.newTarget ();
                pn.target = catchTarget;
                // mark it
                pn.addChildToBack (catchTarget);

                //
                //  Given
                //
                //   try {
                //       tryBlock;
                //   } catch (e if condition1) {
                //       something1;
                //   ...
                //
                //   } catch (e if conditionN) {
                //       somethingN;
                //   } catch (e) {
                //       somethingDefault;
                //   }
                //
                //  rewrite as
                //
                //   try {
                //       tryBlock;
                //       goto after_catch:
                //   } catch (x) {
                //       with (newCatchScope(e, x)) {
                //           if (condition1) {
                //               something1;
                //               goto after_catch;
                //           }
                //       }
                //   ...
                //       with (newCatchScope(e, x)) {
                //           if (conditionN) {
                //               somethingN;
                //               goto after_catch;
                //           }
                //       }
                //       with (newCatchScope(e, x)) {
                //           somethingDefault;
                //           goto after_catch;
                //       }
                //   }
                // after_catch:
                //
                // If there is no default catch, then the last with block
                // arround  "somethingDefault;" is replaced by "rethrow;"

                // It is assumed that catch handler generation will store
                // exeception object in handlerBlock register

                // Block with local for exception scope objects
                Node catchScopeBlock = new Node (Token.LOCAL_BLOCK);

                // expects catchblocks children to be (cond block) pairs.
                Node cb = catchBlocks.FirstChild;
                bool hasDefault = false;
                int scopeIndex = 0;
                while (cb != null) {
                    int catchLineNo = cb.Lineno;

                    Node name = cb.FirstChild;
                    Node cond = name.Next;
                    Node catchStatement = cond.Next;
                    cb.removeChild (name);
                    cb.removeChild (cond);
                    cb.removeChild (catchStatement);

                    // Add goto to the catch statement to jump out of catch
                    // but prefix it with LEAVEWITH since try..catch produces
                    // "with"code in order to limit the scope of the exception
                    // object.
                    catchStatement.addChildToBack (new Node (Token.LEAVEWITH));
                    catchStatement.addChildToBack (makeJump (Token.GOTO, endCatch));

                    // Create condition "if" when present
                    Node condStmt;
                    if (cond.Type == Token.EMPTY) {
                        condStmt = catchStatement;
                        hasDefault = true;
                    }
                    else {
                        condStmt = CreateIf (cond, catchStatement, null, catchLineNo);
                    }

                    // Generate code to Create the scope object and store
                    // it in catchScopeBlock register
                    Node catchScope = new Node (Token.CATCH_SCOPE, name, CreateUseLocal (handlerBlock));
                    catchScope.putProp (Node.LOCAL_BLOCK_PROP, catchScopeBlock);
                    catchScope.putIntProp (Node.CATCH_SCOPE_PROP, scopeIndex);
                    catchScopeBlock.addChildToBack (catchScope);

                    // Add with statement based on catch scope object
                    catchScopeBlock.addChildToBack (CreateWith (CreateUseLocal (catchScopeBlock), condStmt, catchLineNo));

                    // move to next cb
                    cb = cb.Next;
                    ++scopeIndex;
                }
                pn.addChildToBack (catchScopeBlock);
                if (!hasDefault) {
                    // Generate code to rethrow if no catch clause was executed
                    Node rethrow = new Node (Token.RETHROW);
                    rethrow.putProp (Node.LOCAL_BLOCK_PROP, handlerBlock);
                    pn.addChildToBack (rethrow);
                }

                pn.addChildToBack (endCatch);
            }

            if (hasFinally) {
                Node finallyTarget = Node.newTarget ();
                pn.Finally = finallyTarget;

                // add jsr finally to the try block
                pn.addChildToBack (makeJump (Token.JSR, finallyTarget));

                // jump around finally code
                Node finallyEnd = Node.newTarget ();
                pn.addChildToBack (makeJump (Token.GOTO, finallyEnd));

                pn.addChildToBack (finallyTarget);
                Node fBlock = new Node (Token.FINALLY, finallyBlock);
                fBlock.putProp (Node.LOCAL_BLOCK_PROP, handlerBlock);
                pn.addChildToBack (fBlock);

                pn.addChildToBack (finallyEnd);
            }
            handlerBlock.addChildToBack (pn);
            return handlerBlock;
        }
 /// <summary> Add a child to the back of the given node.  This function
 /// breaks the Factory abstraction, but it removes a requirement
 /// from implementors of Node.
 /// </summary>
 internal void addChildToBack(Node parent, Node child)
 {
     parent.addChildToBack (child);
 }
        /// <summary> If caseExpression argument is null it indicate default label.</summary>
        internal void addSwitchCase(Node switchBlock, Node caseExpression, Node statements)
        {
            if (switchBlock.Type != Token.BLOCK)
                throw Context.CodeBug ();
            Node.Jump switchNode = (Node.Jump)switchBlock.FirstChild;
            if (switchNode.Type != Token.SWITCH)
                throw Context.CodeBug ();

            Node gotoTarget = Node.newTarget ();
            if (caseExpression != null) {
                Node.Jump caseNode = new Node.Jump (Token.CASE, caseExpression);
                caseNode.target = gotoTarget;
                switchNode.addChildToBack (caseNode);
            }
            else {
                switchNode.Default = gotoTarget;
            }
            switchBlock.addChildToBack (gotoTarget);
            switchBlock.addChildToBack (statements);
        }
        /// <summary> For .. In
        /// 
        /// </summary>
        internal Node CreateForIn(Node loop, Node lhs, Node obj, Node body, bool isForEach)
        {
            int type = lhs.Type;

            Node lvalue;
            if (type == Token.VAR) {
                /*
                * check that there was only one variable given.
                * we can't do this in the parser, because then the
                * parser would have to know something about the
                * 'init' node of the for-in loop.
                */
                Node lastChild = lhs.LastChild;
                if (lhs.FirstChild != lastChild) {
                    parser.ReportError ("msg.mult.index");
                }
                lvalue = Node.newString (Token.NAME, lastChild.String);
            }
            else {
                lvalue = makeReference (lhs);
                if (lvalue == null) {
                    parser.ReportError ("msg.bad.for.in.lhs");
                    return obj;
                }
            }

            Node localBlock = new Node (Token.LOCAL_BLOCK);

            int initType = (isForEach) ? Token.ENUM_INIT_VALUES : Token.ENUM_INIT_KEYS;
            Node init = new Node (initType, obj);
            init.putProp (Node.LOCAL_BLOCK_PROP, localBlock);
            Node cond = new Node (Token.ENUM_NEXT);
            cond.putProp (Node.LOCAL_BLOCK_PROP, localBlock);
            Node id = new Node (Token.ENUM_ID);
            id.putProp (Node.LOCAL_BLOCK_PROP, localBlock);

            Node newBody = new Node (Token.BLOCK);
            Node assign = simpleAssignment (lvalue, id);
            newBody.addChildToBack (new Node (Token.EXPR_VOID, assign));
            newBody.addChildToBack (body);

            loop = CreateWhile (loop, cond, newBody);
            loop.addChildToFront (init);
            if (type == Token.VAR)
                loop.addChildToFront (lhs);
            localBlock.addChildToBack (loop);

            return localBlock;
        }
        /// <summary> If statement</summary>
        internal Node CreateIf(Node cond, Node ifTrue, Node ifFalse, int lineno)
        {
            int condStatus = isAlwaysDefinedBoolean (cond);
            if (condStatus == ALWAYS_TRUE_BOOLEAN) {
                return ifTrue;
            }
            else if (condStatus == ALWAYS_FALSE_BOOLEAN) {
                if (ifFalse != null) {
                    return ifFalse;
                }
                return new Node (Token.BLOCK, lineno);
            }

            Node result = new Node (Token.BLOCK, lineno);
            Node ifNotTarget = Node.newTarget ();
            Node.Jump IFNE = new Node.Jump (Token.IFNE, cond);
            IFNE.target = ifNotTarget;

            result.addChildToBack (IFNE);
            result.addChildrenToBack (ifTrue);

            if (ifFalse != null) {
                Node endTarget = Node.newTarget ();
                result.addChildToBack (makeJump (Token.GOTO, endTarget));
                result.addChildToBack (ifNotTarget);
                result.addChildrenToBack (ifFalse);
                result.addChildToBack (endTarget);
            }
            else {
                result.addChildToBack (ifNotTarget);
            }

            return result;
        }
        private Node CreateLoop(Node.Jump loop, int loopType, Node body, Node cond, Node init, Node incr)
        {
            Node bodyTarget = Node.newTarget ();
            Node condTarget = Node.newTarget ();
            if (loopType == LOOP_FOR && cond.Type == Token.EMPTY) {
                cond = new Node (Token.TRUE);
            }
            Node.Jump IFEQ = new Node.Jump (Token.IFEQ, cond);
            IFEQ.target = bodyTarget;
            Node breakTarget = Node.newTarget ();

            loop.addChildToBack (bodyTarget);
            loop.addChildrenToBack (body);
            if (loopType == LOOP_WHILE || loopType == LOOP_FOR) {
                // propagate lineno to condition
                loop.addChildrenToBack (new Node (Token.EMPTY, loop.Lineno));
            }
            loop.addChildToBack (condTarget);
            loop.addChildToBack (IFEQ);
            loop.addChildToBack (breakTarget);

            loop.target = breakTarget;
            Node continueTarget = condTarget;

            if (loopType == LOOP_WHILE || loopType == LOOP_FOR) {
                // Just add a GOTO to the condition in the do..while
                loop.addChildToFront (makeJump (Token.GOTO, condTarget));

                if (loopType == LOOP_FOR) {
                    if (init.Type != Token.EMPTY) {
                        if (init.Type != Token.VAR) {
                            init = new Node (Token.EXPR_VOID, init);
                        }
                        loop.addChildToFront (init);
                    }
                    Node incrTarget = Node.newTarget ();
                    loop.addChildAfter (incrTarget, body);
                    if (incr.Type != Token.EMPTY) {
                        incr = new Node (Token.EXPR_VOID, incr);
                        loop.addChildAfter (incr, incrTarget);
                    }
                    continueTarget = incrTarget;
                }
            }

            loop.Continue = continueTarget;

            return loop;
        }
        internal Node initFunction(FunctionNode fnNode, int functionIndex, Node statements, int functionType)
        {
            fnNode.itsFunctionType = functionType;
            fnNode.addChildToBack (statements);

            int functionCount = fnNode.FunctionCount;
            if (functionCount != 0) {
                // Functions containing other functions require activation objects
                fnNode.itsNeedsActivation = true;
                for (int i = 0; i != functionCount; ++i) {
                    FunctionNode fn = fnNode.getFunctionNode (i);
                    // nested function expression statements overrides var
                    if (fn.FunctionType == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) {
                        string name = fn.FunctionName;
                        if (name != null && name.Length != 0) {
                            fnNode.removeParamOrVar (name);
                        }
                    }
                }
            }

            if (functionType == FunctionNode.FUNCTION_EXPRESSION) {
                string name = fnNode.FunctionName;
                if (name != null && name.Length != 0 && !fnNode.hasParamOrVar (name)) {
                    // A function expression needs to have its name as a
                    // variable (if it isn't already allocated as a variable).
                    // See ECMA Ch. 13.  We add code to the beginning of the
                    // function to initialize a local variable of the
                    // function's name to the function value.
                    fnNode.addVar (name);
                    Node setFn = new Node (Token.EXPR_VOID, new Node (Token.SETNAME, Node.newString (Token.BINDNAME, name), new Node (Token.THISFN)));
                    statements.addChildrenToFront (setFn);
                }
            }

            // Add return to end if needed.
            Node lastStmt = statements.LastChild;
            if (lastStmt == null || lastStmt.Type != Token.RETURN) {
                statements.addChildToBack (new Node (Token.RETURN));
            }

            Node result = Node.newString (Token.FUNCTION, fnNode.FunctionName);
            result.putIntProp (Node.FUNCTION_PROP, functionIndex);
            return result;
        }
 /// <summary> Throw, Return, Label, Break and Continue are defined in ASTFactory.</summary>
 /// <summary> With</summary>
 internal Node CreateWith(Node obj, Node body, int lineno)
 {
     setRequiresActivation ();
     Node result = new Node (Token.BLOCK, lineno);
     result.addChildToBack (new Node (Token.ENTERWITH, obj));
     Node bodyNode = new Node (Token.WITH, body, lineno);
     result.addChildrenToBack (bodyNode);
     result.addChildToBack (new Node (Token.LEAVEWITH));
     return result;
 }
 internal Node CreateArrayLiteral(ObjArray elems, int skipCount)
 {
     int length = elems.size ();
     int [] skipIndexes = null;
     if (skipCount != 0) {
         skipIndexes = new int [skipCount];
     }
     Node array = new Node (Token.ARRAYLIT);
     for (int i = 0, j = 0; i != length; ++i) {
         Node elem = (Node)elems.Get (i);
         if (elem != null) {
             array.addChildToBack (elem);
         }
         else {
             skipIndexes [j] = i;
             ++j;
         }
     }
     if (skipCount != 0) {
         array.putProp (Node.SKIP_INDEXES_PROP, skipIndexes);
     }
     return array;
 }
Exemple #13
0
        private void transformCompilationUnit_r(ScriptOrFnNode tree, Node parent)
        {
            Node node = null;

            using (Helpers.StackOverflowVerifier sov = new Helpers.StackOverflowVerifier(1024)) {
                for (; ;)
                {
                    Node previous = null;
                    if (node == null)
                    {
                        node = parent.FirstChild;
                    }
                    else
                    {
                        previous = node;
                        node     = node.Next;
                    }
                    if (node == null)
                    {
                        break;
                    }

                    int type = node.Type;

                    switch (type)
                    {
                    case Token.LABEL:
                    case Token.SWITCH:
                    case Token.LOOP:
                        loops.push(node);
                        loopEnds.push(((Node.Jump)node).target);
                        break;


                    case Token.WITH: {
                        loops.push(node);
                        Node leave = node.Next;
                        if (leave.Type != Token.LEAVEWITH)
                        {
                            Context.CodeBug();
                        }
                        loopEnds.push(leave);
                        break;
                    }


                    case Token.TRY: {
                        Node.Jump jump          = (Node.Jump)node;
                        Node      finallytarget = jump.Finally;
                        if (finallytarget != null)
                        {
                            hasFinally = true;
                            loops.push(node);
                            loopEnds.push(finallytarget);
                        }
                        break;
                    }


                    case Token.TARGET:
                    case Token.LEAVEWITH:
                        if (!loopEnds.Empty && loopEnds.peek() == node)
                        {
                            loopEnds.pop();
                            loops.pop();
                        }
                        break;


                    case Token.RETURN: {
                        /* If we didn't support try/finally, it wouldn't be
                         * necessary to put LEAVEWITH nodes here... but as
                         * we do need a series of JSR FINALLY nodes before
                         * each RETURN, we need to ensure that each finally
                         * block gets the correct scope... which could mean
                         * that some LEAVEWITH nodes are necessary.
                         */
                        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.Type;
                            if (elemtype == Token.TRY || elemtype == Token.WITH)
                            {
                                Node unwind;
                                if (elemtype == Token.TRY)
                                {
                                    Node.Jump jsrnode   = new Node.Jump(Token.JSR);
                                    Node      jsrtarget = ((Node.Jump)n).Finally;
                                    jsrnode.target = jsrtarget;
                                    unwind         = jsrnode;
                                }
                                else
                                {
                                    unwind = new Node(Token.LEAVEWITH);
                                }
                                if (unwindBlock == null)
                                {
                                    unwindBlock = new Node(Token.BLOCK, node.Lineno);
                                }
                                unwindBlock.addChildToBack(unwind);
                            }
                        }
                        if (unwindBlock != null)
                        {
                            Node returnNode = node;
                            Node returnExpr = returnNode.FirstChild;
                            node = replaceCurrent(parent, previous, node, unwindBlock);
                            if (returnExpr == null)
                            {
                                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);
                            }
                            // skip transformCompilationUnit_r to avoid infinite loop

                            goto siblingLoop;
                        }
                        break;
                    }


                    case Token.BREAK:
                    case Token.CONTINUE: {
                        Node.Jump jump          = (Node.Jump)node;
                        Node.Jump jumpStatement = jump.JumpStatement;
                        if (jumpStatement == null)
                        {
                            Context.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 Context.CodeBug();
                            }
                            --i;
                            Node n = (Node)loops.Get(i);
                            if (n == jumpStatement)
                            {
                                break;
                            }

                            int elemtype = n.Type;
                            if (elemtype == Token.WITH)
                            {
                                Node leave = new Node(Token.LEAVEWITH);
                                previous = addBeforeCurrent(parent, previous, node, leave);
                            }
                            else if (elemtype == Token.TRY)
                            {
                                Node.Jump tryNode    = (Node.Jump)n;
                                Node.Jump jsrFinally = new Node.Jump(Token.JSR);
                                jsrFinally.target = tryNode.Finally;
                                previous          = addBeforeCurrent(parent, previous, node, jsrFinally);
                            }
                        }

                        if (type == Token.BREAK)
                        {
                            jump.target = jumpStatement.target;
                        }
                        else
                        {
                            jump.target = jumpStatement.Continue;
                        }
                        jump.Type = Token.GOTO;

                        break;
                    }


                    case Token.CALL:
                        visitCall(node, tree);
                        break;


                    case Token.NEW:
                        visitNew(node, tree);
                        break;


                    case Token.VAR: {
                        Node result = new Node(Token.BLOCK);
                        for (Node cursor = node.FirstChild; cursor != null;)
                        {
                            // Move cursor to next before createAssignment get chance
                            // to change n.next
                            Node n = cursor;
                            if (n.Type != Token.NAME)
                            {
                                Context.CodeBug();
                            }
                            cursor = cursor.Next;
                            if (!n.hasChildren())
                            {
                                continue;
                            }
                            Node init = n.FirstChild;
                            n.removeChild(init);
                            n.Type = Token.BINDNAME;
                            n      = new Node(Token.SETNAME, n, init);
                            Node pop = new Node(Token.EXPR_VOID, n, node.Lineno);
                            result.addChildToBack(pop);
                        }
                        node = replaceCurrent(parent, previous, node, result);
                        break;
                    }


                    case Token.NAME:
                    case Token.SETNAME:
                    case Token.DELPROP: {
                        // Turn name to var for faster access if possible
                        if (tree.Type != Token.FUNCTION || ((FunctionNode)tree).RequiresActivation)
                        {
                            break;
                        }
                        Node nameSource;
                        if (type == Token.NAME)
                        {
                            nameSource = node;
                        }
                        else
                        {
                            nameSource = node.FirstChild;
                            if (nameSource.Type != Token.BINDNAME)
                            {
                                if (type == Token.DELPROP)
                                {
                                    break;
                                }
                                throw Context.CodeBug();
                            }
                        }
                        string name = nameSource.String;
                        if (tree.hasParamOrVar(name))
                        {
                            if (type == Token.NAME)
                            {
                                node.Type = Token.GETVAR;
                            }
                            else if (type == Token.SETNAME)
                            {
                                node.Type       = Token.SETVAR;
                                nameSource.Type = 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 Context.CodeBug();
                            }
                        }
                        break;
                    }
                    }

                    transformCompilationUnit_r(tree, node);

siblingLoop:
                    ;
                }
            }
        }