From 091d00f1bd22f821d3926c46dd1d6cf7fbc746f4 Mon Sep 17 00:00:00 2001
From: Gaming4JC <g4jc@hyperbola.info>
Date: Sun, 14 Jul 2019 16:37:57 -0400
Subject: 1339395 - Part 2: Add parser support for rest and spread object
 properties.

---
 js/src/builtin/ReflectParse.cpp      | 12 +++++++++++
 js/src/frontend/BytecodeEmitter.cpp  | 12 +++++++++++
 js/src/frontend/FullParseHandler.h   | 12 +++++++++++
 js/src/frontend/Parser.cpp           | 42 ++++++++++++++++++++++++++++++------
 js/src/frontend/SyntaxParseHandler.h |  1 +
 5 files changed, 73 insertions(+), 6 deletions(-)

(limited to 'js')

diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp
index 3495ede2f..8e8bb2417 100644
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -3222,6 +3222,8 @@ ASTSerializer::property(ParseNode* pn, MutableHandleValue dst)
         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()) {
@@ -3342,6 +3344,16 @@ ASTSerializer::objectPattern(ParseNode* pn, MutableHandleValue dst)
         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);
diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
index c2eae4344..f9a1f8675 100644
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -5815,6 +5815,11 @@ BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFla
         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;
@@ -9449,6 +9454,13 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp,
             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;
diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h
index d34cdf43d..2d7f57e1e 100644
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -395,6 +395,18 @@ class FullParseHandler
         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)
     {
diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
index 585eef59d..47e0f943d 100644
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -4367,9 +4367,20 @@ Parser<ParseHandler>::objectBindingPattern(DeclarationKind kind, YieldHandling y
             break;
 
         if (tt == TOK_TRIPLEDOT) {
-            // TODO: rest-binding property
-            error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt));
-            return null();
+            // rest-binding property
+            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;
 
@@ -4439,6 +4450,10 @@ Parser<ParseHandler>::objectBindingPattern(DeclarationKind kind, YieldHandling y
             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,
@@ -9693,9 +9708,22 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError*
             break;
 
         if (tt == TOK_TRIPLEDOT) {
-            // TODO: object spread
-            error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt));
-            return null();
+            // object spread
+            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;
 
@@ -9860,6 +9888,8 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError*
             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,
diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h
index 7de6a242e..a604b599f 100644
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -298,6 +298,7 @@ class SyntaxParseHandler
     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, Node gen) { return NodeGeneric; }
-- 
cgit v1.2.3