private int RewriteForNumberVariables(Node n, int desired) { switch (n.GetType()) { case Token.EXPR_VOID: { Node child = n.GetFirstChild(); int type = RewriteForNumberVariables(child, NumberType); if (type == NumberType) { n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); } return NoType; } case Token.NUMBER: { n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); return NumberType; } case Token.GETVAR: { int varIndex = theFunction.GetVarIndex(n); if (inDirectCallFunction && theFunction.IsParameter(varIndex) && desired == NumberType) { n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); return NumberType; } else { if (theFunction.IsNumberVar(varIndex)) { n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); return NumberType; } } return NoType; } case Token.INC: case Token.DEC: { Node child = n.GetFirstChild(); int type = RewriteForNumberVariables(child, NumberType); if (child.GetType() == Token.GETVAR) { if (type == NumberType && !ConvertParameter(child)) { n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); MarkDCPNumberContext(child); return NumberType; } return NoType; } else { if (child.GetType() == Token.GETELEM || child.GetType() == Token.GETPROP) { return type; } } return NoType; } case Token.SETVAR: { Node lChild = n.GetFirstChild(); Node rChild = lChild.GetNext(); int rType = RewriteForNumberVariables(rChild, NumberType); int varIndex = theFunction.GetVarIndex(n); if (inDirectCallFunction && theFunction.IsParameter(varIndex)) { if (rType == NumberType) { if (!ConvertParameter(rChild)) { n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); return NumberType; } MarkDCPNumberContext(rChild); return NoType; } else { return rType; } } else { if (theFunction.IsNumberVar(varIndex)) { if (rType != NumberType) { n.RemoveChild(rChild); n.AddChildToBack(new Node(Token.TO_DOUBLE, rChild)); } n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); MarkDCPNumberContext(rChild); return NumberType; } else { if (rType == NumberType) { if (!ConvertParameter(rChild)) { n.RemoveChild(rChild); n.AddChildToBack(new Node(Token.TO_OBJECT, rChild)); } } return NoType; } } goto case Token.LE; } case Token.LE: case Token.LT: case Token.GE: case Token.GT: { Node lChild = n.GetFirstChild(); Node rChild = lChild.GetNext(); int lType = RewriteForNumberVariables(lChild, NumberType); int rType = RewriteForNumberVariables(rChild, NumberType); MarkDCPNumberContext(lChild); MarkDCPNumberContext(rChild); if (ConvertParameter(lChild)) { if (ConvertParameter(rChild)) { return NoType; } else { if (rType == NumberType) { n.PutIntProp(Node.ISNUMBER_PROP, Node.RIGHT); } } } else { if (ConvertParameter(rChild)) { if (lType == NumberType) { n.PutIntProp(Node.ISNUMBER_PROP, Node.LEFT); } } else { if (lType == NumberType) { if (rType == NumberType) { n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); } else { n.PutIntProp(Node.ISNUMBER_PROP, Node.LEFT); } } else { if (rType == NumberType) { n.PutIntProp(Node.ISNUMBER_PROP, Node.RIGHT); } } } } // we actually build a boolean value return NoType; } case Token.ADD: { Node lChild = n.GetFirstChild(); Node rChild = lChild.GetNext(); int lType = RewriteForNumberVariables(lChild, NumberType); int rType = RewriteForNumberVariables(rChild, NumberType); if (ConvertParameter(lChild)) { if (ConvertParameter(rChild)) { return NoType; } else { if (rType == NumberType) { n.PutIntProp(Node.ISNUMBER_PROP, Node.RIGHT); } } } else { if (ConvertParameter(rChild)) { if (lType == NumberType) { n.PutIntProp(Node.ISNUMBER_PROP, Node.LEFT); } } else { if (lType == NumberType) { if (rType == NumberType) { n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); return NumberType; } else { n.PutIntProp(Node.ISNUMBER_PROP, Node.LEFT); } } else { if (rType == NumberType) { n.PutIntProp(Node.ISNUMBER_PROP, Node.RIGHT); } } } } return NoType; } case Token.BITXOR: case Token.BITOR: case Token.BITAND: case Token.RSH: case Token.LSH: case Token.SUB: case Token.MUL: case Token.DIV: case Token.MOD: { Node lChild = n.GetFirstChild(); Node rChild = lChild.GetNext(); int lType = RewriteForNumberVariables(lChild, NumberType); int rType = RewriteForNumberVariables(rChild, NumberType); MarkDCPNumberContext(lChild); MarkDCPNumberContext(rChild); if (lType == NumberType) { if (rType == NumberType) { n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); return NumberType; } else { if (!ConvertParameter(rChild)) { n.RemoveChild(rChild); n.AddChildToBack(new Node(Token.TO_DOUBLE, rChild)); n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); } return NumberType; } } else { if (rType == NumberType) { if (!ConvertParameter(lChild)) { n.RemoveChild(lChild); n.AddChildToFront(new Node(Token.TO_DOUBLE, lChild)); n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); } return NumberType; } else { if (!ConvertParameter(lChild)) { n.RemoveChild(lChild); n.AddChildToFront(new Node(Token.TO_DOUBLE, lChild)); } if (!ConvertParameter(rChild)) { n.RemoveChild(rChild); n.AddChildToBack(new Node(Token.TO_DOUBLE, rChild)); } n.PutIntProp(Node.ISNUMBER_PROP, Node.BOTH); return NumberType; } } goto case Token.SETELEM; } case Token.SETELEM: case Token.SETELEM_OP: { Node arrayBase = n.GetFirstChild(); Node arrayIndex = arrayBase.GetNext(); Node rValue = arrayIndex.GetNext(); int baseType = RewriteForNumberVariables(arrayBase, NumberType); if (baseType == NumberType) { if (!ConvertParameter(arrayBase)) { n.RemoveChild(arrayBase); n.AddChildToFront(new Node(Token.TO_OBJECT, arrayBase)); } } int indexType = RewriteForNumberVariables(arrayIndex, NumberType); if (indexType == NumberType) { if (!ConvertParameter(arrayIndex)) { // setting the ISNUMBER_PROP signals the codegen // to use the OptRuntime.setObjectIndex that takes // a double index n.PutIntProp(Node.ISNUMBER_PROP, Node.LEFT); } } int rValueType = RewriteForNumberVariables(rValue, NumberType); if (rValueType == NumberType) { if (!ConvertParameter(rValue)) { n.RemoveChild(rValue); n.AddChildToBack(new Node(Token.TO_OBJECT, rValue)); } } return NoType; } case Token.GETELEM: { Node arrayBase = n.GetFirstChild(); Node arrayIndex = arrayBase.GetNext(); int baseType = RewriteForNumberVariables(arrayBase, NumberType); if (baseType == NumberType) { if (!ConvertParameter(arrayBase)) { n.RemoveChild(arrayBase); n.AddChildToFront(new Node(Token.TO_OBJECT, arrayBase)); } } int indexType = RewriteForNumberVariables(arrayIndex, NumberType); if (indexType == NumberType) { if (!ConvertParameter(arrayIndex)) { // setting the ISNUMBER_PROP signals the codegen // to use the OptRuntime.getObjectIndex that takes // a double index n.PutIntProp(Node.ISNUMBER_PROP, Node.RIGHT); } } return NoType; } case Token.CALL: { Node child = n.GetFirstChild(); // the function node // must be an object RewriteAsObjectChildren(child, child.GetFirstChild()); child = child.GetNext(); // the first arg OptFunctionNode target = (OptFunctionNode)n.GetProp(Node.DIRECTCALL_PROP); if (target != null) { while (child != null) { int type = RewriteForNumberVariables(child, NumberType); if (type == NumberType) { MarkDCPNumberContext(child); } child = child.GetNext(); } } else { RewriteAsObjectChildren(n, child); } return NoType; } default: { RewriteAsObjectChildren(n, n.GetFirstChild()); return NoType; } } }
private Node TransformObjectLiteral(ObjectLiteral node) { if (node.IsDestructuring()) { return node; } // createObjectLiteral rewrites its argument as object // creation plus object property entries, so later compiler // stages don't need to know about object literals. decompiler.AddToken(Token.LC); IList<ObjectProperty> elems = node.GetElements(); Node @object = new Node(Token.OBJECTLIT); object[] properties; if (elems.IsEmpty()) { properties = ScriptRuntime.emptyArgs; } else { int size = elems.Count; int i = 0; properties = new object[size]; foreach (ObjectProperty prop in elems) { if (prop.IsGetter()) { decompiler.AddToken(Token.GET); } else { if (prop.IsSetter()) { decompiler.AddToken(Token.SET); } } properties[i++] = GetPropKey(prop.GetLeft()); // OBJECTLIT is used as ':' in object literal for // decompilation to solve spacing ambiguity. if (!(prop.IsGetter() || prop.IsSetter())) { decompiler.AddToken(Token.OBJECTLIT); } Node right = Transform(prop.GetRight()); if (prop.IsGetter()) { right = CreateUnary(Token.GET, right); } else { if (prop.IsSetter()) { right = CreateUnary(Token.SET, right); } } @object.AddChildToBack(right); if (i < size) { decompiler.AddToken(Token.COMMA); } } } decompiler.AddToken(Token.RC); @object.PutProp(Node.OBJECT_IDS_PROP, properties); return @object; }
private void RewriteAsObjectChildren(Node n, Node child) { // Force optimized children to be objects while (child != null) { Node nextChild = child.GetNext(); int type = RewriteForNumberVariables(child, NoType); if (type == NumberType) { if (!ConvertParameter(child)) { n.RemoveChild(child); Node nuChild = new Node(Token.TO_OBJECT, child); if (nextChild == null) { n.AddChildToBack(nuChild); } else { n.AddChildBefore(nuChild, nextChild); } } } child = nextChild; } }
private Node TransformArrayComp(ArrayComprehension node) { // An array comprehension expression such as // // [expr for (x in foo) for each ([y, z] in bar) if (cond)] // // is rewritten approximately as // // new Scope(ARRAYCOMP) { // new Node(BLOCK) { // let tmp1 = new Array; // for (let x in foo) { // for each (let tmp2 in bar) { // if (cond) { // tmp1.push([y, z] = tmp2, expr); // } // } // } // } // createName(tmp1) // } int lineno = node.GetLineno(); Scope scopeNode = CreateScopeNode(Token.ARRAYCOMP, lineno); string arrayName = currentScriptOrFn.GetNextTempName(); PushScope(scopeNode); try { DefineSymbol(Token.LET, arrayName, false); Node block = new Node(Token.BLOCK, lineno); Node newArray = CreateCallOrNew(Token.NEW, CreateName("Array")); Node init = new Node(Token.EXPR_VOID, CreateAssignment(Token.ASSIGN, CreateName(arrayName), newArray), lineno); block.AddChildToBack(init); block.AddChildToBack(ArrayCompTransformHelper(node, arrayName)); scopeNode.AddChildToBack(block); scopeNode.AddChildToBack(CreateName(arrayName)); return scopeNode; } finally { PopScope(); } }
private Node TransformArrayLiteral(ArrayLiteral node) { if (node.IsDestructuring()) { return node; } decompiler.AddToken(Token.LB); IList<AstNode> elems = node.GetElements(); Node array = new Node(Token.ARRAYLIT); IList<int> skipIndexes = null; for (int i = 0; i < elems.Count; ++i) { AstNode elem = elems[i]; if (elem.GetType() != Token.EMPTY) { array.AddChildToBack(Transform(elem)); } else { if (skipIndexes == null) { skipIndexes = new List<int>(); } skipIndexes.Add(i); } if (i < elems.Count - 1) { decompiler.AddToken(Token.COMMA); } } decompiler.AddToken(Token.RB); array.PutIntProp(Node.DESTRUCTURING_ARRAY_LENGTH, node.GetDestructuringLength()); if (skipIndexes != null) { int[] skips = new int[skipIndexes.Count]; for (int i_1 = 0; i_1 < skipIndexes.Count; i_1++) { skips[i_1] = skipIndexes[i_1]; } array.PutProp(Node.SKIP_INDEXES_PROP, skips); } return array; }
private 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; }
private 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; } // Replace if (false) xxx by empty block return new Node(Token.BLOCK, lineno); } } Node result = new Node(Token.BLOCK, lineno); Node ifNotTarget = Node.NewTarget(); Jump IFNE = new 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; }
/// <summary>Generate IR for a for..in loop.</summary> /// <remarks>Generate IR for a for..in loop.</remarks> private Node CreateForIn(int declType, Node loop, Node lhs, Node obj, Node body, bool isForEach) { int destructuring = -1; int destructuringLen = 0; Node lvalue; int type = lhs.GetType(); if (type == Token.VAR || type == Token.LET) { Node kid = lhs.GetLastChild(); int kidType = kid.GetType(); if (kidType == Token.ARRAYLIT || kidType == Token.OBJECTLIT) { type = destructuring = kidType; lvalue = kid; destructuringLen = 0; if (kid is ArrayLiteral) { destructuringLen = ((ArrayLiteral)kid).GetDestructuringLength(); } } else { if (kidType == Token.NAME) { lvalue = Node.NewString(Token.NAME, kid.GetString()); } else { ReportError("msg.bad.for.in.lhs"); return null; } } } else { if (type == Token.ARRAYLIT || type == Token.OBJECTLIT) { destructuring = type; lvalue = lhs; destructuringLen = 0; if (lhs is ArrayLiteral) { destructuringLen = ((ArrayLiteral)lhs).GetDestructuringLength(); } } else { lvalue = MakeReference(lhs); if (lvalue == null) { ReportError("msg.bad.for.in.lhs"); return null; } } } Node localBlock = new Node(Token.LOCAL_BLOCK); int initType = isForEach ? Token.ENUM_INIT_VALUES : (destructuring != -1 ? Token.ENUM_INIT_ARRAY : 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; if (destructuring != -1) { assign = CreateDestructuringAssignment(declType, lvalue, id); if (!isForEach && (destructuring == Token.OBJECTLIT || destructuringLen != 2)) { // destructuring assignment is only allowed in for..each or // with an array type of length 2 (to hold key and value) ReportError("msg.bad.for.in.destruct"); } } else { assign = SimpleAssignment(lvalue, id); } newBody.AddChildToBack(new Node(Token.EXPR_VOID, assign)); newBody.AddChildToBack(body); loop = CreateLoop((Jump)loop, LOOP_WHILE, newBody, cond, null, null); loop.AddChildToFront(init); if (type == Token.VAR || type == Token.LET) { loop.AddChildToFront(lhs); } localBlock.AddChildToBack(loop); return localBlock; }
/// <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 void CloseSwitch(Node switchBlock) { if (switchBlock.GetType() != Token.BLOCK) { throw Kit.CodeBug(); } Jump switchNode = (Jump)switchBlock.GetFirstChild(); if (switchNode.GetType() != Token.SWITCH) { throw Kit.CodeBug(); } Node switchBreakTarget = Node.NewTarget(); // switchNode.target is only used by NodeTransformer // to detect switch end switchNode.target = switchBreakTarget; Node defaultTarget = switchNode.GetDefault(); if (defaultTarget == null) { defaultTarget = switchBreakTarget; } switchBlock.AddChildAfter(MakeJump(Token.GOTO, defaultTarget), switchNode); switchBlock.AddChildToBack(switchBreakTarget); }
private Node InitFunction(FunctionNode fnNode, int functionIndex, Node statements, int functionType) { fnNode.SetFunctionType(functionType); fnNode.AddChildToBack(statements); int functionCount = fnNode.GetFunctionCount(); if (functionCount != 0) { // Functions containing other functions require activation objects fnNode.SetRequiresActivation(); } if (functionType == FunctionNode.FUNCTION_EXPRESSION) { Name name = fnNode.GetFunctionName(); if (name != null && name.Length() != 0 && fnNode.GetSymbol(name.GetIdentifier()) == null) { // 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, but only if the // function doesn't already define a formal parameter, var, // or nested function with the same name. fnNode.PutSymbol(new Symbol(Token.FUNCTION, name.GetIdentifier())); Node setFn = new Node(Token.EXPR_VOID, new Node(Token.SETNAME, Node.NewString(Token.BINDNAME, name.GetIdentifier()), new Node(Token.THISFN))); statements.AddChildrenToFront(setFn); } } // Add return to end if needed. Node lastStmt = statements.GetLastChild(); if (lastStmt == null || lastStmt.GetType() != Token.RETURN) { statements.AddChildToBack(new Node(Token.RETURN)); } Node result = Node.NewString(Token.FUNCTION, fnNode.GetName()); result.PutIntProp(Node.FUNCTION_PROP, functionIndex); return result; }
/// <summary>If caseExpression argument is null it indicates a default label.</summary> /// <remarks>If caseExpression argument is null it indicates a default label.</remarks> private void AddSwitchCase(Node switchBlock, Node caseExpression, Node statements) { if (switchBlock.GetType() != Token.BLOCK) { throw Kit.CodeBug(); } Jump switchNode = (Jump)switchBlock.GetFirstChild(); if (switchNode.GetType() != Token.SWITCH) { throw Kit.CodeBug(); } Node gotoTarget = Node.NewTarget(); if (caseExpression != null) { Jump caseNode = new Jump(Token.CASE, caseExpression); caseNode.target = gotoTarget; switchNode.AddChildToBack(caseNode); } else { switchNode.SetDefault(gotoTarget); } switchBlock.AddChildToBack(gotoTarget); switchBlock.AddChildToBack(statements); }
private Node TransformXmlLiteral(XmlLiteral node) { // a literal like <foo>{bar}</foo> is rewritten as // new XML("<foo>" + bar + "</foo>"); Node pnXML = new Node(Token.NEW, node.GetLineno()); IList<XmlFragment> frags = node.GetFragments(); XmlString first = (XmlString)frags[0]; bool anon = first.GetXml().Trim().StartsWith("<>"); pnXML.AddChildToBack(CreateName(anon ? "XMLList" : "XML")); Node pn = null; foreach (XmlFragment frag in frags) { if (frag is XmlString) { string xml = ((XmlString)frag).GetXml(); decompiler.AddName(xml); if (pn == null) { pn = CreateString(xml); } else { pn = CreateBinary(Token.ADD, pn, CreateString(xml)); } } else { XmlExpression xexpr = (XmlExpression)frag; bool isXmlAttr = xexpr.IsXmlAttribute(); Node expr; decompiler.AddToken(Token.LC); if (xexpr.GetExpression() is EmptyExpression) { expr = CreateString(string.Empty); } else { expr = Transform(xexpr.GetExpression()); } decompiler.AddToken(Token.RC); if (isXmlAttr) { // Need to put the result in double quotes expr = CreateUnary(Token.ESCXMLATTR, expr); Node prepend = CreateBinary(Token.ADD, CreateString("\""), expr); expr = CreateBinary(Token.ADD, prepend, CreateString("\"")); } else { expr = CreateUnary(Token.ESCXMLTEXT, expr); } pn = CreateBinary(Token.ADD, pn, expr); } } pnXML.AddChildToBack(pn); return pnXML; }
private Node TransformScript(ScriptNode node) { decompiler.AddToken(Token.SCRIPT); if (currentScope != null) { Kit.CodeBug(); } currentScope = node; Node body = new Node(Token.BLOCK); foreach (Node kid in node) { body.AddChildToBack(Transform((AstNode)kid)); } node.RemoveChildren(); Node children = body.GetFirstChild(); if (children != null) { node.AddChildrenToBack(children); } return node; }
private void TransformCompilationUnit_r(ScriptNode tree, Node parent, Scope scope, bool createScopeObjects, bool inStrictMode) { Node node = null; for (; ; ) { Node previous = null; if (node == null) { node = parent.GetFirstChild(); } else { previous = node; node = node.GetNext(); } if (node == null) { break; } int type = node.GetType(); if (createScopeObjects && (type == Token.BLOCK || type == Token.LOOP || type == Token.ARRAYCOMP) && (node is Scope)) { Scope newScope = (Scope)node; if (newScope.GetSymbolTable() != null) { // transform to let statement so we get a with statement // created to contain scoped let variables Node let = new Node(type == Token.ARRAYCOMP ? Token.LETEXPR : Token.LET); Node innerLet = new Node(Token.LET); let.AddChildToBack(innerLet); foreach (string name in newScope.GetSymbolTable().Keys) { innerLet.AddChildToBack(Node.NewString(Token.NAME, name)); } newScope.SetSymbolTable(null); // so we don't transform again Node oldNode = node; node = ReplaceCurrent(parent, previous, node, let); type = node.GetType(); let.AddChildToBack(oldNode); } } switch (type) { case Token.LABEL: case Token.SWITCH: case Token.LOOP: { loops.Push(node); loopEnds.Push(((Jump)node).target); break; } case Token.WITH: { loops.Push(node); Node leave = node.GetNext(); if (leave.GetType() != Token.LEAVEWITH) { Kit.CodeBug(); } loopEnds.Push(leave); break; } case Token.TRY: { Jump jump = (Jump)node; Node finallytarget = jump.GetFinally(); if (finallytarget != null) { hasFinally = true; loops.Push(node); loopEnds.Push(finallytarget); } break; } case Token.TARGET: case Token.LEAVEWITH: { if (!loopEnds.IsEmpty() && loopEnds.Peek() == node) { loopEnds.Pop(); loops.Pop(); } break; } case Token.YIELD: { ((FunctionNode)tree).AddResumptionPoint(node); break; } case Token.RETURN: { bool isGenerator = tree.GetType() == Token.FUNCTION && ((FunctionNode)tree).IsGenerator(); if (isGenerator) { node.PutIntProp(Node.GENERATOR_END_PROP, 1); } 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.GetType(); if (elemtype == Token.TRY || elemtype == Token.WITH) { Node unwind; if (elemtype == Token.TRY) { Jump jsrnode = new Jump(Token.JSR); Node jsrtarget = ((Jump)n).GetFinally(); jsrnode.target = jsrtarget; unwind = jsrnode; } else { unwind = new Node(Token.LEAVEWITH); } if (unwindBlock == null) { unwindBlock = new Node(Token.BLOCK, node.GetLineno()); } unwindBlock.AddChildToBack(unwind); } } if (unwindBlock != null) { Node returnNode = node; Node returnExpr = returnNode.GetFirstChild(); node = ReplaceCurrent(parent, previous, node, unwindBlock); if (returnExpr == null || isGenerator) { 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, scope, createScopeObjects, inStrictMode); } // skip transformCompilationUnit_r to avoid infinite loop goto siblingLoop_continue; } break; } case Token.BREAK: case Token.CONTINUE: { Jump jump = (Jump)node; Jump jumpStatement = jump.GetJumpStatement(); if (jumpStatement == null) { Kit.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 Kit.CodeBug(); } --i; Node n = (Node)loops.Get(i); if (n == jumpStatement) { break; } int elemtype = n.GetType(); if (elemtype == Token.WITH) { Node leave = new Node(Token.LEAVEWITH); previous = AddBeforeCurrent(parent, previous, node, leave); } else { if (elemtype == Token.TRY) { Jump tryNode = (Jump)n; Jump jsrFinally = new Jump(Token.JSR); jsrFinally.target = tryNode.GetFinally(); previous = AddBeforeCurrent(parent, previous, node, jsrFinally); } } } if (type == Token.BREAK) { jump.target = jumpStatement.target; } else { jump.target = jumpStatement.GetContinue(); } jump.SetType(Token.GOTO); break; } case Token.CALL: { VisitCall(node, tree); break; } case Token.NEW: { VisitNew(node, tree); break; } case Token.LETEXPR: case Token.LET: { Node child = node.GetFirstChild(); if (child.GetType() == Token.LET) { // We have a let statement or expression rather than a // let declaration bool createWith = tree.GetType() != Token.FUNCTION || ((FunctionNode)tree).RequiresActivation(); node = VisitLet(createWith, parent, previous, node); break; } goto case Token.CONST; } case Token.CONST: case Token.VAR: { // fall through to process let declaration... Node result = new Node(Token.BLOCK); for (Node cursor = node.GetFirstChild(); cursor != null; ) { // Move cursor to next before createAssignment gets chance // to change n.next Node n = cursor; cursor = cursor.GetNext(); if (n.GetType() == Token.NAME) { if (!n.HasChildren()) { continue; } Node init = n.GetFirstChild(); n.RemoveChild(init); n.SetType(Token.BINDNAME); n = new Node(type == Token.CONST ? Token.SETCONST : Token.SETNAME, n, init); } else { // May be a destructuring assignment already transformed // to a LETEXPR if (n.GetType() != Token.LETEXPR) { throw Kit.CodeBug(); } } Node pop = new Node(Token.EXPR_VOID, n, node.GetLineno()); result.AddChildToBack(pop); } node = ReplaceCurrent(parent, previous, node, result); break; } case Token.TYPEOFNAME: { Scope defining = scope.GetDefiningScope(node.GetString()); if (defining != null) { node.SetScope(defining); } break; } case Token.TYPEOF: case Token.IFNE: { Node child = node.GetFirstChild(); if (type == Token.IFNE) { while (child.GetType() == Token.NOT) { child = child.GetFirstChild(); } if (child.GetType() == Token.EQ || child.GetType() == Token.NE) { Node first = child.GetFirstChild(); Node last = child.GetLastChild(); if (first.GetType() == Token.NAME && first.GetString().Equals("undefined")) { child = last; } else { if (last.GetType() == Token.NAME && last.GetString().Equals("undefined")) { child = first; } } } } if (child.GetType() == Token.GETPROP) { child.SetType(Token.GETPROPNOWARN); } break; } case Token.SETNAME: { if (inStrictMode) { node.SetType(Token.STRICT_SETNAME); } goto case Token.NAME; } case Token.NAME: case Token.SETCONST: case Token.DELPROP: { // Turn name to var for faster access if possible if (createScopeObjects) { break; } Node nameSource; if (type == Token.NAME) { nameSource = node; } else { nameSource = node.GetFirstChild(); if (nameSource.GetType() != Token.BINDNAME) { if (type == Token.DELPROP) { break; } throw Kit.CodeBug(); } } if (nameSource.GetScope() != null) { break; } // already have a scope set string name = nameSource.GetString(); Scope defining = scope.GetDefiningScope(name); if (defining != null) { nameSource.SetScope(defining); if (type == Token.NAME) { node.SetType(Token.GETVAR); } else { if (type == Token.SETNAME || type == Token.STRICT_SETNAME) { node.SetType(Token.SETVAR); nameSource.SetType(Token.STRING); } else { if (type == Token.SETCONST) { node.SetType(Token.SETCONSTVAR); nameSource.SetType(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 Kit.CodeBug(); } } } } } break; } } TransformCompilationUnit_r(tree, node, node is Scope ? (Scope)node : scope, createScopeObjects, inStrictMode); siblingLoop_continue: ; } siblingLoop_break: ; }
protected internal virtual Node VisitLet(bool createWith, Node parent, Node previous, Node scopeNode) { Node vars = scopeNode.GetFirstChild(); Node body = vars.GetNext(); scopeNode.RemoveChild(vars); scopeNode.RemoveChild(body); bool isExpression = scopeNode.GetType() == Token.LETEXPR; Node result; Node newVars; if (createWith) { result = new Node(isExpression ? Token.WITHEXPR : Token.BLOCK); result = ReplaceCurrent(parent, previous, scopeNode, result); List<object> list = new List<object>(); Node objectLiteral = new Node(Token.OBJECTLIT); for (Node v = vars.GetFirstChild(); v != null; v = v.GetNext()) { Node current = v; if (current.GetType() == Token.LETEXPR) { // destructuring in let expr, e.g. let ([x, y] = [3, 4]) {} IList<object> destructuringNames = (IList<object>)current.GetProp(Node.DESTRUCTURING_NAMES); Node c = current.GetFirstChild(); if (c.GetType() != Token.LET) { throw Kit.CodeBug(); } // Add initialization code to front of body if (isExpression) { body = new Node(Token.COMMA, c.GetNext(), body); } else { body = new Node(Token.BLOCK, new Node(Token.EXPR_VOID, c.GetNext()), body); } // Update "list" and "objectLiteral" for the variables // defined in the destructuring assignment if (destructuringNames != null) { Sharpen.Collections.AddAll(list, destructuringNames); for (int i = 0; i < destructuringNames.Count; i++) { objectLiteral.AddChildToBack(new Node(Token.VOID, Node.NewNumber(0.0))); } } current = c.GetFirstChild(); } // should be a NAME, checked below if (current.GetType() != Token.NAME) { throw Kit.CodeBug(); } list.Add(ScriptRuntime.GetIndexObject(current.GetString())); Node init = current.GetFirstChild(); if (init == null) { init = new Node(Token.VOID, Node.NewNumber(0.0)); } objectLiteral.AddChildToBack(init); } objectLiteral.PutProp(Node.OBJECT_IDS_PROP, Sharpen.Collections.ToArray(list)); newVars = new Node(Token.ENTERWITH, objectLiteral); result.AddChildToBack(newVars); result.AddChildToBack(new Node(Token.WITH, body)); result.AddChildToBack(new Node(Token.LEAVEWITH)); } else { result = new Node(isExpression ? Token.COMMA : Token.BLOCK); result = ReplaceCurrent(parent, previous, scopeNode, result); newVars = new Node(Token.COMMA); for (Node v = vars.GetFirstChild(); v != null; v = v.GetNext()) { Node current = v; if (current.GetType() == Token.LETEXPR) { // destructuring in let expr, e.g. let ([x, y] = [3, 4]) {} Node c = current.GetFirstChild(); if (c.GetType() != Token.LET) { throw Kit.CodeBug(); } // Add initialization code to front of body if (isExpression) { body = new Node(Token.COMMA, c.GetNext(), body); } else { body = new Node(Token.BLOCK, new Node(Token.EXPR_VOID, c.GetNext()), body); } // We're removing the LETEXPR, so move the symbols Scope.JoinScopes((Scope)current, (Scope)scopeNode); current = c.GetFirstChild(); } // should be a NAME, checked below if (current.GetType() != Token.NAME) { throw Kit.CodeBug(); } Node stringNode = Node.NewString(current.GetString()); stringNode.SetScope((Scope)scopeNode); Node init = current.GetFirstChild(); if (init == null) { init = new Node(Token.VOID, Node.NewNumber(0.0)); } newVars.AddChildToBack(new Node(Token.SETVAR, stringNode, init)); } if (isExpression) { result.AddChildToBack(newVars); scopeNode.SetType(Token.COMMA); result.AddChildToBack(scopeNode); scopeNode.AddChildToBack(body); if (body is Scope) { Scope scopeParent = ((Scope)body).GetParentScope(); ((Scope)body).SetParentScope((Scope)scopeNode); ((Scope)scopeNode).SetParentScope(scopeParent); } } else { result.AddChildToBack(new Node(Token.EXPR_VOID, newVars)); scopeNode.SetType(Token.BLOCK); result.AddChildToBack(scopeNode); scopeNode.AddChildrenToBack(body); if (body is Scope) { Scope scopeParent = ((Scope)body).GetParentScope(); ((Scope)body).SetParentScope((Scope)scopeNode); ((Scope)scopeNode).SetParentScope(scopeParent); } } } return result; }