Bug 1505267 - Do not clone list node in appendDirectivesToBody. r=Yoric
authorTooru Fujisawa <arai_a@mac.com>
Fri, 09 Nov 2018 15:25:39 +0900
changeset 501790 c22d2d5b7af664565e888b039badd8157d80264c
parent 501789 73042df54a94e6b961e5f7128d15a5e4ff01c56b
child 501791 d3303c33ff84dffa70e517ea3ca627846937aef1
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersYoric
bugs1505267
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1505267 - Do not clone list node in appendDirectivesToBody. r=Yoric
js/src/frontend/BinSource-auto.cpp
js/src/frontend/BinSource.cpp
js/src/frontend/BinSource.h
js/src/frontend/BinSource.yaml
js/src/frontend/ParseNode.h
--- a/js/src/frontend/BinSource-auto.cpp
+++ b/js/src/frontend/BinSource-auto.cpp
@@ -2350,23 +2350,23 @@ BinASTParser<Tok>::parseInterfaceEagerFu
     BinParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
     BINJS_TRY(funpc.init());
     parseContext_->functionScope().useAsVarScope(parseContext_);
     MOZ_ASSERT(parseContext_->isFunctionBox());
 
     ParseContext::Scope lexicalScope(cx_, parseContext_, usedNames_);
     BINJS_TRY(lexicalScope.init(parseContext_));
     ListNode* params;
-    ListNode* tmpBody;
+    ListNode* body;
     MOZ_TRY(parseFunctionOrMethodContents(
-        length, &params, &tmpBody));
-    BINJS_MOZ_TRY_DECL(body, appendDirectivesToBody(tmpBody, directives));
+        length, &params, &body));
+    MOZ_TRY(prependDirectivesToBody(body, directives));
     BINJS_TRY_DECL(lexicalScopeData, NewLexicalScopeData(cx_, lexicalScope, alloc_, parseContext_));
-    BINJS_TRY_VAR(body, factory_.newLexicalScope(*lexicalScopeData, body));
-    BINJS_MOZ_TRY_DECL(result, buildFunction(start, kind, name, params, body, funbox));
+    BINJS_TRY_DECL(bodyScope, factory_.newLexicalScope(*lexicalScopeData, body));
+    BINJS_MOZ_TRY_DECL(result, buildFunction(start, kind, name, params, bodyScope, funbox));
     return result;
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceEagerFunctionExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::EagerFunctionExpression);
     BINJS_TRY(CheckRecursionLimit(cx_));
@@ -2406,23 +2406,23 @@ BinASTParser<Tok>::parseInterfaceEagerFu
     BinParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
     BINJS_TRY(funpc.init());
     parseContext_->functionScope().useAsVarScope(parseContext_);
     MOZ_ASSERT(parseContext_->isFunctionBox());
 
     ParseContext::Scope lexicalScope(cx_, parseContext_, usedNames_);
     BINJS_TRY(lexicalScope.init(parseContext_));
     ListNode* params;
-    ListNode* tmpBody;
+    ListNode* body;
     MOZ_TRY(parseFunctionExpressionContents(
-        length, &params, &tmpBody));
-    BINJS_MOZ_TRY_DECL(body, appendDirectivesToBody(tmpBody, directives));
+        length, &params, &body));
+    MOZ_TRY(prependDirectivesToBody(body, directives));
     BINJS_TRY_DECL(lexicalScopeData, NewLexicalScopeData(cx_, lexicalScope, alloc_, parseContext_));
-    BINJS_TRY_VAR(body, factory_.newLexicalScope(*lexicalScopeData, body));
-    BINJS_MOZ_TRY_DECL(result, buildFunction(start, kind, name, params, body, funbox));
+    BINJS_TRY_DECL(bodyScope, factory_.newLexicalScope(*lexicalScopeData, body));
+    BINJS_MOZ_TRY_DECL(result, buildFunction(start, kind, name, params, bodyScope, funbox));
     return result;
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceEagerGetter(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::EagerGetter);
     BINJS_TRY(CheckRecursionLimit(cx_));
@@ -2456,20 +2456,20 @@ BinASTParser<Tok>::parseInterfaceEagerGe
     BinParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
     BINJS_TRY(funpc.init());
     parseContext_->functionScope().useAsVarScope(parseContext_);
     MOZ_ASSERT(parseContext_->isFunctionBox());
 
     ParseContext::Scope lexicalScope(cx_, parseContext_, usedNames_);
     BINJS_TRY(lexicalScope.init(parseContext_));
     ListNode* params;
