Allow any LeftHandSideExpression as a valid AssignmentElement
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 13 Nov 2015 19:13:39 +0000 (19:13 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 13 Nov 2015 19:13:39 +0000 (19:13 +0000)
https://bugs.webkit.org/show_bug.cgi?id=151026

Patch by Caitlin Potter <caitpotter88@gmail.com> on 2015-11-13
Reviewed by Geoffrey Garen.

* bytecompiler/NodesCodegen.cpp:
(JSC::AssignmentElementNode::collectBoundIdentifiers):
(JSC::AssignmentElementNode::bindValue):
(JSC::AssignmentElementNode::toString):
* parser/ASTBuilder.h:
(JSC::ASTBuilder::isAssignmentLocation):
(JSC::ASTBuilder::createAssignmentElement):
* parser/NodeConstructors.h:
(JSC::AssignmentElementNode::AssignmentElementNode):
* parser/Nodes.h:
(JSC::AssignmentElementNode::assignmentTarget):
(JSC::AssignmentElementNode::divotStart):
(JSC::AssignmentElementNode::divotEnd):
* parser/Parser.cpp:
(JSC::Parser<LexerType>::createAssignmentElement):
(JSC::Parser<LexerType>::parseDestructuringPattern):
* parser/Parser.h:
* parser/SyntaxChecker.h:
(JSC::SyntaxChecker::operatorStackPop):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@192436 268f45cc-cd09-0410-ab3c-d52691b4dbfc

13 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/parser/ASTBuilder.h
Source/JavaScriptCore/parser/NodeConstructors.h
Source/JavaScriptCore/parser/Nodes.h
Source/JavaScriptCore/parser/Parser.cpp
Source/JavaScriptCore/parser/Parser.h
Source/JavaScriptCore/parser/SyntaxChecker.h
Source/JavaScriptCore/tests/es6.yaml
Source/JavaScriptCore/tests/es6/destructuring_assignment_non_simple_target.js [new file with mode: 0644]
Source/JavaScriptCore/tests/es6/destructuring_initializer_scoping.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/destructuring-assignment-syntax.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/rest-elements.js

index f528e2b..35b2226 100644 (file)
@@ -1,3 +1,30 @@
+2015-11-13  Caitlin Potter  <caitpotter88@gmail.com>
+
+        Allow any LeftHandSideExpression as a valid AssignmentElement
+        https://bugs.webkit.org/show_bug.cgi?id=151026
+
+        Reviewed by Geoffrey Garen.
+
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::AssignmentElementNode::collectBoundIdentifiers):
+        (JSC::AssignmentElementNode::bindValue):
+        (JSC::AssignmentElementNode::toString):
+        * parser/ASTBuilder.h:
+        (JSC::ASTBuilder::isAssignmentLocation):
+        (JSC::ASTBuilder::createAssignmentElement):
+        * parser/NodeConstructors.h:
+        (JSC::AssignmentElementNode::AssignmentElementNode):
+        * parser/Nodes.h:
+        (JSC::AssignmentElementNode::assignmentTarget):
+        (JSC::AssignmentElementNode::divotStart):
+        (JSC::AssignmentElementNode::divotEnd):
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::createAssignmentElement):
+        (JSC::Parser<LexerType>::parseDestructuringPattern):
+        * parser/Parser.h:
+        * parser/SyntaxChecker.h:
+        (JSC::SyntaxChecker::operatorStackPop):
+
 2015-11-13  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         op_assert should declare that it uses the first register argument
index 873f4ab..4166671 100644 (file)
@@ -3397,7 +3397,65 @@ void BindingNode::collectBoundIdentifiers(Vector<Identifier>& identifiers) const
 {
     identifiers.append(m_boundProperty);
 }
