Bug 1339395 - Part 2: Add parser support for rest and spread object properties. r=shu
authorAndré Bargull <andre.bargull@gmail.com>
Thu, 04 May 2017 05:05:26 -0700
changeset 356677 66ad093f7e6a4d342dceb9522809f019dbc6a2db
parent 356676 7ccc011304aa188aa50651cbb148d0f6b09ad495
child 356678 b6c489432f1102a449c4e216e3cb2075822a98ab
push id31768
push usercbook@mozilla.com
push dateFri, 05 May 2017 13:17:50 +0000
treeherdermozilla-central@9348b76977e8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu
bugs1339395
milestone55.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 1339395 - Part 2: Add parser support for rest and spread object properties. r=shu
js/src/builtin/ReflectParse.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/FullParseHandler.h
js/src/frontend/Parser.cpp
js/src/frontend/SyntaxParseHandler.h
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -3221,16 +3221,18 @@ ASTSerializer::propertyName(ParseNode* p
 bool
 ASTSerializer::property(ParseNode* pn, MutableHandleValue dst)
 {
     if (pn->isKind(PNK_MUTATEPROTO)) {
         RootedValue val(cx);
         return expression(pn->pn_kid, &val) &&
                builder.prototypeMutation(val, &pn->pn_pos, dst);
     }
+    if (pn->isKind(PNK_SPREAD))
+        return expression(pn, dst);
 
     PropKind kind;
     switch (pn->getOp()) {
       case JSOP_INITPROP:
         kind = PROP_INIT;
         break;
 
       case JSOP_INITPROP_GETTER:
@@ -3341,16 +3343,26 @@ ASTSerializer::objectPattern(ParseNode* 
 {
     MOZ_ASSERT(pn->isKind(PNK_OBJECT));
 
     NodeVector elts(cx);
     if (!elts.reserve(pn->pn_count))
         return false;
 
     for (ParseNode* propdef = pn->pn_head; propdef; propdef = propdef->pn_next) {
+        if (propdef->isKind(PNK_SPREAD)) {
+            RootedValue target(cx);
+            RootedValue spread(cx);
+            if (!pattern(propdef->pn_kid, &target))
+                return false;
+            if(!builder.spreadExpression(target, &propdef->pn_pos, &spread))
+                return false;
+            elts.infallibleAppend(spread);
+            continue;
+        }
         LOCAL_ASSERT(propdef->isKind(PNK_MUTATEPROTO) != propdef->isOp(JSOP_INITPROP));
 
         RootedValue key(cx);
         ParseNode* target;
         if (propdef->isKind(PNK_MUTATEPROTO)) {
             RootedValue pname(cx, StringValue(cx->names().proto));
             if (!builder.literal(pname, &propdef->pn_pos, &key))
                 return false;
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -5887,16 +5887,21 @@ BytecodeEmitter::emitDestructuringOpsObj
     MOZ_ASSERT(pattern->isArity(PN_LIST));
 
     MOZ_ASSERT(this->stackDepth > 0);                             // ... RHS
 
     if (!emitRequireObjectCoercible())                            // ... RHS
         return false;
 
     for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) {
+        if (member->isKind(PNK_SPREAD)) {
+            // FIXME: Implement
+            continue;
+        }
+
         ParseNode* subpattern;
         if (member->isKind(PNK_MUTATEPROTO))
             subpattern = member->pn_kid;
         else
             subpattern = member->pn_right;
         ParseNode* lhs = subpattern;
         if (lhs->isKind(PNK_ASSIGN))
             lhs = lhs->pn_left;
@@ -9715,16 +9720,23 @@ BytecodeEmitter::emitPropertyList(ParseN
             if (!emitTree(propdef->pn_kid))
                 return false;
             objp.set(nullptr);
             if (!emit1(JSOP_MUTATEPROTO))
                 return false;
             continue;
         }
 
+        if (propdef->isKind(PNK_SPREAD)) {
+            MOZ_ASSERT(type == ObjectLiteral);
+            objp.set(nullptr);
+            // FIXME: implement
+            continue;
+        }
+
         bool extraPop = false;
         if (type == ClassBody && propdef->as<ClassMethod>().isStatic()) {
             extraPop = true;
             if (!emit1(JSOP_DUP2))
                 return false;
             if (!emit1(JSOP_POP))
                 return false;
         }
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -365,16 +365,28 @@ class FullParseHandlerBase
         setListFlag(literal, PNX_NONCONST);
         ParseNode* propdef = newBinary(PNK_SHORTHAND, name, expr, JSOP_INITPROP);
         if (!propdef)
             return false;
         literal->append(propdef);
         return true;
     }
 
+    MOZ_MUST_USE bool addSpreadProperty(ParseNode* literal, uint32_t begin, ParseNode* inner) {
+        MOZ_ASSERT(literal->isKind(PNK_OBJECT));
+        MOZ_ASSERT(literal->isArity(PN_LIST));
+
+        setListFlag(literal, PNX_NONCONST);
+        ParseNode* spread = newSpread(begin, inner);
+        if (!spread)
+            return false;
+        literal->append(spread);
+        return true;
+    }
+
     MOZ_MUST_USE bool addObjectMethodDefinition(ParseNode* literal, ParseNode* key, ParseNode* fn,
                                                 JSOp op)
     {
         MOZ_ASSERT(literal->isArity(PN_LIST));
         MOZ_ASSERT(key->isKind(PNK_NUMBER) ||
                    key->isKind(PNK_OBJECT_PROPERTY_NAME) ||
                    key->isKind(PNK_STRING) ||
                    key->isKind(PNK_COMPUTED_NAME));
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -4408,19 +4408,29 @@ Parser<ParseHandler, CharT>::objectBindi
     for (;;) {
         TokenKind tt;
         if (!tokenStream.peekToken(&tt))
             return null();
         if (tt == TOK_RC)
             break;
 
         if (tt == TOK_TRIPLEDOT) {
-            // TODO: rest-binding property
-            error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt));
-            return null();
+            tokenStream.consumeKnownToken(TOK_TRIPLEDOT);
+            uint32_t begin = pos().begin;
+
+            TokenKind tt;
+            if (!tokenStream.getToken(&tt))
+                return null();
+
+            Node inner = bindingIdentifierOrPattern(kind, yieldHandling, tt);
+            if (!inner)
+                return null();
+
+            if (!handler.addSpreadProperty(literal, begin, inner))
+                return null();
         } else {
             TokenPos namePos = tokenStream.nextToken().pos;
 
             PropertyType propType;
             Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom);
             if (!propName)
                 return null();
 
@@ -4480,16 +4490,20 @@ Parser<ParseHandler, CharT>::objectBindi
             }
         }
 
         bool matched;
         if (!tokenStream.matchToken(&matched, TOK_COMMA))
             return null();
         if (!matched)
             break;
+        if (tt == TOK_TRIPLEDOT) {
+            error(JSMSG_REST_WITH_COMMA);
+            return null();
+        }
     }
 
     MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::None,
                                      reportMissingClosing(JSMSG_CURLY_AFTER_LIST,
                                                           JSMSG_CURLY_OPENED, begin));
 
     handler.setEndPosition(literal, pos().end);
     return literal;
@@ -9785,19 +9799,31 @@ Parser<ParseHandler, CharT>::objectLiter
     for (;;) {
         TokenKind tt;
         if (!tokenStream.peekToken(&tt))
             return null();
         if (tt == TOK_RC)
             break;
 
         if (tt == TOK_TRIPLEDOT) {
-            // TODO: object spread
-            error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt));
-            return null();
+            tokenStream.consumeKnownToken(TOK_TRIPLEDOT);
+            uint32_t begin = pos().begin;
+
+            TokenPos innerPos;
+            if (!tokenStream.peekTokenPos(&innerPos, TokenStream::Operand))
+                return null();
+
+            Node inner = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
+                                    possibleError);
+            if (!inner)
+                return null();
+            if (possibleError)
+                checkDestructuringAssignmentTarget(inner, innerPos, possibleError);
+            if (!handler.addSpreadProperty(literal, begin, inner))
+                return null();
         } else {
             TokenPos namePos = tokenStream.nextToken().pos;
 
             PropertyType propType;
             Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom);
             if (!propName)
                 return null();
 
