Bug 1113378 - Part 1/2 - Always fully parse IIFEs. r=Waldo
authorSean Stangl <sstangl@mozilla.com>
Fri, 19 Dec 2014 14:48:17 -0800
changeset 242246 5834d0b43de6224dc2883d5329f7ab8076486da8
parent 242245 989ff55c0404bb692200ddfa1d2f50cd42e465c5
child 242247 369167a98ec376a7e768642f67503c862f516068
push id7677
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 18:11:24 +0000
treeherdermozilla-aurora@f531d838c055 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs1113378
milestone38.0a1
Bug 1113378 - Part 1/2 - Always fully parse IIFEs. r=Waldo
js/src/frontend/FullParseHandler.h
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/frontend/SyntaxParseHandler.h
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -596,16 +596,19 @@ class FullParseHandler
     void setListFlag(ParseNode *pn, unsigned flag) {
         MOZ_ASSERT(pn->isArity(PN_LIST));
         pn->pn_xflags |= flag;
     }
     ParseNode *setInParens(ParseNode *pn) {
         pn->setInParens(true);
         return pn;
     }
+    ParseNode *setLikelyIIFE(ParseNode *pn) {
+        return setInParens(pn);
+    }
     void setPrologue(ParseNode *pn) {
         pn->pn_prologue = true;
     }
 
     bool isConstant(ParseNode *pn) {
         return pn->isConstant();
     }
     PropertyName *isName(ParseNode *pn) {
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -506,16 +506,17 @@ class ParseNode
 
     bool isAssignment() const {
         ParseNodeKind kind = getKind();
         return PNK_ASSIGNMENT_START <= kind && kind <= PNK_ASSIGNMENT_LAST;
     }
 
     /* Boolean attributes. */
     bool isInParens() const                { return pn_parens; }
+    bool isLikelyIIFE() const              { return isInParens(); }
     void setInParens(bool enabled)         { pn_parens = enabled; }
     bool isUsed() const                    { return pn_used; }
     void setUsed(bool enabled)             { pn_used = enabled; }
     bool isDefn() const                    { return pn_defn; }
     void setDefn(bool enabled)             { pn_defn = enabled; }
 
     static const unsigned NumDefinitionFlagBits = 10;
     static const unsigned NumListFlagBits = 10;
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2150,25 +2150,28 @@ Parser<ParseHandler>::templateLiteral()
     } while (tt == TOK_TEMPLATE_HEAD);
     return nodeList;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::functionDef(HandlePropertyName funName,
                                   FunctionType type, FunctionSyntaxKind kind,
-                                  GeneratorKind generatorKind)
+                                  GeneratorKind generatorKind, InvokedPrediction invoked)
 {
     MOZ_ASSERT_IF(kind == Statement, funName);
 
     /* Make a TOK_FUNCTION node. */
     Node pn = handler.newFunctionDefinition();
     if (!pn)
         return null();
 
+    if (invoked)
+        pn = handler.setLikelyIIFE(pn);
+
     bool bodyProcessed;
     if (!checkFunctionDefinition(funName, &pn, kind, &bodyProcessed))
         return null();
 
     if (bodyProcessed)
         return pn;
 
     RootedObject proto(context);
@@ -2318,16 +2321,23 @@ Parser<FullParseHandler>::functionArgsAn
 
     // Create box for fun->object early to protect against last-ditch GC.
     FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives, generatorKind);
     if (!funbox)
         return false;
 
     // Try a syntax parse for this inner function.
     do {
+        // If we're assuming this function is an IIFE, always perform a full
+        // parse to avoid the overhead of a lazy syntax-only parse. Although
+        // the prediction may be incorrect, IIFEs are common enough that it
+        // pays off for lots of code.
+        if (pn->isLikelyIIFE())
+            break;
+
         Parser<SyntaxParseHandler> *parser = handler.syntaxParser;
         if (!parser)
             break;
 
         {
             // Move the syntax parser to the current position in the stream.
             TokenStream::Position position(keepAtoms);
             tokenStream.tell(&position);
@@ -2632,17 +2642,17 @@ Parser<ParseHandler>::functionStmt()
         !report(ParseStrictError, pc->sc->strict, null(), JSMSG_STRICT_FUNCTION_STATEMENT))
         return null();
 
     return functionDef(name, Normal, Statement, generatorKind);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::functionExpr()
+Parser<ParseHandler>::functionExpr(InvokedPrediction invoked)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
 
     GeneratorKind generatorKind = NotGenerator;
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
 
@@ -2658,17 +2668,17 @@ Parser<ParseHandler>::functionExpr()
     } else if (tt == TOK_YIELD) {
         if (!checkYieldNameValidity())
             return null();
         name = tokenStream.currentName();
     } else {
         tokenStream.ungetToken();
     }
 
