/// <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 CreatePropertyGet(Node target, string ns, string name, int memberTypeFlags) { if (ns == null && memberTypeFlags == 0) { if (target == null) { return CreateName (name); } checkActivationName (name, Token.GETPROP); if (ScriptRuntime.isSpecialProperty (name)) { Node rf = new Node (Token.REF_SPECIAL, target); rf.putProp (Node.NAME_PROP, name); return new Node (Token.GET_REF, rf); } return new Node (Token.GETPROP, target, CreateString (name)); } Node elem = CreateString (name); memberTypeFlags |= Node.PROPERTY_FLAG; return CreateMemberRefGet (target, ns, elem, memberTypeFlags); }
/// <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> 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; }
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; }
internal Node CreateUseLocal(Node localBlock) { if (Token.LOCAL_BLOCK != localBlock.Type) throw Context.CodeBug (); Node result = new Node (Token.LOCAL_LOAD); result.putProp (Node.LOCAL_BLOCK_PROP, localBlock); return result; }