/// <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 /// ... /// </summary> /// <remarks> /// 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. /// </remarks> private Node CreateTryCatchFinally(Node tryBlock, Node catchBlocks, Node finallyBlock, int lineno) { bool hasFinally = (finallyBlock != null) && (finallyBlock.GetType() != Token.BLOCK || finallyBlock.HasChildren()); // short circuit if (tryBlock.GetType() == 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); Jump pn = new 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.GetFirstChild(); bool hasDefault = false; int scopeIndex = 0; while (cb != null) { int catchLineNo = cb.GetLineno(); Node name = cb.GetFirstChild(); Node cond = name.GetNext(); Node catchStatement = cond.GetNext(); 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.GetType() == 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.GetNext(); ++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.SetFinally(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; }
private Node CreateLoop(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.GetType() == Token.EMPTY) { cond = new Node(Token.TRUE); } Jump IFEQ = new 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.GetLineno())); } 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) { int initType = init.GetType(); if (initType != Token.EMPTY) { if (initType != Token.VAR && initType != Token.LET) { init = new Node(Token.EXPR_VOID, init); } loop.AddChildToFront(init); } Node incrTarget = Node.NewTarget(); loop.AddChildAfter(incrTarget, body); if (incr.GetType() != Token.EMPTY) { incr = new Node(Token.EXPR_VOID, incr); loop.AddChildAfter(incr, incrTarget); } continueTarget = incrTarget; } } loop.SetContinue(continueTarget); return loop; }