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")); } }
Node enterSwitch (Node switchSelector, int lineno, Node switchLabel) { Node switchNode = nf.CreateSwitch (switchSelector, lineno); if (loopAndSwitchSet == null) { loopAndSwitchSet = new ObjArray (); } loopAndSwitchSet.push (switchNode); return switchNode; }
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 enterLoop (Node loopLabel) { Node loop = nf.CreateLoopNode (loopLabel, ts.Lineno); if (loopSet == null) { loopSet = new ObjArray (); if (loopAndSwitchSet == null) { loopAndSwitchSet = new ObjArray (); } } loopSet.push (loop); loopAndSwitchSet.push (loop); return loop; }
/// <summary> /// Support for non-ecma "get"/"set" spidermonkey extension. /// </summary> /// <example> /// NAME getter: FUNCTION () SCOPE /// NAME setter: FUNCTION () SCOPE /// </example> bool CheckForGetterOrSetter (ObjArray elems) { int tt; string name = ts.String; consumeToken (); tt = peekToken (); if (tt != Token.NAME) return false; string type = ts.String; if (type != "getter" && type != "setter") { return false; } consumeToken (); matchToken (Token.COLON); matchToken (Token.FUNCTION); Node func = function (FunctionNode.FUNCTION_EXPRESSION); object property = ScriptRuntime.getIndexObject (name); elems.add ((type [0] == 'g') ? (object)new Node.GetterPropertyLiteral (property) : (object)new Node.SetterPropertyLiteral (property)); elems.add (func); return true; }
Node primaryExpr () { try { if (currentStackIndex++ > ScriptRuntime.MAXSTACKSIZE) { currentStackIndex = 0; throw Context.ReportRuntimeError ( ScriptRuntime.GetMessage ("mag.too.deep.parser.recursion"), sourceURI, ts.Lineno, null, 0); } Node pn; int ttFlagged = nextFlaggedToken (); int tt = ttFlagged & CLEAR_TI_MASK; switch (tt) { case Token.FUNCTION: return function (FunctionNode.FUNCTION_EXPRESSION); case Token.LB: { ObjArray elems = new ObjArray (); int skipCount = 0; decompiler.AddToken (Token.LB); bool after_lb_or_comma = true; for (; ; ) { tt = peekToken (); if (tt == Token.COMMA) { consumeToken (); decompiler.AddToken (Token.COMMA); if (!after_lb_or_comma) { after_lb_or_comma = true; } else { elems.add ((object)null); ++skipCount; } } else if (tt == Token.RB) { consumeToken (); decompiler.AddToken (Token.RB); break; } else { if (!after_lb_or_comma) { ReportError ("msg.no.bracket.arg"); } elems.add (assignExpr (false)); after_lb_or_comma = false; } } return nf.CreateArrayLiteral (elems, skipCount); } case Token.LC: { ObjArray elems = new ObjArray (); decompiler.AddToken (Token.LC); if (!matchToken (Token.RC)) { bool first = true; do { object property; if (!first) decompiler.AddToken (Token.COMMA); else first = false; tt = peekToken (); switch (tt) { case Token.NAME: case Token.STRING: consumeToken (); if (compilerEnv.getterAndSetterSupport) { if (tt == Token.NAME) if (CheckForGetOrSet (elems) || CheckForGetterOrSetter (elems)) goto next_prop; } // map NAMEs to STRINGs in object literal context // but tell the decompiler the proper type string s = ts.String; if (tt == Token.NAME) { decompiler.AddName (s); } else { decompiler.AddString (s); } property = ScriptRuntime.getIndexObject (s); break; case Token.NUMBER: consumeToken (); double n = ts.Number; decompiler.AddNumber (n); property = ScriptRuntime.getIndexObject (n); break; case Token.RC: // trailing comma is OK. goto commaloop_brk; default: ReportError ("msg.bad.prop"); goto commaloop_brk; } mustMatchToken (Token.COLON, "msg.no.colon.prop"); // OBJLIT is used as ':' in object literal for // decompilation to solve spacing ambiguity. decompiler.AddToken (Token.OBJECTLIT); elems.add (property); elems.add (assignExpr (false)); next_prop: ; } while (matchToken (Token.COMMA)); commaloop_brk: ; mustMatchToken (Token.RC, "msg.no.brace.prop"); } decompiler.AddToken (Token.RC); return nf.CreateObjectLiteral (elems); } case Token.LP: /* Brendan's IR-jsparse.c makes a new node tagged with * TOK_LP here... I'm not sure I understand why. Isn't * the grouping already implicit in the structure of the * parse tree? also TOK_LP is already overloaded (I * think) in the C IR as 'function call.' */ decompiler.AddToken (Token.LP); pn = expr (false); decompiler.AddToken (Token.RP); mustMatchToken (Token.RP, "msg.no.paren"); return pn; case Token.XMLATTR: mustHaveXML (); decompiler.AddToken (Token.XMLATTR); pn = attributeAccess (null, 0); return pn; case Token.NAME: { string name = ts.String; if ((ttFlagged & TI_CHECK_LABEL) != 0) { if (peekToken () == Token.COLON) { // Do not consume colon, it is used as unwind indicator // to return to statementHelper. // TODO: Better way? return nf.CreateLabel (ts.Lineno); } } decompiler.AddName (name); if (compilerEnv.isXmlAvailable ()) { pn = propertyName (null, name, 0); } else { pn = nf.CreateName (name); } return pn; } case Token.NUMBER: { double n = ts.Number; decompiler.AddNumber (n); return nf.CreateNumber (n); } case Token.STRING: { string s = ts.String; decompiler.AddString (s); return nf.CreateString (s); } case Token.DIV: case Token.ASSIGN_DIV: { // Got / or /= which should be treated as regexp in fact ts.readRegExp (tt); string flags = ts.regExpFlags; ts.regExpFlags = null; string re = ts.String; decompiler.AddRegexp (re, flags); int index = currentScriptOrFn.addRegexp (re, flags); return nf.CreateRegExp (index); } case Token.NULL: case Token.THIS: case Token.FALSE: case Token.TRUE: decompiler.AddToken (tt); return nf.CreateLeaf (tt); case Token.RESERVED: ReportError ("msg.reserved.id"); break; case Token.ERROR: /* the scanner or one of its subroutines reported the error. */ break; case Token.EOF: ReportError ("msg.unexpected.eof"); break; default: ReportError ("msg.syntax"); break; } return null; // should never reach here } finally { currentStackIndex--; } }
/// <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; }
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; }
public int addRegexp(string str, string flags) { if (str == null) Context.CodeBug (); if (regexps == null) { regexps = new ObjArray (); } regexps.add (str); regexps.add (flags); return regexps.size () / 2 - 1; }
public int addFunction(FunctionNode fnNode) { if (fnNode == null) Context.CodeBug (); if (functions == null) { functions = new ObjArray (); } functions.add (fnNode); return functions.size () - 1; }