-    ListNode* tmpBody;
+    ListNode* body;
     MOZ_TRY(parseGetterContents(
-        length, &params, &tmpBody));
-    BINJS_MOZ_TRY_DECL(body, appendDirectivesToBody(tmpBody, directives));
+        length, &params, &body));
+    MOZ_TRY(prependDirectivesToBody(body, directives));
     BINJS_MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body, funbox));
     BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, accessorType));
     return result;
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceEagerMethod(const size_t start, const BinKind kind, const BinFields& fields)
 {
@@ -2512,20 +2512,20 @@ BinASTParser<Tok>::parseInterfaceEagerMe
     BinParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
     BINJS_TRY(funpc.init());
     parseContext_->functionScope().useAsVarScope(parseContext_);
     MOZ_ASSERT(parseContext_->isFunctionBox());
 
     ParseContext::Scope lexicalScope(cx_, parseContext_, usedNames_);
     BINJS_TRY(lexicalScope.init(parseContext_));
     ListNode* params;
-    ListNode* tmpBody;
+    ListNode* body;
     MOZ_TRY(parseFunctionOrMethodContents(
-        length, &params, &tmpBody));
-    BINJS_MOZ_TRY_DECL(body, appendDirectivesToBody(tmpBody, directives));
+        length, &params, &body));
+    MOZ_TRY(prependDirectivesToBody(body, directives));
     BINJS_MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body, funbox));
     BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, accessorType));
     return result;
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceEagerSetter(const size_t start, const BinKind kind, const BinFields& fields)
 {
@@ -2562,20 +2562,20 @@ BinASTParser<Tok>::parseInterfaceEagerSe
     BinParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
     BINJS_TRY(funpc.init());
     parseContext_->functionScope().useAsVarScope(parseContext_);
     MOZ_ASSERT(parseContext_->isFunctionBox());
 
     ParseContext::Scope lexicalScope(cx_, parseContext_, usedNames_);
     BINJS_TRY(lexicalScope.init(parseContext_));
     ListNode* params;
-    ListNode* tmpBody;
+    ListNode* body;
     MOZ_TRY(parseSetterContents(
-        length, &params, &tmpBody));
-    BINJS_MOZ_TRY_DECL(body, appendDirectivesToBody(tmpBody, directives));
+        length, &params, &body));
+    MOZ_TRY(prependDirectivesToBody(body, directives));
     BINJS_MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body, funbox));
     BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, accessorType));
     return result;
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceEmptyStatement(const size_t start, const BinKind kind, const BinFields& fields)
 {
@@ -3425,17 +3425,17 @@ BinASTParser<Tok>::parseInterfaceScript(
 #endif // defined(DEBUG)
 
     MOZ_TRY(parseAssertedScriptGlobalScope());
 
     BINJS_MOZ_TRY_DECL(directives, parseListOfDirective());
     forceStrictIfNecessary(parseContext_->sc(), directives);
     BINJS_MOZ_TRY_DECL(statements, parseListOfStatement());
 
-    MOZ_TRY(checkClosedVars(parseContext_->varScope())); BINJS_MOZ_TRY_DECL(result, appendDirectivesToBody(/* body = */ statements, /* directives = */ directives));
+    MOZ_TRY(checkClosedVars(parseContext_->varScope())); MOZ_TRY(prependDirectivesToBody(/* body = */ statements, directives)); auto result = statements;
     return result;
 }
 
 
 /*
  interface SetterContents : Node {
     bool isThisCaptured;
     AssertedParameterScope parameterScope;
--- a/js/src/frontend/BinSource.cpp
+++ b/js/src/frontend/BinSource.cpp
@@ -634,49 +634,47 @@ BinASTParser<Tok>::checkFunctionClosedVa
     MOZ_TRY(checkClosedVars(parseContext_->functionScope()));
     if (parseContext_->functionBox()->function()->isNamedLambda()) {
         MOZ_TRY(checkClosedVars(parseContext_->namedLambdaScope()));
     }
 
     return Ok();
 }
 
-template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::appendDirectivesToBody(ListNode* body, ListNode* directives)
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::prependDirectivesToBody(ListNode* body, ListNode* directives)
 {
     if (!directives) {
-        return body;
+        return Ok();
+    }
+
+    if (directives->empty()) {
+        return Ok();
     }
 
-    ParseNode* result = body;
-    if (!directives->empty()) {
-        // Convert directive list to a list of strings.
-        auto pos = directives->head()->pn_pos;
-        BINJS_TRY_DECL(prefix, factory_.newStatementList(pos));
-        for (ParseNode* iter : directives->contents()) {
-            BINJS_TRY_DECL(statement, factory_.newExprStatement(iter, iter->pn_pos.end));
-            prefix->appendWithoutOrderAssumption(statement);
-        }
+    MOZ_TRY(prependDirectivesImpl(body, directives->head()));
+
+    return Ok();
+}
 
-        // Prepend to the body.
-        ParseNode* iter = body->head();
-        while (iter) {
-            ParseNode* next = iter->pn_next;
-            prefix->appendWithoutOrderAssumption(iter);
-            iter = next;
-        }
-        prefix->setKind(body->getKind());
-        prefix->setOp(body->getOp());
-        if (body->hasTopLevelFunctionDeclarations()) {
-            prefix->setHasTopLevelFunctionDeclarations();
-        }
-        result = prefix;
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::prependDirectivesImpl(ListNode* body, ParseNode* directive)
+{
+    BINJS_TRY(CheckRecursionLimit(cx_));
+
+    // Prepend in the reverse order by using stack, so that the directives are
+    // prepended in the original order.
+    if (ParseNode* next = directive->pn_next) {
+        MOZ_TRY(prependDirectivesImpl(body, next));
     }
 
-    return result;
+    BINJS_TRY_DECL(statement, factory_.newExprStatement(directive, directive->pn_pos.end));
+    body->prependAndUpdatePos(statement);
+
+    return Ok();
 }
 
 template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
 BinASTParser<Tok>::raiseInvalidClosedVar(JSAtom* name)
 {
     return raiseError("Captured variable was not declared as captured");
 }
 
--- a/js/src/frontend/BinSource.h
+++ b/js/src/frontend/BinSource.h
@@ -220,19 +220,22 @@ class BinASTParser : public BinASTParser
     // When leaving a scope, check that none of its bindings are known closed over and un-marked.
     MOZ_MUST_USE JS::Result<Ok> checkClosedVars(ParseContext::Scope& scope);
 
     // As a convenience, a helper that checks the body, parameter, and recursive binding scopes.
     MOZ_MUST_USE JS::Result<Ok> checkFunctionClosedVars();
 
     // --- Utilities.
 
-    MOZ_MUST_USE JS::Result<ParseNode*> appendDirectivesToBody(ListNode* body,
+    MOZ_MUST_USE JS::Result<Ok> prependDirectivesToBody(ListNode* body,
         ListNode* directives);
 
+    MOZ_MUST_USE JS::Result<Ok> prependDirectivesImpl(ListNode* body,
+        ParseNode* directive);
+
     // Optionally force a strict context without restarting the parse when we see a strict
     // directive.
     void forceStrictIfNecessary(SharedContext* sc, ListNode* directives);
 
   private: // Implement ErrorReporter
     const JS::ReadOnlyCompileOptions& options_;
 
     const JS::ReadOnlyCompileOptions& options() const override {
--- a/js/src/frontend/BinSource.yaml
+++ b/js/src/frontend/BinSource.yaml
@@ -742,25 +742,25 @@ EagerFunctionExpression:
                 BinParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
                 BINJS_TRY(funpc.init());
                 parseContext_->functionScope().useAsVarScope(parseContext_);
                 MOZ_ASSERT(parseContext_->isFunctionBox());
 
                 ParseContext::Scope lexicalScope(cx_, parseContext_, usedNames_);
                 BINJS_TRY(lexicalScope.init(parseContext_));
                 ListNode* params;
-                ListNode* tmpBody;
+                ListNode* body;
             extra-args: |
-                length, &params, &tmpBody
+                length, &params, &body
             after: |
-                BINJS_MOZ_TRY_DECL(body, appendDirectivesToBody(tmpBody, directives));
+                MOZ_TRY(prependDirectivesToBody(body, directives));
     build: |
         BINJS_TRY_DECL(lexicalScopeData, NewLexicalScopeData(cx_, lexicalScope, alloc_, parseContext_));
-        BINJS_TRY_VAR(body, factory_.newLexicalScope(*lexicalScopeData, body));
-        BINJS_MOZ_TRY_DECL(result, buildFunction(start, kind, name, params, body, funbox));
+        BINJS_TRY_DECL(bodyScope, factory_.newLexicalScope(*lexicalScopeData, body));
+        BINJS_MOZ_TRY_DECL(result, buildFunction(start, kind, name, params, bodyScope, funbox));
 
 LazyFunctionExpression:
     init: |
         const auto syntax = FunctionSyntaxKind::Expression;
     fields:
         isGenerator:
             after: |
                 if (isGenerator) {
@@ -1125,17 +1125,18 @@ ReturnStatement:
 
 Script:
     fields:
         directives:
             after: |
                 forceStrictIfNecessary(parseContext_->sc(), directives);
     build:
         MOZ_TRY(checkClosedVars(parseContext_->varScope()));
-        BINJS_MOZ_TRY_DECL(result, appendDirectivesToBody(/* body = */ statements, /* directives = */ directives));
+        MOZ_TRY(prependDirectivesToBody(/* body = */ statements, directives));
+        auto result = statements;
 
 ShorthandProperty:
     build: |
         MOZ_ASSERT(name->isKind(ParseNodeKind::Name));
         MOZ_ASSERT(!factory_.isUsableAsObjectPropertyName(name));
         BINJS_TRY_DECL(propName, factory_.newObjectLiteralPropertyName(name->template as<NameNode>().name(), tokenizer_->pos(start)));
 
         BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(propName, name, AccessorType::None));
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -1272,16 +1272,21 @@ class ListNode : public ParseNode
         item->pn_next = pn_u.list.head;
         pn_u.list.head = item;
         if (pn_u.list.tail == &pn_u.list.head) {
             pn_u.list.tail = &item->pn_next;
         }
         pn_u.list.count++;
     }
 
+    void prependAndUpdatePos(ParseNode* item) {
+        prepend(item);
+        pn_pos.begin = item->pn_pos.begin;
+    }
+
     // Methods used by FoldConstants.cpp.
     // Caller is responsible for keeping the list consistent.
     ParseNode** unsafeHeadReference() {
         return &pn_u.list.head;
     }
 
     void unsafeReplaceTail(ParseNode** newTail) {
         pn_u.list.tail = newTail;