Ejemplo n.º 1
0
        /// <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;
        }
Ejemplo n.º 2
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:
                    ;
                }
            }
        }