-    return functionDef(name, Normal, Expression, generatorKind);
+    return functionDef(name, Normal, Expression, generatorKind, invoked);
 }
 
 /*
  * Return true if this node, known to be an unparenthesized string literal,
  * could be the string of a directive in a Directive Prologue. Directive
  * strings never contain escape sequences or line continuations.
  * isEscapeFreeStringLiteral, below, checks whether the node itself could be
  * a directive.
@@ -4400,20 +4410,20 @@ SyntaxParseHandler::Node
 Parser<SyntaxParseHandler>::exportDeclaration()
 {
     JS_ALWAYS_FALSE(abortIfSyntaxParser());
     return SyntaxParseHandler::NodeFailure;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::expressionStatement()
+Parser<ParseHandler>::expressionStatement(InvokedPrediction invoked)
 {
     tokenStream.ungetToken();
-    Node pnexpr = expr();
+    Node pnexpr = expr(invoked);
     if (!pnexpr)
         return null();
     if (!MatchOrInsertSemicolon(tokenStream))
         return null();
     return handler.newExprStatement(pnexpr, pos().end);
 }
 
 template <typename ParseHandler>
@@ -5881,26 +5891,29 @@ Parser<ParseHandler>::statement(bool can
         TokenKind next;
         if (!tokenStream.peekToken(&next))
             return null();
         if (next == TOK_COLON)
             return labeledStatement();
         return expressionStatement();
       }
 
+      case TOK_NEW:
+        return expressionStatement(PredictInvoked);
+
       default:
         return expressionStatement();
     }
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::expr()
-{
-    Node pn = assignExpr();
+Parser<ParseHandler>::expr(InvokedPrediction invoked)
+{
+    Node pn = assignExpr(invoked);
     if (!pn)
         return null();
 
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_COMMA))
         return null();
     if (matched) {
         Node seq = handler.newList(PNK_COMMA, pn);
@@ -6012,33 +6025,33 @@ Precedence(ParseNodeKind pnk) {
 
     MOZ_ASSERT(pnk >= PNK_BINOP_FIRST);
     MOZ_ASSERT(pnk <= PNK_BINOP_LAST);
     return PrecedenceTable[pnk - PNK_BINOP_FIRST];
 }
 
 template <typename ParseHandler>
 MOZ_ALWAYS_INLINE typename ParseHandler::Node
-Parser<ParseHandler>::orExpr1()
+Parser<ParseHandler>::orExpr1(InvokedPrediction invoked)
 {
     // Shift-reduce parser for the left-associative binary operator part of
     // the JS syntax.
 
     // Conceptually there's just one stack, a stack of pairs (lhs, op).
     // It's implemented using two separate arrays, though.
     Node nodeStack[PRECEDENCE_CLASSES];
     ParseNodeKind kindStack[PRECEDENCE_CLASSES];
     int depth = 0;
 
     bool oldParsingForInit = pc->parsingForInit;
     pc->parsingForInit = false;
 
     Node pn;
     for (;;) {
-        pn = unaryExpr();
+        pn = unaryExpr(invoked);
         if (!pn)
             return pn;
 
         // If a binary operator follows, consume it and compute the
         // corresponding operator.
         TokenKind tok;
         if (!tokenStream.getToken(&tok))
             return null();
@@ -6078,19 +6091,19 @@ Parser<ParseHandler>::orExpr1()
 
     MOZ_ASSERT(depth == 0);
     pc->parsingForInit = oldParsingForInit;
     return pn;
 }
 
 template <typename ParseHandler>
 MOZ_ALWAYS_INLINE typename ParseHandler::Node
-Parser<ParseHandler>::condExpr1()
-{
-    Node condition = orExpr1();
+Parser<ParseHandler>::condExpr1(InvokedPrediction invoked)
+{
+    Node condition = orExpr1(invoked);
     if (!condition || !tokenStream.isCurrentTokenType(TOK_HOOK))
         return condition;
 
     /*
      * Always accept the 'in' operator in the middle clause of a ternary,
      * where it's unambiguous, even if we might be parsing the init of a
      * for statement.
      */
