void VisitStatement (Node node) { int type = node.Type; Node child = node.FirstChild; switch (type) { case Token.FUNCTION: { int fnIndex = node.getExistingIntProp (Node.FUNCTION_PROP); int fnType = scriptOrFn.getFunctionNode (fnIndex).FunctionType; // Only function expressions or function expression // statements needs closure code creating new function // object on stack as function statements are initialized // at script/function start // In addition function expression can not present here // at statement level, they must only present as expressions. if (fnType == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) { addIndexOp (Icode_CLOSURE_STMT, fnIndex); } else { if (fnType != FunctionNode.FUNCTION_STATEMENT) { throw Context.CodeBug (); } } } break; case Token.SCRIPT: case Token.LABEL: case Token.LOOP: case Token.BLOCK: case Token.EMPTY: case Token.WITH: updateLineNumber (node); while (child != null) { VisitStatement (child); child = child.Next; } break; case Token.ENTERWITH: VisitExpression (child, 0); addToken (Token.ENTERWITH); stackChange (-1); break; case Token.LEAVEWITH: addToken (Token.LEAVEWITH); break; case Token.LOCAL_BLOCK: { int local = allocLocal (); node.putIntProp (Node.LOCAL_PROP, local); updateLineNumber (node); while (child != null) { VisitStatement (child); child = child.Next; } addIndexOp (Icode_LOCAL_CLEAR, local); releaseLocal (local); } break; case Token.DEBUGGER: updateLineNumber (node); addIcode (Icode_DEBUGGER); break; case Token.SWITCH: updateLineNumber (node); { // See comments in IRFactory.createSwitch() for description // of SWITCH node Node switchNode = (Node.Jump)node; VisitExpression (child, 0); for (Node.Jump caseNode = (Node.Jump)child.Next; caseNode != null; caseNode = (Node.Jump)caseNode.Next) { if (caseNode.Type != Token.CASE) throw badTree (caseNode); Node test = caseNode.FirstChild; addIcode (Icode_DUP); stackChange (1); VisitExpression (test, 0); addToken (Token.SHEQ); stackChange (-1); // If true, Icode_IFEQ_POP will jump and remove case // value from stack addGoto (caseNode.target, Icode_IFEQ_POP); stackChange (-1); } addIcode (Icode_POP); stackChange (-1); } break; case Token.TARGET: markTargetLabel (node); break; case Token.IFEQ: case Token.IFNE: { Node target = ((Node.Jump)node).target; VisitExpression (child, 0); addGoto (target, type); stackChange (-1); } break; case Token.GOTO: { Node target = ((Node.Jump)node).target; addGoto (target, type); } break; case Token.JSR: { Node target = ((Node.Jump)node).target; addGoto (target, Icode_GOSUB); } break; case Token.FINALLY: { // Account for incomming GOTOSUB address stackChange (1); int finallyRegister = getLocalBlockRef (node); addIndexOp (Icode_STARTSUB, finallyRegister); stackChange (-1); while (child != null) { VisitStatement (child); child = child.Next; } addIndexOp (Icode_RETSUB, finallyRegister); } break; case Token.EXPR_VOID: case Token.EXPR_RESULT: updateLineNumber (node); VisitExpression (child, 0); addIcode ((type == Token.EXPR_VOID) ? Icode_POP : Icode_POP_RESULT); stackChange (-1); break; case Token.TRY: { Node.Jump tryNode = (Node.Jump)node; int exceptionObjectLocal = getLocalBlockRef (tryNode); int scopeLocal = allocLocal (); addIndexOp (Icode_SCOPE_SAVE, scopeLocal); int tryStart = itsICodeTop; while (child != null) { VisitStatement (child); child = child.Next; } Node catchTarget = tryNode.target; if (catchTarget != null) { int catchStartPC = itsLabelTable [getTargetLabel (catchTarget)]; addExceptionHandler (tryStart, catchStartPC, catchStartPC, false, exceptionObjectLocal, scopeLocal); } Node finallyTarget = tryNode.Finally; if (finallyTarget != null) { int finallyStartPC = itsLabelTable [getTargetLabel (finallyTarget)]; addExceptionHandler (tryStart, finallyStartPC, finallyStartPC, true, exceptionObjectLocal, scopeLocal); } addIndexOp (Icode_LOCAL_CLEAR, scopeLocal); releaseLocal (scopeLocal); } break; case Token.CATCH_SCOPE: { int localIndex = getLocalBlockRef (node); int scopeIndex = node.getExistingIntProp (Node.CATCH_SCOPE_PROP); string name = child.String; child = child.Next; VisitExpression (child, 0); // load expression object addStringPrefix (name); addIndexPrefix (localIndex); addToken (Token.CATCH_SCOPE); addUint8 (scopeIndex != 0 ? 1 : 0); stackChange (-1); } break; case Token.THROW: updateLineNumber (node); VisitExpression (child, 0); addToken (Token.THROW); addUint16 (itsLineNumber & 0xFFFF); stackChange (-1); break; case Token.RETHROW: updateLineNumber (node); addIndexOp (Token.RETHROW, getLocalBlockRef (node)); break; case Token.RETURN: updateLineNumber (node); if (child != null) { VisitExpression (child, ECF_TAIL); addToken (Token.RETURN); stackChange (-1); } else { addIcode (Icode_RETUNDEF); } break; case Token.RETURN_RESULT: updateLineNumber (node); addToken (Token.RETURN_RESULT); break; case Token.ENUM_INIT_KEYS: case Token.ENUM_INIT_VALUES: VisitExpression (child, 0); addIndexOp (type, getLocalBlockRef (node)); stackChange (-1); break; default: throw badTree (node); } if (itsStackDepth != 0) { throw Context.CodeBug (); } }
/// <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; }
internal Node CreateIncDec(int nodeType, bool post, Node child) { child = makeReference (child); if (child == null) { string msg; if (nodeType == Token.DEC) { msg = "msg.bad.decr"; } else { msg = "msg.bad.incr"; } parser.ReportError (msg); return null; } int childType = child.Type; switch (childType) { case Token.NAME: case Token.GETPROP: case Token.GETELEM: case Token.GET_REF: { Node n = new Node (nodeType, child); int incrDecrMask = 0; if (nodeType == Token.DEC) { incrDecrMask |= Node.DECR_FLAG; } if (post) { incrDecrMask |= Node.POST_FLAG; } n.putIntProp (Node.INCRDECR_PROP, incrDecrMask); return n; } } throw Context.CodeBug (); }
/// <summary> Regular expressions</summary> internal Node CreateRegExp(int regexpIndex) { Node n = new Node (Token.REGEXP); n.putIntProp (Node.REGEXP_PROP, regexpIndex); return n; }
internal Node CreateCallOrNew(int nodeType, Node child) { int type = Node.NON_SPECIALCALL; if (child.Type == Token.NAME) { string name = child.String; if (name.Equals ("eval")) { type = Node.SPECIALCALL_EVAL; } else if (name.Equals ("With")) { type = Node.SPECIALCALL_WITH; } } else if (child.Type == Token.GETPROP) { string name = child.LastChild.String; if (name.Equals ("eval")) { type = Node.SPECIALCALL_EVAL; } } Node node = new Node (nodeType, child); if (type != Node.NON_SPECIALCALL) { // Calls to these functions require activation objects. setRequiresActivation (); node.putIntProp (Node.SPECIALCALL_PROP, type); } return node; }