-    
+
+void AssignmentElementNode::collectBoundIdentifiers(Vector<Identifier>&) const
+{
+}
+
+void AssignmentElementNode::bindValue(BytecodeGenerator& generator, RegisterID* value) const
+{
+    if (m_assignmentTarget->isResolveNode()) {
+        ResolveNode* lhs = static_cast<ResolveNode*>(m_assignmentTarget);
+        Variable var = generator.variable(lhs->identifier());
+        bool isReadOnly = var.isReadOnly();
+        if (RegisterID* local = var.local()) {
+            generator.emitTDZCheckIfNecessary(var, local, nullptr);
+
+            if (isReadOnly)
+                generator.emitReadOnlyExceptionIfNeeded(var);
+            else {
+                generator.invalidateForInContextForLocal(local);
+                generator.moveToDestinationIfNeeded(local, value);
+                generator.emitProfileType(local, divotStart(), divotEnd());
+            }
+            return;
+        }
+        if (generator.isStrictMode())
+            generator.emitExpressionInfo(divotEnd(), divotStart(), divotEnd());
+        RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var);
+        generator.emitTDZCheckIfNecessary(var, nullptr, scope.get());
+        if (isReadOnly) {
+            bool threwException = generator.emitReadOnlyExceptionIfNeeded(var);
+            if (threwException)
+                return;
+        }
+        generator.emitExpressionInfo(divotEnd(), divotStart(), divotEnd());
+        if (!isReadOnly) {
+            generator.emitPutToScope(scope.get(), var, value, generator.isStrictMode() ? ThrowIfNotFound : DoNotThrowIfNotFound, NotInitialization);
+            generator.emitProfileType(value, var, divotStart(), divotEnd());
+        }
+    } else if (m_assignmentTarget->isDotAccessorNode()) {
+        DotAccessorNode* lhs = static_cast<DotAccessorNode*>(m_assignmentTarget);
+        RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(lhs->base(), true, false);
+        generator.emitExpressionInfo(divotEnd(), divotStart(), divotEnd());
+        generator.emitPutById(base.get(), lhs->identifier(), value);
+        generator.emitProfileType(value, divotStart(), divotEnd());
+    } else if (m_assignmentTarget->isBracketAccessorNode()) {
+        BracketAccessorNode* lhs = static_cast<BracketAccessorNode*>(m_assignmentTarget);
+        RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(lhs->base(), true, false);
+        RefPtr<RegisterID> property = generator.emitNodeForLeftHandSide(lhs->subscript(), true, false);
+        generator.emitExpressionInfo(divotEnd(), divotStart(), divotEnd());
+        generator.emitPutByVal(base.get(), property.get(), value);
+        generator.emitProfileType(value, divotStart(), divotEnd());
+    }
+}
+
+void AssignmentElementNode::toString(StringBuilder& builder) const
+{
+    if (m_assignmentTarget->isResolveNode())
+        builder.append(static_cast<ResolveNode*>(m_assignmentTarget)->identifier().string());
+}
+
 RegisterID* SpreadExpressionNode::emitBytecode(BytecodeGenerator&, RegisterID*)
 {
     RELEASE_ASSERT_NOT_REACHED();
index 7c85ac0..6d75d60 100644 (file)
@@ -121,6 +121,7 @@ public:
     typedef ArrayPatternNode* ArrayPattern;
     typedef ObjectPatternNode* ObjectPattern;
     typedef BindingNode* BindingPattern;
+    typedef AssignmentElementNode* AssignmentElement;
     static const bool CreatesAST = true;
     static const bool NeedsFreeVariableInfo = true;
     static const bool CanUseFunctionCache = true;
@@ -524,6 +525,26 @@ public:
         return pattern->isBindingNode();
     }
 
+    bool isAssignmentLocation(const Expression& pattern)
+    {
+        return pattern->isAssignmentLocation();
+    }
+
+    bool isObjectLiteral(const Expression& node)
+    {
+        return node->isObjectLiteral();
+    }
+
+    bool isArrayLiteral(const Expression& node)
+    {
+        return node->isArrayLiteral();
+    }
+
+    bool isObjectOrArrayLiteral(const Expression& node)
+    {
+        return isObjectLiteral(node) || isArrayLiteral(node);
+    }
+
     StatementNode* createEmptyStatement(const JSTokenLocation& location) { return new (m_parserArena) EmptyStatementNode(location); }
 
     StatementNode* createDeclarationStatement(const JSTokenLocation& location, ExpressionNode* expr, int start, int end)
