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: ; } } }
internal static object Interpret (InterpretedFunction ifun, Context cx, IScriptable scope, IScriptable thisObj, object [] args) { using (Helpers.StackOverflowVerifier sov = new Helpers.StackOverflowVerifier (128)) { if (!ScriptRuntime.hasTopCall (cx)) Context.CodeBug (); if (cx.interpreterSecurityDomain != ifun.securityDomain) { object savedDomain = cx.interpreterSecurityDomain; cx.interpreterSecurityDomain = ifun.securityDomain; try { return ifun.securityController.callWithDomain (ifun.securityDomain, cx, ifun, scope, thisObj, args); } finally { cx.interpreterSecurityDomain = savedDomain; } } CallFrame frame = new CallFrame (); initFrame (cx, scope, thisObj, args, null, 0, args.Length, ifun, null, frame); return InterpretLoop (cx, frame, (object)null); } }
internal static string defaultObjectToSource(Context cx, IScriptable scope, IScriptable thisObj, object [] args) { using (Helpers.StackOverflowVerifier sov = new Helpers.StackOverflowVerifier (1024)) { bool toplevel, iterating; if (cx.iterating == null) { toplevel = true; iterating = false; cx.iterating = new ObjToIntMap (31); } else { toplevel = false; iterating = cx.iterating.has (thisObj); } System.Text.StringBuilder result = new System.Text.StringBuilder (128); if (toplevel) { result.Append ("("); } result.Append ('{'); // Make sure cx.iterating is set to null when done // so we don't leak memory try { if (!iterating) { cx.iterating.intern (thisObj); // stop recursion. object [] ids = thisObj.GetIds (); for (int i = 0; i < ids.Length; i++) { if (i > 0) result.Append (", "); object id = ids [i]; object value; if (id is int) { int intId = ((int)id); value = thisObj.Get (intId, thisObj); result.Append (intId); } else { string strId = (string)id; value = thisObj.Get (strId, thisObj); if (ScriptRuntime.isValidIdentifierName (strId)) { result.Append (strId); } else { result.Append ('\''); result.Append (ScriptRuntime.escapeString (strId, '\'')); result.Append ('\''); } } result.Append (':'); result.Append (ScriptRuntime.uneval (cx, scope, value)); } } } finally { if (toplevel) { cx.iterating = null; } } result.Append ('}'); if (toplevel) { result.Append (')'); } return result.ToString (); } }
/* * Top-down regular expression grammar, based closely on Perl4. * * regexp: altern A regular expression is one or more * altern '|' regexp alternatives separated by vertical bar. */ private static bool parseDisjunction(CompilerState state) { using (Helpers.StackOverflowVerifier sov = new Helpers.StackOverflowVerifier (1024)) { if (!parseAlternative (state)) return false; char [] source = state.cpbegin; int index = state.cp; if (index != source.Length && source [index] == '|') { RENode altResult; ++state.cp; altResult = new RENode (REOP_ALT); altResult.kid = state.result; if (!parseDisjunction (state)) return false; altResult.kid2 = state.result; state.result = altResult; /* ALT, <next>, ..., JUMP, <end> ... JUMP <end> */ state.progLength += 9; } return true; } }
Node statement () { using (Helpers.StackOverflowVerifier sov = new Helpers.StackOverflowVerifier (512)) { try { Node pn = statementHelper (null); if (pn != null) { return pn; } } catch (ParserException) { } } // skip to end of statement int lineno = ts.Lineno; for (; ; ) { int tt = peekTokenOrEOL (); consumeToken (); switch (tt) { case Token.ERROR: case Token.EOF: case Token.EOL: case Token.SEMI: goto guessingStatementEnd_brk; } } guessingStatementEnd_brk: ; return nf.CreateExprStatement (nf.CreateName ("error"), lineno); }
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; } }
Node unaryExpr () { using (Helpers.StackOverflowVerifier sov = new Helpers.StackOverflowVerifier (4096)) { int tt; tt = peekToken (); switch (tt) { case Token.VOID: case Token.NOT: case Token.BITNOT: case Token.TYPEOF: consumeToken (); decompiler.AddToken (tt); return nf.CreateUnary (tt, unaryExpr ()); case Token.ADD: consumeToken (); // Convert to special POS token in decompiler and parse tree decompiler.AddToken (Token.POS); return nf.CreateUnary (Token.POS, unaryExpr ()); case Token.SUB: consumeToken (); // Convert to special NEG token in decompiler and parse tree decompiler.AddToken (Token.NEG); return nf.CreateUnary (Token.NEG, unaryExpr ()); case Token.INC: case Token.DEC: consumeToken (); decompiler.AddToken (tt); return nf.CreateIncDec (tt, false, memberExpr (true)); case Token.DELPROP: consumeToken (); decompiler.AddToken (Token.DELPROP); return nf.CreateUnary (Token.DELPROP, unaryExpr ()); case Token.ERROR: consumeToken (); break; // XML stream encountered in expression. case Token.LT: if (compilerEnv.isXmlAvailable ()) { consumeToken (); Node pn = xmlInitializer (); return memberExprTail (true, pn); } // Fall thru to the default handling of RELOP goto default; default: { Node pn = memberExpr (true); // Don't look across a newline boundary for a postfix incop. tt = peekTokenOrEOL (); if (tt == Token.INC || tt == Token.DEC) { consumeToken (); decompiler.AddToken (tt); return nf.CreateIncDec (tt, true, pn); } return pn; } } return nf.CreateName ("err"); // Only reached on error. Try to continue. } }
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: ; } } }