@@ -6179,17 +6192,17 @@ Parser<SyntaxParseHandler>::checkAndMark
     {
         return abortIfSyntaxParser();
     }
     return checkStrictAssignment(pn);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::assignExpr()
+Parser<ParseHandler>::assignExpr(InvokedPrediction invoked)
 {
     JS_CHECK_RECURSION(context, return null());
 
     // It's very common at this point to have a "detectably simple" expression,
     // i.e. a name/number/string token followed by one of the following tokens
     // that obviously isn't part of an expression: , ; : ) ] }
     //
     // (In Parsemark this happens 81.4% of the time;  in code with large
@@ -6231,17 +6244,17 @@ Parser<ParseHandler>::assignExpr()
 
     tokenStream.ungetToken();
 
     // Save the tokenizer state in case we find an arrow function and have to
     // rewind.
     TokenStream::Position start(keepAtoms);
     tokenStream.tell(&start);
 
-    Node lhs = condExpr1();
+    Node lhs = condExpr1(invoked);
     if (!lhs)
         return null();
 
     ParseNodeKind kind;
     JSOp op;
     switch (tokenStream.currentToken().type) {
       case TOK_ASSIGN:       kind = PNK_ASSIGN;       op = JSOP_NOP;    break;
       case TOK_ADDASSIGN:    kind = PNK_ADDASSIGN;    op = JSOP_ADD;    break;
@@ -6337,17 +6350,17 @@ Parser<ParseHandler>::unaryOpExpr(ParseN
     Node kid = unaryExpr();
     if (!kid)
         return null();
     return handler.newUnary(kind, op, begin, kid);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::unaryExpr()
+Parser<ParseHandler>::unaryExpr(InvokedPrediction invoked)
 {
     Node pn, pn2;
 
     JS_CHECK_RECURSION(context, return null());
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
@@ -6395,17 +6408,17 @@ Parser<ParseHandler>::unaryExpr()
                 return null();
             pc->sc->setBindingsAccessedDynamically();
         }
 
         return handler.newDelete(begin, expr);
       }
 
       default:
-        pn = memberExpr(tt, true);
+        pn = memberExpr(tt, /* allowCallSyntax = */ true, invoked);
         if (!pn)
             return null();
 
         /* Don't look across a newline boundary for a postfix incop. */
         if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
             return null();
         if (tt == TOK_INC || tt == TOK_DEC) {
             tokenStream.consumeKnownToken(tt);
@@ -7498,50 +7511,50 @@ Parser<ParseHandler>::argumentList(Node 
         return false;
     }
     handler.setEndPosition(listNode, pos().end);
     return true;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax)
+Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax, InvokedPrediction invoked)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
 
     Node lhs;
 
     JS_CHECK_RECURSION(context, return null());
 
     /* Check for new expression first. */
     if (tt == TOK_NEW) {
         lhs = handler.newList(PNK_NEW, null(), JSOP_NEW);
         if (!lhs)
             return null();
 
         if (!tokenStream.getToken(&tt, TokenStream::Operand))
             return null();
-        Node ctorExpr = memberExpr(tt, false);
+        Node ctorExpr = memberExpr(tt, false, PredictInvoked);
         if (!ctorExpr)
             return null();
 
         handler.addList(lhs, ctorExpr);
 
         bool matched;
         if (!tokenStream.matchToken(&matched, TOK_LP))
             return null();
         if (matched) {
             bool isSpread = false;
             if (!argumentList(lhs, &isSpread))
                 return null();
             if (isSpread)
                 handler.setOp(lhs, JSOP_SPREADNEW);
         }
     } else {
-        lhs = primaryExpr(tt);
+        lhs = primaryExpr(tt, invoked);
         if (!lhs)
             return null();
     }
 
     while (true) {
         if (!tokenStream.getToken(&tt))
             return null();
         if (tt == TOK_EOF)
@@ -8124,24 +8137,24 @@ Parser<ParseHandler>::methodDefinition(N
         return false;
     if (!handler.addMethodDefinition(literal, propname, fn, op))
         return false;
     return true;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::primaryExpr(TokenKind tt)
+Parser<ParseHandler>::primaryExpr(TokenKind tt, InvokedPrediction invoked)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
     JS_CHECK_RECURSION(context, return null());
 
     switch (tt) {
       case TOK_FUNCTION:
-        return functionExpr();
+        return functionExpr(invoked);
 
       case TOK_LB:
         return arrayInitializer();
 
       case TOK_LC:
         return objectLiteral();
 
       case TOK_LET:
@@ -8265,17 +8278,17 @@ Parser<ParseHandler>::parenExprOrGenerat
 
     /*
      * Always accept the 'in' operator in a parenthesized expression,
      * where it's unambiguous, even if we might be parsing the init of a
      * for statement.
      */
     bool oldParsingForInit = pc->parsingForInit;
     pc->parsingForInit = false;
-    Node pn = expr();
+    Node pn = expr(PredictInvoked);
     pc->parsingForInit = oldParsingForInit;
 
     if (!pn)
         return null();
 
 #if JS_HAS_GENERATOR_EXPRS
     if (!tokenStream.matchToken(&matched, TOK_FOR))
         return null();
@@ -8343,17 +8356,17 @@ Parser<ParseHandler>::exprInParens()
 
     /*
      * Always accept the 'in' operator in a parenthesized expression,
      * where it's unambiguous, even if we might be parsing the init of a
      * for statement.
      */
     bool oldParsingForInit = pc->parsingForInit;
     pc->parsingForInit = false;
-    Node pn = expr();
+    Node pn = expr(PredictInvoked);
     pc->parsingForInit = oldParsingForInit;
 
     if (!pn)
         return null();
 
 #if JS_HAS_GENERATOR_EXPRS
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_FOR))
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -514,16 +514,19 @@ class Parser : private JS::AutoGCRooter,
 
     virtual bool strictMode() { return pc->sc->strict; }
 
     const ReadOnlyCompileOptions &options() const {
         return tokenStream.options();
     }
 
   private:
+    enum InvokedPrediction { PredictUninvoked = false, PredictInvoked = true };
+
+  private:
     /*
      * JS parsers, from lowest to highest precedence.
      *
      * Each parser must be called during the dynamic scope of a ParseContext
      * object, pointed to by this->pc.
      *
      * Each returns a parse node tree or null on error.
      *
@@ -531,17 +534,17 @@ class Parser : private JS::AutoGCRooter,
      * pointing to the token one past the end of the parsed fragment.  For a
      * number of the parsers this is convenient and avoids a lot of
      * unnecessary ungetting and regetting of tokens.
      *
      * Some parsers have two versions:  an always-inlined version (with an 'i'
      * suffix) and a never-inlined version (with an 'n' suffix).
      */
     Node functionStmt();
-    Node functionExpr();
+    Node functionExpr(InvokedPrediction invoked = PredictUninvoked);
     Node statements();
 
     Node blockStatement();
     Node ifStatement();
     Node doWhileStatement();
     Node whileStatement();
     Node forStatement();
     Node switchStatement();
@@ -553,43 +556,44 @@ class Parser : private JS::AutoGCRooter,
     Node throwStatement();
     Node tryStatement();
     Node debuggerStatement();
 
     Node lexicalDeclaration(bool isConst);
     Node letStatement();
     Node importDeclaration();
     Node exportDeclaration();
-    Node expressionStatement();
+    Node expressionStatement(InvokedPrediction invoked = PredictUninvoked);
     Node variables(ParseNodeKind kind, bool *psimple = nullptr,
                    StaticBlockObject *blockObj = nullptr,
                    VarContext varContext = HoistVars);
-    Node expr();
-    Node assignExpr();
+    Node expr(InvokedPrediction invoked = PredictUninvoked);
+    Node assignExpr(InvokedPrediction invoked = PredictUninvoked);
     Node assignExprWithoutYield(unsigned err);
     Node yieldExpression();
-    Node condExpr1();
-    Node orExpr1();
-    Node unaryExpr();
-    Node memberExpr(TokenKind tt, bool allowCallSyntax);
-    Node primaryExpr(TokenKind tt);
+    Node condExpr1(InvokedPrediction invoked = PredictUninvoked);
+    Node orExpr1(InvokedPrediction invoked = PredictUninvoked);
+    Node unaryExpr(InvokedPrediction invoked = PredictUninvoked);
+    Node memberExpr(TokenKind tt, bool allowCallSyntax,
+                    InvokedPrediction invoked = PredictUninvoked);
+    Node primaryExpr(TokenKind tt, InvokedPrediction invoked = PredictUninvoked);
     Node parenExprOrGeneratorComprehension();
     Node exprInParens();
 
     bool methodDefinition(Node literal, Node propname, FunctionType type, FunctionSyntaxKind kind,
                           GeneratorKind generatorKind, JSOp Op);
 
     /*
      * Additional JS parsers.
      */
     bool functionArguments(FunctionSyntaxKind kind, FunctionType type, Node *list, Node funcpn,
                            bool *hasRest);
 
     Node functionDef(HandlePropertyName name, FunctionType type, FunctionSyntaxKind kind,
-                     GeneratorKind generatorKind);
+                     GeneratorKind generatorKind, InvokedPrediction invoked = PredictUninvoked);
     bool functionArgsAndBody(Node pn, HandleFunction fun,
                              FunctionType type, FunctionSyntaxKind kind,
                              GeneratorKind generatorKind,
                              Directives inheritedDirectives, Directives *newDirectives);
 
     Node unaryOpExpr(ParseNodeKind kind, JSOp op, uint32_t begin);
 
     Node condition();
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -226,16 +226,19 @@ class SyntaxParseHandler
     void setBlockId(Node pn, unsigned blockid) {}
     void setFlag(Node pn, unsigned flag) {}
     void setListFlag(Node pn, unsigned flag) {}
     Node setInParens(Node pn) {
         // String literals enclosed by parentheses are ignored during
         // strict mode parsing.
         return (pn == NodeString) ? NodeGeneric : pn;
     }
+    Node setLikelyIIFE(Node pn) {
+        return pn; // Remain in syntax-parse mode.
+    }
     void setPrologue(Node pn) {}
 
     bool isConstant(Node pn) { return false; }
     PropertyName *isName(Node pn) {
         return (pn == NodeName) ? lastAtom->asPropertyName() : nullptr;
     }
     PropertyName *isGetProp(Node pn) {
         return (pn == NodeGetProp) ? lastAtom->asPropertyName() : nullptr;