/// <summary> Script (for associating file/url names with toplevel scripts.)</summary> internal void initScript (ScriptOrFnNode scriptNode, Node body) { Node children = body.FirstChild; if (children != null) { scriptNode.addChildrenToBack (children); } }
private static void toStringTreeHelper(ScriptOrFnNode treeTop, Node n, ObjToIntMap printIds, int level, System.Text.StringBuilder sb) { if (Token.printTrees) { if (printIds == null) { printIds = new ObjToIntMap(); generatePrintIds(treeTop, printIds); } for (int i = 0; i != level; ++i) { sb.Append(" "); } n.toString(printIds, sb); sb.Append('\n'); for (Node cursor = n.FirstChild; cursor != null; cursor = cursor.Next) { if (cursor.Type == Token.FUNCTION) { int fnIndex = cursor.getExistingIntProp(Node.FUNCTION_PROP); FunctionNode fn = treeTop.getFunctionNode(fnIndex); toStringTreeHelper(fn, fn, null, level + 1, sb); } else { toStringTreeHelper(treeTop, cursor, printIds, level + 1, sb); } } } }
public void transform (ScriptOrFnNode tree) { transformCompilationUnit (tree); for (int i = 0; i != tree.FunctionCount; ++i) { FunctionNode fn = tree.getFunctionNode (i); transform (fn); } }
public void transform(ScriptOrFnNode tree) { transformCompilationUnit(tree); for (int i = 0; i != tree.FunctionCount; ++i) { FunctionNode fn = tree.getFunctionNode(i); transform(fn); } }
public string toStringTree(ScriptOrFnNode treeTop) { if (Token.printTrees) { System.Text.StringBuilder sb = new System.Text.StringBuilder(); toStringTreeHelper(treeTop, this, null, 0, sb); return(sb.ToString()); } return(null); }
private void transformCompilationUnit(ScriptOrFnNode tree) { loops = new ObjArray(); loopEnds = new ObjArray(); // to save against upchecks if no finally blocks are used. hasFinally = false; try { transformCompilationUnit_r(tree, tree); } catch (Helpers.StackOverflowVerifierException) { throw Context.ReportRuntimeError( ScriptRuntime.GetMessage("mag.too.deep.parser.recursion")); } }
private void transformCompilationUnit (ScriptOrFnNode tree) { loops = new ObjArray (); loopEnds = new ObjArray (); // to save against upchecks if no finally blocks are used. hasFinally = false; try { transformCompilationUnit_r (tree, tree); } catch (Helpers.StackOverflowVerifierException) { throw Context.ReportRuntimeError ( ScriptRuntime.GetMessage ("mag.too.deep.parser.recursion")); } }
protected internal virtual void visitCall (Node node, ScriptOrFnNode tree) { }
private static void toStringTreeHelper(ScriptOrFnNode treeTop, Node n, ObjToIntMap printIds, int level, System.Text.StringBuilder sb) { if (Token.printTrees) { if (printIds == null) { printIds = new ObjToIntMap (); generatePrintIds (treeTop, printIds); } for (int i = 0; i != level; ++i) { sb.Append (" "); } n.toString (printIds, sb); sb.Append ('\n'); for (Node cursor = n.FirstChild; cursor != null; cursor = cursor.Next) { if (cursor.Type == Token.FUNCTION) { int fnIndex = cursor.getExistingIntProp (Node.FUNCTION_PROP); FunctionNode fn = treeTop.getFunctionNode (fnIndex); toStringTreeHelper (fn, fn, null, level + 1, sb); } else { toStringTreeHelper (treeTop, cursor, printIds, level + 1, sb); } } } }
public string toStringTree(ScriptOrFnNode treeTop) { if (Token.printTrees) { System.Text.StringBuilder sb = new System.Text.StringBuilder (); toStringTreeHelper (treeTop, this, null, 0, sb); return sb.ToString (); } return null; }
protected internal virtual void visitCall(Node node, ScriptOrFnNode tree) { }
public virtual object Compile (CompilerEnvirons compilerEnv, ScriptOrFnNode tree, string encodedSource, bool returnFunction) { this.compilerEnv = compilerEnv; new NodeTransformer ().transform (tree); if (Token.printTrees) { System.Console.Out.WriteLine (tree.toStringTree (tree)); } if (returnFunction) { tree = tree.getFunctionNode (0); } scriptOrFn = tree; itsData = new InterpreterData (compilerEnv.LanguageVersion, scriptOrFn.SourceName, encodedSource); itsData.topLevel = true; if (returnFunction) { generateFunctionICode (); } else { generateICodeFromTree (scriptOrFn); } return itsData; }
Node function (int functionType) { using (Helpers.StackOverflowVerifier sov = new Helpers.StackOverflowVerifier (1024)) { int syntheticType = functionType; int baseLineno = ts.Lineno; // line number where source starts int functionSourceStart = decompiler.MarkFunctionStart (functionType); string name; Node memberExprNode = null; int tt = peekToken (); if (tt == Token.NAME) { consumeToken (); name = ts.String; decompiler.AddName (name); if (!matchToken (Token.LP)) { if (compilerEnv.isAllowMemberExprAsFunctionName ()) { // Extension to ECMA: if 'function <name>' does not follow // by '(', assume <name> starts memberExpr Node memberExprHead = nf.CreateName (name); name = ""; memberExprNode = memberExprTail (false, memberExprHead); } mustMatchToken (Token.LP, "msg.no.paren.parms"); } } else if (matchToken (Token.LP)) { // Anonymous function name = ""; } else { name = ""; if (compilerEnv.isAllowMemberExprAsFunctionName ()) { // Note that memberExpr can not start with '(' like // in function (1+2).toString(), because 'function (' already // processed as anonymous function memberExprNode = memberExpr (false); } mustMatchToken (Token.LP, "msg.no.paren.parms"); } if (memberExprNode != null) { syntheticType = FunctionNode.FUNCTION_EXPRESSION; } bool nested = insideFunction (); FunctionNode fnNode = nf.CreateFunction (name); if (nested || nestingOfWith > 0) { // 1. Nested functions are not affected by the dynamic scope flag // as dynamic scope is already a parent of their scope. // 2. Functions defined under the with statement also immune to // this setup, in which case dynamic scope is ignored in favor // of with object. fnNode.itsIgnoreDynamicScope = true; } int functionIndex = currentScriptOrFn.addFunction (fnNode); int functionSourceEnd; ScriptOrFnNode savedScriptOrFn = currentScriptOrFn; currentScriptOrFn = fnNode; int savedNestingOfWith = nestingOfWith; nestingOfWith = 0; Hashtable savedLabelSet = labelSet; labelSet = null; ObjArray savedLoopSet = loopSet; loopSet = null; ObjArray savedLoopAndSwitchSet = loopAndSwitchSet; loopAndSwitchSet = null; Node body; try { decompiler.AddToken (Token.LP); if (!matchToken (Token.RP)) { bool first = true; do { if (!first) decompiler.AddToken (Token.COMMA); first = false; mustMatchToken (Token.NAME, "msg.no.parm"); string s = ts.String; if (fnNode.hasParamOrVar (s)) { AddWarning ("msg.dup.parms", s); } fnNode.addParam (s); decompiler.AddName (s); } while (matchToken (Token.COMMA)); mustMatchToken (Token.RP, "msg.no.paren.after.parms"); } decompiler.AddToken (Token.RP); mustMatchToken (Token.LC, "msg.no.brace.body"); decompiler.AddEol (Token.LC); body = parseFunctionBody (); mustMatchToken (Token.RC, "msg.no.brace.after.body"); decompiler.AddToken (Token.RC); functionSourceEnd = decompiler.MarkFunctionEnd (functionSourceStart); if (functionType != FunctionNode.FUNCTION_EXPRESSION) { if (compilerEnv.LanguageVersion >= Context.Versions.JS1_2) { // function f() {} function g() {} is not allowed in 1.2 // or later but for compatibility with old scripts // the check is done only if language is // explicitly set. // TODO: warning needed if version == VERSION_DEFAULT ? tt = peekTokenOrEOL (); if (tt == Token.FUNCTION) { ReportError ("msg.no.semi.stmt"); } } // Add EOL only if function is not part of expression // since it gets SEMI + EOL from Statement in that case decompiler.AddToken (Token.EOL); } } finally { loopAndSwitchSet = savedLoopAndSwitchSet; loopSet = savedLoopSet; labelSet = savedLabelSet; nestingOfWith = savedNestingOfWith; currentScriptOrFn = savedScriptOrFn; } fnNode.setEncodedSourceBounds (functionSourceStart, functionSourceEnd); fnNode.SourceName = sourceURI; fnNode.BaseLineno = baseLineno; fnNode.EndLineno = ts.Lineno; Node pn = nf.initFunction (fnNode, functionIndex, body, syntheticType); if (memberExprNode != null) { pn = nf.CreateAssignment (Token.ASSIGN, memberExprNode, pn, false); if (functionType != FunctionNode.FUNCTION_EXPRESSION) { // TOOD: check JScript behavior: should it be createExprStatement? pn = nf.CreateExprStatementNoReturn (pn, baseLineno); } } return pn; } }
ScriptOrFnNode Parse () { this.decompiler = CreateDecompiler (compilerEnv); this.nf = new NodeFactory (this); currentScriptOrFn = nf.CreateScript (); int sourceStartOffset = decompiler.CurrentOffset; this.encodedSource = null; decompiler.AddToken (Token.SCRIPT); this.currentFlaggedToken = Token.EOF; this.syntaxErrorCount = 0; int baseLineno = ts.Lineno; // line number where source starts /* so we have something to add nodes to until * we've collected all the source */ Node pn = nf.CreateLeaf (Token.BLOCK); for (; ; ) { int tt = peekToken (); if (tt <= Token.EOF) { break; } Node n; if (tt == Token.FUNCTION) { consumeToken (); try { n = function (calledByCompileFunction ? FunctionNode.FUNCTION_EXPRESSION : FunctionNode.FUNCTION_STATEMENT); } catch (ParserException) { break; } } else { n = statement (); } nf.addChildToBack (pn, n); } if (this.syntaxErrorCount != 0) { string msg = Convert.ToString (this.syntaxErrorCount); msg = ScriptRuntime.GetMessage ("msg.got.syntax.errors", msg); throw errorReporter.RuntimeError (msg, sourceURI, baseLineno, null, 0); } currentScriptOrFn.SourceName = sourceURI; currentScriptOrFn.BaseLineno = baseLineno; currentScriptOrFn.EndLineno = ts.Lineno; int sourceEndOffset = decompiler.CurrentOffset; currentScriptOrFn.setEncodedSourceBounds (sourceStartOffset, sourceEndOffset); nf.initScript (currentScriptOrFn, pn); if (compilerEnv.isGeneratingSource ()) { encodedSource = decompiler.EncodedSource; } this.decompiler = null; // It helps GC return currentScriptOrFn; }
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: ; } } }
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.CONST: 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 ((node.Type == Token.VAR) ? Token.SETNAME : Token.SETNAME_CONST, 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: ; } } }
private void toString(ObjToIntMap printIds, System.Text.StringBuilder sb) { if (Token.printTrees) { sb.Append(Token.name(this.Type)); if (this is StringNode) { sb.Append(' '); sb.Append(String); } else if (this is ScriptOrFnNode) { ScriptOrFnNode sof = (ScriptOrFnNode)this; if (this is FunctionNode) { FunctionNode fn = (FunctionNode)this; sb.Append(' '); sb.Append(fn.FunctionName); } sb.Append(" [source name: "); sb.Append(sof.SourceName); sb.Append("] [encoded source length: "); sb.Append(sof.EncodedSourceEnd - sof.EncodedSourceStart); sb.Append("] [base line: "); sb.Append(sof.BaseLineno); sb.Append("] [end line: "); sb.Append(sof.EndLineno); sb.Append(']'); } else if (this is Jump) { Jump jump = (Jump)this; if (this.Type == Token.BREAK || this.Type == Token.CONTINUE) { sb.Append(" [label: "); appendPrintId(jump.JumpStatement, printIds, sb); sb.Append(']'); } else if (this.Type == Token.TRY) { Node catchNode = jump.target; Node finallyTarget = jump.Finally; if (catchNode != null) { sb.Append(" [catch: "); appendPrintId(catchNode, printIds, sb); sb.Append(']'); } if (finallyTarget != null) { sb.Append(" [finally: "); appendPrintId(finallyTarget, printIds, sb); sb.Append(']'); } } else if (this.Type == Token.LABEL || this.Type == Token.LOOP || this.Type == Token.SWITCH) { sb.Append(" [break: "); appendPrintId(jump.target, printIds, sb); sb.Append(']'); if (this.Type == Token.LOOP) { sb.Append(" [continue: "); appendPrintId(jump.Continue, printIds, sb); sb.Append(']'); } } else { sb.Append(" [target: "); appendPrintId(jump.target, printIds, sb); sb.Append(']'); } } else if (this.Type == Token.NUMBER) { sb.Append(' '); sb.Append(Double); } else if (this.Type == Token.TARGET) { sb.Append(' '); appendPrintId(this, printIds, sb); } if (lineno != -1) { sb.Append(' '); sb.Append(lineno); } for (PropListItem x = propListHead; x != null; x = x.next) { int Type = x.Type; sb.Append(" ["); sb.Append(propToString(Type)); sb.Append(": "); string value; switch (Type) { case TARGETBLOCK_PROP: // can't add this as it recurses value = "target block property"; break; case LOCAL_BLOCK_PROP: // can't add this as it is dull value = "last local block"; break; case ISNUMBER_PROP: switch (x.intValue) { case BOTH: value = "both"; break; case RIGHT: value = "right"; break; case LEFT: value = "left"; break; default: throw Context.CodeBug(); } break; case SPECIALCALL_PROP: switch (x.intValue) { case SPECIALCALL_EVAL: value = "eval"; break; case SPECIALCALL_WITH: value = "with"; break; default: // NON_SPECIALCALL should not be stored throw Context.CodeBug(); } break; default: object obj = x.objectValue; if (obj != null) { value = obj.ToString(); } else { value = Convert.ToString(x.intValue); } break; } sb.Append(value); sb.Append(']'); } } }