@@ -9951,16 +9977,18 @@ Parser<ParseHandler, CharT>::objectLiter
             }
         }
 
         bool matched;
         if (!tokenStream.matchToken(&matched, TOK_COMMA))
             return null();
         if (!matched)
             break;
+        if (tt == TOK_TRIPLEDOT && possibleError)
+            possibleError->setPendingDestructuringErrorAt(pos(), JSMSG_REST_WITH_COMMA);
     }
 
     MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::None,
                                      reportMissingClosing(JSMSG_CURLY_AFTER_LIST,
                                                           JSMSG_CURLY_OPENED, openedPos));
 
     handler.setEndPosition(literal, pos().end);
     return literal;
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -290,16 +290,17 @@ class SyntaxParseHandlerBase
 
     Node newNewTarget(Node newHolder, Node targetHolder) { return NodeGeneric; }
     Node newPosHolder(const TokenPos& pos) { return NodeGeneric; }
     Node newSuperBase(Node thisName, const TokenPos& pos) { return NodeSuperBase; }
 
     MOZ_MUST_USE bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; }
     MOZ_MUST_USE bool addPropertyDefinition(Node literal, Node name, Node expr) { return true; }
     MOZ_MUST_USE bool addShorthand(Node literal, Node name, Node expr) { return true; }
+    MOZ_MUST_USE bool addSpreadProperty(Node literal, uint32_t begin, Node inner) { return true; }
     MOZ_MUST_USE bool addObjectMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; }
     MOZ_MUST_USE bool addClassMethodDefinition(Node literal, Node name, Node fn, JSOp op, bool isStatic) { return true; }
     Node newYieldExpression(uint32_t begin, Node value) { return NodeGeneric; }
     Node newYieldStarExpression(uint32_t begin, Node value) { return NodeGeneric; }
     Node newAwaitExpression(uint32_t begin, Node value) { return NodeGeneric; }
 
     // Statements