@@ -836,6 +857,11 @@ public:
         return new (m_parserArena) BindingNode(boundProperty, start, end, context);
     }
 
+    AssignmentElement createAssignmentElement(const Expression& assignmentTarget, const JSTextPosition& start, const JSTextPosition& end)
+    {
+        return new (m_parserArena) AssignmentElementNode(assignmentTarget, start, end);
+    }
+
     void setEndOffset(Node* node, int offset)
     {
         node->setEndOffset(offset);
index 1bce30e..bf512ab 100644 (file)
@@ -1020,7 +1020,15 @@ namespace JSC {
         , m_bindingContext(context)
     {
     }
-    
+
+    inline AssignmentElementNode::AssignmentElementNode(ExpressionNode* assignmentTarget, const JSTextPosition& start, const JSTextPosition& end)
+        : DestructuringPatternNode()
+        , m_divotStart(start)
+        , m_divotEnd(end)
+        , m_assignmentTarget(assignmentTarget)
+    {
+    }
+
     inline DestructuringAssignmentNode::DestructuringAssignmentNode(const JSTokenLocation& location, DestructuringPatternNode* bindings, ExpressionNode* initializer)
         : ExpressionNode(location)
         , m_bindings(bindings)
index cde49a9..13982c5 100644 (file)
@@ -153,6 +153,8 @@ namespace JSC {
 
         virtual bool isNumber() const { return false; }
         virtual bool isString() const { return false; }
+        virtual bool isObjectLiteral() const { return false; }
+        virtual bool isArrayLiteral() const { return false; }
         virtual bool isNull() const { return false; }
         virtual bool isPure(BytecodeGenerator&) const { return false; }        
         virtual bool isConstant() const { return false; }
@@ -592,6 +594,8 @@ namespace JSC {
         ArrayNode(const JSTokenLocation&, ElementNode*);
         ArrayNode(const JSTokenLocation&, int elision, ElementNode*);
 
+        virtual bool isArrayLiteral() const override { return true; }
+
         ArgumentListNode* toArgumentList(ParserArena&, int, int) const;
 
         ElementNode* elements() const { ASSERT(isSimpleArray()); return m_element; }
@@ -647,6 +651,7 @@ namespace JSC {
     public:
         ObjectLiteralNode(const JSTokenLocation&);
         ObjectLiteralNode(const JSTokenLocation&, PropertyListNode*);
+        virtual bool isObjectLiteral() const override { return true; }
 
     private:
         virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
@@ -2048,6 +2053,24 @@ namespace JSC {
         AssignmentContext m_bindingContext;
     };
 
+    class AssignmentElementNode : public DestructuringPatternNode {
+    public:
+        AssignmentElementNode(ExpressionNode* assignmentTarget, const JSTextPosition& start, const JSTextPosition& end);
+        const ExpressionNode* assignmentTarget() { return m_assignmentTarget; }
+
+        const JSTextPosition& divotStart() const { return m_divotStart; }
+        const JSTextPosition& divotEnd() const { return m_divotEnd; }
+
+    private:
+        virtual void collectBoundIdentifiers(Vector<Identifier>&) const override;
+        virtual void bindValue(BytecodeGenerator&, RegisterID*) const override;
+        virtual void toString(StringBuilder&) const override;
+
+        JSTextPosition m_divotStart;
+        JSTextPosition m_divotEnd;
+        ExpressionNode* m_assignmentTarget;
+    };
+
     class DestructuringAssignmentNode : public ExpressionNode {
     public:
         DestructuringAssignmentNode(const JSTokenLocation&, DestructuringPatternNode*, ExpressionNode*);
index 374bcb2..6072680 100644 (file)
@@ -731,6 +731,12 @@ template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::createB
 }
 
 template <typename LexerType>
+template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern Parser<LexerType>::createAssignmentElement(TreeBuilder& context, TreeExpression& assignmentTarget, const JSTextPosition& startPosition, const JSTextPosition& endPosition)
+{
+    return context.createAssignmentElement(assignmentTarget, startPosition, endPosition);
+}
+
+template <typename LexerType>
 template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseArrowFunctionSingleExpressionBodySourceElements(TreeBuilder& context)
 {
     ASSERT(!match(OPENBRACE));
@@ -766,6 +772,34 @@ template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::tryPars
 }
 
 template <typename LexerType>
+template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseBindingOrAssignmentElement(TreeBuilder& context, DestructuringKind kind, ExportType exportType, const Identifier** duplicateIdentifier, bool* hasDestructuringPattern, AssignmentContext bindingContext, int depth)
+{
+    if (kind == DestructureToExpressions)
+        return parseAssignmentElement(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth);
+    return parseDestructuringPattern(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth);
+}
+
+template <typename LexerType>
+template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseAssignmentElement(TreeBuilder& context, DestructuringKind kind, ExportType exportType, const Identifier** duplicateIdentifier, bool* hasDestructuringPattern, AssignmentContext bindingContext, int depth)
+{
+    SavePoint savePoint = createSavePoint();
+    TreeDestructuringPattern assignmentTarget = 0;
+
+    if (match(OPENBRACE) || match(OPENBRACKET))
+        assignmentTarget = parseDestructuringPattern(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth);
+    if (!assignmentTarget || match(DOT) || match(OPENBRACKET) || match(OPENPAREN) || match(TEMPLATE)) {
+        restoreSavePoint(savePoint);
+        JSTextPosition startPosition = tokenStartPosition();
+        auto element = parseMemberExpression(context);
+
+        semanticFailIfFalse(element && context.isAssignmentLocation(element), "Invalid destructuring assignment target");
+
+        return createAssignmentElement(context, element, startPosition, lastTokenEndPosition());
+    }
+    return assignmentTarget;
+}
+
+template <typename LexerType>
 template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDestructuringPattern(TreeBuilder& context, DestructuringKind kind, ExportType exportType, const Identifier** duplicateIdentifier, bool* hasDestructuringPattern, AssignmentContext bindingContext, int depth)
 {
     failIfStackOverflow();
@@ -795,7 +829,7 @@ template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDe
             if (UNLIKELY(match(DOTDOTDOT))) {
                 JSTokenLocation location = m_token.m_location;
                 next();
-                auto innerPattern = parseDestructuringPattern(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1);
+                auto innerPattern = parseBindingOrAssignmentElement(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1);
                 if (kind == DestructureToExpressions && !innerPattern)
                     return 0;
                 failIfFalse(innerPattern, "Cannot parse this destructuring pattern");
@@ -808,7 +842,7 @@ template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDe
             }
 
             JSTokenLocation location = m_token.m_location;
-            auto innerPattern = parseDestructuringPattern(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1);
+            auto innerPattern = parseBindingOrAssignmentElement(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1);
             if (kind == DestructureToExpressions && !innerPattern)
                 return 0;
             failIfFalse(innerPattern, "Cannot parse this destructuring pattern");
@@ -816,8 +850,6 @@ template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDe
             context.appendArrayPatternEntry(arrayPattern, location, innerPattern, defaultValue);
         } while (consume(COMMA));
 
-        if (kind == DestructureToExpressions && !match(CLOSEBRACKET))
-            return 0;
         consumeOrFail(CLOSEBRACKET, restElementWasFound ? "Expected a closing ']' following a rest element destructuring pattern" : "Expected either a closing ']' or a ',' following an element destructuring pattern");
         context.finishArrayPattern(arrayPattern, divotStart, divotStart, lastTokenEndPosition());
         pattern = arrayPattern;
@@ -845,7 +877,7 @@ template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDe
                 JSToken identifierToken = m_token;
                 next();
                 if (consume(COLON))
-                    innerPattern = parseDestructuringPattern(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1);
+                    innerPattern = parseBindingOrAssignmentElement(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1);
                 else
                     innerPattern = createBindingPattern(context, kind, exportType, *propertyName, depth + 1, identifierToken, bindingContext, duplicateIdentifier);
             } else {
@@ -878,7 +910,7 @@ template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDe
                     
                     failWithMessage("Expected a ':' prior to a named destructuring property");
                 }
-                innerPattern = parseDestructuringPattern(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1);
+                innerPattern = parseBindingOrAssignmentElement(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1);
             }
             if (kind == DestructureToExpressions && !innerPattern)
                 return 0;
@@ -2689,6 +2721,7 @@ template <typename TreeBuilder> TreeExpression Parser<LexerType>::parseAssignmen
     JSTokenLocation location(tokenLocation());
     int initialAssignmentCount = m_assignmentCount;
     int initialNonLHSCount = m_nonLHSCount;
+    String assignmentPatternError = String();
     if (match(OPENBRACE) || match(OPENBRACKET)) {
         SavePoint savePoint = createSavePoint();
         auto pattern = tryParseDestructuringPatternExpression(context, AssignmentContext::AssignmentExpression);
@@ -2697,6 +2730,7 @@ template <typename TreeBuilder> TreeExpression Parser<LexerType>::parseAssignmen
             if (rhs)
                 return context.createDestructuringAssignment(location, pattern, rhs);
         }
+        assignmentPatternError = m_errorMessage;
         restoreSavePoint(savePoint);
     }
 
@@ -2711,6 +2745,10 @@ template <typename TreeBuilder> TreeExpression Parser<LexerType>::parseAssignmen
 #endif
     
     TreeExpression lhs = parseConditionalExpression(context);
+    if (!assignmentPatternError.isNull() && (lhs && (context.isObjectOrArrayLiteral(lhs) && match(EQUAL)))) {
+        setErrorMessage(assignmentPatternError);
+        return 0;
+    }
     failIfFalse(lhs, "Cannot parse expression");
     if (initialNonLHSCount != m_nonLHSCount) {
         if (m_token.m_type >= EQUAL && m_token.m_type <= OREQUAL)
index e213e07..e8838f2 100644 (file)
@@ -1160,6 +1160,9 @@ private:
     template <class TreeBuilder> TreeSourceElements parseArrowFunctionSingleExpressionBodySourceElements(TreeBuilder&);
     template <class TreeBuilder> TreeExpression parseArrowFunctionExpression(TreeBuilder&);
     template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern createBindingPattern(TreeBuilder&, DestructuringKind, ExportType, const Identifier&, int depth, JSToken, AssignmentContext, const Identifier** duplicateIdentifier);
+    template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern createAssignmentElement(TreeBuilder&, TreeExpression&, const JSTextPosition&, const JSTextPosition&);
+    template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern parseBindingOrAssignmentElement(TreeBuilder& context, DestructuringKind, ExportType, const Identifier** duplicateIdentifier, bool* hasDestructuringPattern, AssignmentContext bindingContext, int depth);
+    template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern parseAssignmentElement(TreeBuilder& context, DestructuringKind, ExportType, const Identifier** duplicateIdentifier, bool* hasDestructuringPattern, AssignmentContext bindingContext, int depth);
     template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern parseDestructuringPattern(TreeBuilder&, DestructuringKind, ExportType, const Identifier** duplicateIdentifier = nullptr, bool* hasDestructuringPattern = nullptr, AssignmentContext = AssignmentContext::DeclarationStatement, int depth = 0);
     template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern tryParseDestructuringPatternExpression(TreeBuilder&, AssignmentContext);
     template <class TreeBuilder> NEVER_INLINE TreeExpression parseDefaultValueForDestructuringPattern(TreeBuilder&);
index 5d393a3..e3a867e 100644 (file)
@@ -346,12 +346,36 @@ public:
     {
         return BindingDestructuring;
     }
+    DestructuringPattern createAssignmentElement(const Expression&, const JSTextPosition&, const JSTextPosition&)
+    {
+        return BindingDestructuring;
+    }
 
     bool isBindingNode(DestructuringPattern pattern)
     {
         return pattern == BindingDestructuring;
     }
 
+    bool isAssignmentLocation(ExpressionType type)
+    {
+        return type == ResolveExpr || type == DotExpr || type == BracketExpr;
+    }
+
+    bool isObjectLiteral(ExpressionType type)
+    {
+        return type == ObjectLiteralExpr;
+    }
+
+    bool isArrayLiteral(ExpressionType type)
+    {
+        return type == ArrayLiteralExpr;
+    }
+
+    bool isObjectOrArrayLiteral(ExpressionType type)
+    {
+        return isObjectLiteral(type) || isArrayLiteral(type);
+    }
+
     void setEndOffset(int, int) { }
     int endOffset(int) { return 0; }
     void setStartOffset(int, int) { }
index 285c5f9..e05c742 100644 (file)
   cmd: runES6 :normal
 - path: es6/destructuring_with_strings.js
   cmd: runES6 :normal
+- path: es6/destructuring_assignment_non_simple_target.js
+  cmd: runES6 :normal
+- path: es6/destructuring_initializer_scoping.js
+  cmd: runES6 :normal
 - path: es6/for..of_loops_with_arrays.js
   cmd: runES6 :normal
 - path: es6/for..of_loops_with_astral_plane_strings.js
 - path: es6/destructuring_iterator_closing.js
   cmd: runES6 :fail
 - path: es6/destructuring_nested_rest.js
-  cmd: runES6 :fail
+  cmd: runES6 :normal
 - path: es6/destructuring_with_generator_instances.js
   cmd: runES6 :fail
 - path: es6/destructuring_with_generic_iterables.js
diff --git a/Source/JavaScriptCore/tests/es6/destructuring_assignment_non_simple_target.js b/Source/JavaScriptCore/tests/es6/destructuring_assignment_non_simple_target.js
new file mode 100644 (file)
index 0000000..ff82983
--- /dev/null
@@ -0,0 +1,16 @@
+function test() {
+    var msg = {};
+    ({
+        name: msg.name,
+        "parameters": [msg.callback, ...msg["parameters"]],
+        0: msg[0]
+    } = {
+        name: "test",
+        parameters: [function cb() { return "test"; }, "a", "b", "c"],
+        "0": NaN
+    });
+    return msg.name === "test" && msg[0] !== msg[0] && msg.callback() === "test" && (msg.parameters + "") === "a,b,c";
+}
+
+if (!test())
+    throw new Error("Test failed");
diff --git a/Source/JavaScriptCore/tests/es6/destructuring_initializer_scoping.js b/Source/JavaScriptCore/tests/es6/destructuring_initializer_scoping.js
new file mode 100644 (file)
index 0000000..5f3b86a
--- /dev/null
@@ -0,0 +1,17 @@
+var outer = [];
+function test() {
+    var a = {};
+    var defaultObj = {
+        name: "default",
+        length: 3,
+        0: "a",
+        1: "b",
+        2: "c",
+        [Symbol.iterator]: Array.prototype[Symbol.iterator]
+    };
+    function tester({ name } = { name: a.name } = [outer[0], ...outer[1]] = defaultObj) { return name; }
+    return tester() === "default" && a.name === "default" && (outer + "") === "a,b,c";
+}
+
+if (!test())
+    throw new Error("Test failed");
diff --git a/Source/JavaScriptCore/tests/stress/destructuring-assignment-syntax.js b/Source/JavaScriptCore/tests/stress/destructuring-assignment-syntax.js
new file mode 100644 (file)
index 0000000..9077481
--- /dev/null
@@ -0,0 +1,59 @@
+
+function testSyntax(script) {
+    try {
+        eval(script);
+    } catch (error) {
+        if (error instanceof SyntaxError)
+            throw new Error("Bad error: " + String(error));
+    }
+}
+
+function testSyntaxError(script, message) {
+    var error = null;
+    try {
+        eval(script);
+    } catch (e) {
+        error = e;
+    }
+    if (!error)
+        throw new Error("Expected syntax error not thrown");
+
+    if (String(error) !== message)
+        throw new Error("Bad error: " + String(error));
+}
+
+testSyntax("({ a: this.a } = {})");
+testSyntax("({ a: this['a'] } = {})");
+testSyntax("({ a: this[\"a\"] } = {})");
+testSyntax("[this.a ] = []");
+testSyntax("[this['a']] = []");
+testSyntax("[this[0]] = []");
+testSyntax("[...this[0]] = []");
+testSyntax("[...[function f() {}.prop]] = []");
+testSyntax("[...[{prop: 1}.prop]] = []");
+testSyntax("[...[this[0], ...this[1]]] = []");
+testSyntax("({ a: obj.a } = {})");
+testSyntax("({ a: obj['a'] } = {})");
+testSyntax("({ a: obj[\"a\"] } = {})");
+testSyntax("({ a: function() {}['prop'] } = {})");
+testSyntax("({ a: {prop: 1}.prop } = {})");
+testSyntax("[obj.a ] = []");
+testSyntax("[obj['a']] = []");
+testSyntax("[obj[0]] = []");
+testSyntax("[function(){}.prop] = []");
+testSyntax("[{prop: 1}.prop] = []");
+
+
+testSyntaxError("[...c = 1] = []", "SyntaxError: Unexpected token '='. Expected a closing ']' following a rest element destructuring pattern.");
+testSyntaxError("[...c, d] = []", "SyntaxError: Unexpected token ','. Expected a closing ']' following a rest element destructuring pattern.");
+testSyntaxError("[this] = []", "SyntaxError: Invalid destructuring assignment target.");
+testSyntaxError("[th\\u{69}s] = []", "SyntaxError: Invalid destructuring assignment target.");
+testSyntaxError("[function() {}] = []", "SyntaxError: Invalid destructuring assignment target.");
+testSyntaxError("['string'] = []", "SyntaxError: Invalid destructuring assignment target.");
+testSyntaxError("[123] = []", "SyntaxError: Invalid destructuring assignment target.");
+testSyntaxError("[true] = []", "SyntaxError: Invalid destructuring assignment target.");
+testSyntaxError("[tru\\u0065] = []", "SyntaxError: Invalid destructuring assignment target.");
+testSyntaxError("[false] = []", "SyntaxError: Invalid destructuring assignment target.");
+testSyntaxError("[f\\u0061lse] = []", "SyntaxError: Invalid destructuring assignment target.");
+testSyntaxError("[null] = []", "SyntaxError: Invalid destructuring assignment target.");
+testSyntaxError("[n\\u{75}ll] = []", "SyntaxError: Invalid destructuring assignment target.");
index 8c96d5c..9d251f8 100644 (file)
@@ -109,21 +109,11 @@ testSyntaxError(String.raw`(function ([a, ...b = 20,,]) { })`, String.raw`Syntax
 testSyntaxError(String.raw`(function ([a, ...[b, c]]) { })`, String.raw`SyntaxError: Unexpected token ']'. Expected identifier for a rest element destructuring pattern.`);
 testSyntaxError(String.raw`(function ([a, ...{ b, c }]) { })`, String.raw`SyntaxError: Unexpected token ']'. Expected identifier for a rest element destructuring pattern.`);
 
-shouldThrow(function () {
-    [a, ...b, c] = 20;
-}, "ReferenceError: Left side of assignment is not a reference.");
 
-shouldThrow(function () {
-    [a, ...b,] = 20
-}, "ReferenceError: Left side of assignment is not a reference.");
-
-shouldThrow(function () {
-    [a, ...b,,] = 20
-}, "ReferenceError: Left side of assignment is not a reference.");
-
-shouldThrow(function () {
-    [a, ...b = 20] = 20
-}, "ReferenceError: Left side of assignment is not a reference.");
+testSyntaxError(String.raw`[a, ...b, c] = 20`, String.raw`SyntaxError: Unexpected token ','. Expected a closing ']' following a rest element destructuring pattern.`);
+testSyntaxError(String.raw`[a, ...b,] = 20`, String.raw`SyntaxError: Unexpected token ','. Expected a closing ']' following a rest element destructuring pattern.`);
+testSyntaxError(String.raw`[a, ...b,,] = 20`, String.raw`SyntaxError: Unexpected token ','. Expected a closing ']' following a rest element destructuring pattern.`);
+testSyntaxError(String.raw`[a, ...b = 20] = 20`, String.raw`SyntaxError: Unexpected token '='. Expected a closing ']' following a rest element destructuring pattern.`);
 
 (function () {
     var a, b, c;