Title: [159139] trunk
Revision
159139
Author
[email protected]
Date
2013-11-12 12:55:29 -0800 (Tue, 12 Nov 2013)

Log Message

Support unprefixed deconstructing assignment
https://bugs.webkit.org/show_bug.cgi?id=124172

Reviewed by Mark Lam.

Source/_javascript_Core:

Add support for unprefixed descontructive assignment.

Happily non-reference types on the left hand side of an assignment
are a runtime error, so we're able to defer validation of the binding
pattern to codegen time when we're already doing a lot more work.

We're also able to predicate our attempt to parse on the existence of
'[' or '{' as they are not as common as other constructs.

* bytecompiler/NodesCodegen.cpp:
(JSC::ArrayPatternNode::emitDirectBinding):
* parser/ASTBuilder.h:
* parser/Parser.cpp:
(JSC::::createBindingPattern):
(JSC::::tryParseDeconstructionPatternExpression):
(JSC::::parseDeconstructionPattern):
(JSC::::parseForStatement):
(JSC::::parseAssignmentExpression):
* parser/Parser.h:
(JSC::Parser::createSavePoint):
(JSC::Parser::restoreSavePoint):
* parser/SyntaxChecker.h:

LayoutTests:

Add and expand testing of destructuring assignment

* js/basic-for-of-expected.txt:
* js/destructuring-assignment-expected.txt:
* js/parser-syntax-check-expected.txt:
* js/script-tests/basic-for-of.js:
* js/script-tests/destructuring-assignment.js:
(testDestructuring):
* js/script-tests/parser-syntax-check.js:

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (159138 => 159139)


--- trunk/LayoutTests/ChangeLog	2013-11-12 20:50:45 UTC (rev 159138)
+++ trunk/LayoutTests/ChangeLog	2013-11-12 20:55:29 UTC (rev 159139)
@@ -1,3 +1,20 @@
+2013-11-11  Oliver Hunt  <[email protected]>
+
+        Support unprefixed deconstructing assignment
+        https://bugs.webkit.org/show_bug.cgi?id=124172
+
+        Reviewed by Mark Lam.
+
+        Add and expand testing of destructuring assignment
+
+        * js/basic-for-of-expected.txt:
+        * js/destructuring-assignment-expected.txt:
+        * js/parser-syntax-check-expected.txt:
+        * js/script-tests/basic-for-of.js:
+        * js/script-tests/destructuring-assignment.js:
+        (testDestructuring):
+        * js/script-tests/parser-syntax-check.js:
+
 2013-11-12  Roger Fong  <[email protected]>
 
 		[Windows] Unreviewed gardening of some media tests.

Modified: trunk/LayoutTests/js/basic-for-of-expected.txt (159138 => 159139)


--- trunk/LayoutTests/js/basic-for-of-expected.txt	2013-11-12 20:50:45 UTC (rev 159138)
+++ trunk/LayoutTests/js/basic-for-of-expected.txt	2013-11-12 20:55:29 UTC (rev 159139)
@@ -63,6 +63,33 @@
 PASS value is testArray[key]
 PASS key is i
 PASS testArray.length is 9
+PASS value is testArray[key]
+PASS key is i
+PASS value is testArray[key]
+PASS key is i
+PASS value is testArray[key]
+PASS key is i
+PASS value is testArray[key]
+PASS key is i
+PASS value is testArray[key]
+PASS key is i
+PASS value is testArray[key]
+PASS key is i
+PASS value is testArray[key]
+PASS key is i
+PASS value is testArray[key]
+PASS key is i
+PASS value is testArray[key]
+PASS key is i
+PASS value is testArray[key]
+PASS key is i
+PASS value is testArray[key]
+PASS key is i
+PASS value is testArray[key]
+PASS key is i
+PASS value is testArray[key]
+PASS key is i
+PASS testArray.length is 13
 PASS successfullyParsed is true
 
 TEST COMPLETE

Modified: trunk/LayoutTests/js/destructuring-assignment-expected.txt (159138 => 159139)


--- trunk/LayoutTests/js/destructuring-assignment-expected.txt	2013-11-12 20:50:45 UTC (rev 159138)
+++ trunk/LayoutTests/js/destructuring-assignment-expected.txt	2013-11-12 20:55:29 UTC (rev 159139)
@@ -7,58 +7,75 @@
 Function as String: (function([a,b]) { return a+b;})
 PASS (function([a,b]) { return a+b;})(['1','2']) is '12'
 PASS (function ([a,b]) { return a+b;})(['1','2']) is '12'
+PASS ([a,b]=['1','2']); var r=a+b; r is '12'
+PASS [a,b]=['1','2']; var r=a+b; r is '12'
 PASS var {a,b}={a:'1',b:'2'}; var r=a+b; r is '12'
 Function as String: (function({a,b}) { return a+b;})
 PASS (function({a,b}) { return a+b;})({a:'1',b:'2'}) is '12'
 PASS (function ({a:a,b:b}) { return a+b;})({a:'1',b:'2'}) is '12'
+PASS ({a,b}={a:'1',b:'2'}); var r=a+b; r is '12'
 PASS var {c:a,d:b}={c:'1',d:'2'}; var r=a+b; r is '12'
 Function as String: (function({c:a,d:b}) { return a+b;})
 PASS (function({c:a,d:b}) { return a+b;})({c:'1',d:'2'}) is '12'
 PASS (function ({c:a,d:b}) { return a+b;})({c:'1',d:'2'}) is '12'
+PASS ({c:a,d:b}={c:'1',d:'2'}); var r=a+b; r is '12'
 PASS var {c:b,d:a}={c:'1',d:'2'}; var r=a+b; r is '21'
 Function as String: (function({c:b,d:a}) { return a+b;})
 PASS (function({c:b,d:a}) { return a+b;})({c:'1',d:'2'}) is '21'
 PASS (function ({c:b,d:a}) { return a+b;})({c:'1',d:'2'}) is '21'
+PASS ({c:b,d:a}={c:'1',d:'2'}); var r=a+b; r is '21'
 PASS var {true:a,false:b,undefined:c,null:d,in:e,for:f,1.5:g,'foo bar':h}={true:'a',false:'b',undefined:'c',null:'d',in:'e',for:'f',1.5:'g','foo bar':'h'}; var r=a+b+c+d+e+f+g+h; r is 'abcdefgh'
 Function as String: (function({true:a,false:b,undefined:c,null:d,in:e,for:f,1.5:g,'foo bar':h}) { return a+b+c+d+e+f+g+h;})
 PASS (function({true:a,false:b,undefined:c,null:d,in:e,for:f,1.5:g,'foo bar':h}) { return a+b+c+d+e+f+g+h;})({true:'a',false:'b',undefined:'c',null:'d',in:'e',for:'f',1.5:'g','foo bar':'h'}) is 'abcdefgh'
 PASS (function ({true:a,false:b,undefined:c,null:d,in:e,for:f,1.5:g,"foo bar":h}) { return a+b+c+d+e+f+g+h;})({true:'a',false:'b',undefined:'c',null:'d',in:'e',for:'f',1.5:'g','foo bar':'h'}) is 'abcdefgh'
+PASS ({true:a,false:b,undefined:c,null:d,in:e,for:f,1.5:g,'foo bar':h}={true:'a',false:'b',undefined:'c',null:'d',in:'e',for:'f',1.5:'g','foo bar':'h'}); var r=a+b+c+d+e+f+g+h; r is 'abcdefgh'
 PASS var [{c:a,d:b}]=[{c:'1',d:'2'}]; var r=a+b; r is '12'
 Function as String: (function([{c:a,d:b}]) { return a+b;})
 PASS (function([{c:a,d:b}]) { return a+b;})([{c:'1',d:'2'}]) is '12'
 PASS (function ([{c:a,d:b}]) { return a+b;})([{c:'1',d:'2'}]) is '12'
+PASS ([{c:a,d:b}]=[{c:'1',d:'2'}]); var r=a+b; r is '12'
+PASS [{c:a,d:b}]=[{c:'1',d:'2'}]; var r=a+b; r is '12'
 PASS var {x:[{c:a,d:b}]}={x:[{c:'1',d:'2'}]}; var r=a+b; r is '12'
 Function as String: (function({x:[{c:a,d:b}]}) { return a+b;})
 PASS (function({x:[{c:a,d:b}]}) { return a+b;})({x:[{c:'1',d:'2'}]}) is '12'
 PASS (function ({x:[{c:a,d:b}]}) { return a+b;})({x:[{c:'1',d:'2'}]}) is '12'
+PASS ({x:[{c:a,d:b}]}={x:[{c:'1',d:'2'}]}); var r=a+b; r is '12'
 PASS var [a,b]=anArray; var r=a+b; r is '12'
 Function as String: (function([a,b]) { return a+b;})
 PASS (function([a,b]) { return a+b;})(anArray) is '12'
 PASS (function ([a,b]) { return a+b;})(anArray) is '12'
+PASS ([a,b]=anArray); var r=a+b; r is '12'
+PASS [a,b]=anArray; var r=a+b; r is '12'
 PASS var {a,b}=anArray; var r=a+b; r is '34'
 Function as String: (function({a,b}) { return a+b;})
 PASS (function({a,b}) { return a+b;})(anArray) is '34'
 PASS (function ({a:a,b:b}) { return a+b;})(anArray) is '34'
+PASS ({a,b}=anArray); var r=a+b; r is '34'
 PASS var {a:a,b:b}=anArray; var r=a+b; r is '34'
 Function as String: (function({a:a,b:b}) { return a+b;})
 PASS (function({a:a,b:b}) { return a+b;})(anArray) is '34'
 PASS (function ({a:a,b:b}) { return a+b;})(anArray) is '34'
+PASS ({a:a,b:b}=anArray); var r=a+b; r is '34'
 PASS var {a,b}=anObject; var r=a+b; r is '12'
 Function as String: (function({a,b}) { return a+b;})
 PASS (function({a,b}) { return a+b;})(anObject) is '12'
 PASS (function ({a:a,b:b}) { return a+b;})(anObject) is '12'
+PASS ({a,b}=anObject); var r=a+b; r is '12'
 PASS var {a:a,b:b}=anObject; var r=a+b; r is '12'
 Function as String: (function({a:a,b:b}) { return a+b;})
 PASS (function({a:a,b:b}) { return a+b;})(anObject) is '12'
 PASS (function ({a:a,b:b}) { return a+b;})(anObject) is '12'
+PASS ({a:a,b:b}=anObject); var r=a+b; r is '12'
 PASS var {0:a,1:b}=anObject; var r=a+b; r is '34'
 Function as String: (function({0:a,1:b}) { return a+b;})
 PASS (function({0:a,1:b}) { return a+b;})(anObject) is '34'
 PASS (function ({0:a,1:b}) { return a+b;})(anObject) is '34'
+PASS ({0:a,1:b}=anObject); var r=a+b; r is '34'
 PASS var {'a':a,'b':b}=anObject; var r=a+b; r is '12'
 Function as String: (function({'a':a,'b':b}) { return a+b;})
 PASS (function({'a':a,'b':b}) { return a+b;})(anObject) is '12'
 PASS (function ({"a":a,"b":b}) { return a+b;})(anObject) is '12'
+PASS ({'a':a,'b':b}=anObject); var r=a+b; r is '12'
 PASS a+b is '1122'
 PASS a+b is '2211'
 PASS testDeconstructArgs('1', '2') is '12'

Modified: trunk/LayoutTests/js/parser-syntax-check-expected.txt (159138 => 159139)


--- trunk/LayoutTests/js/parser-syntax-check-expected.txt	2013-11-12 20:50:45 UTC (rev 159138)
+++ trunk/LayoutTests/js/parser-syntax-check-expected.txt	2013-11-12 20:55:29 UTC (rev 159139)
@@ -615,6 +615,10 @@
 PASS Valid:   "function f() { for (var [of] in of){} }"
 PASS Valid:   "for (var {of} in of){}"
 PASS Valid:   "function f() { for (var {of} in of){} }"
+PASS Valid:   "for ([of] in of){}"
+PASS Valid:   "function f() { for ([of] in of){} }"
+PASS Valid:   "for ({of} in of){}"
+PASS Valid:   "function f() { for ({of} in of){} }"
 PASS Invalid: "for (of of of of){}"
 PASS Invalid: "function f() { for (of of of of){} }"
 PASS Invalid: "for (of of; of; of){}"
@@ -678,8 +682,16 @@
 PASS Valid:   "function f() { [...bar,,,,] }"
 PASS Valid:   "[,,,,...bar]"
 PASS Valid:   "function f() { [,,,,...bar] }"
-PASS Valid:   "({x: 1})"
-PASS Valid:   "function f() { ({x: 1}) }"
+PASS Valid:   "({1: x})"
+PASS Valid:   "function f() { ({1: x}) }"
+PASS Valid:   "({1: x}=1)"
+PASS Valid:   "function f() { ({1: x}=1) }"
+PASS Valid:   "var {1:x}=1"
+PASS Valid:   "function f() { var {1:x}=1 }"
+PASS Valid:   "[x]=1"
+PASS Valid:   "function f() { [x]=1 }"
+PASS Valid:   "var [x]=1"
+PASS Valid:   "function f() { var [x]=1 }"
 PASS Valid:   "({[x]: 1})"
 PASS Valid:   "function f() { ({[x]: 1}) }"
 PASS Invalid: "({get [x](){}})"

Modified: trunk/LayoutTests/js/script-tests/basic-for-of.js (159138 => 159139)


--- trunk/LayoutTests/js/script-tests/basic-for-of.js	2013-11-12 20:50:45 UTC (rev 159138)
+++ trunk/LayoutTests/js/script-tests/basic-for-of.js	2013-11-12 20:55:29 UTC (rev 159139)
@@ -54,3 +54,21 @@
         testArray[4] = 5
 }
 shouldBe("testArray.length", String(i))
+
+
+var i = 0;
+for ([key, value] of testArray.entries()) {
+    shouldBe("value", "testArray[key]")
+    shouldBe("key", "i")
+    i++
+    if (i % 2 == 0)
+        testArray[i] *= 2;
+    if (i < 4)
+        testArray.push(testArray.length)
+    if (i == 4)
+        delete testArray[4]
+    if (i == 5)
+        testArray[4] = 5
+}
+shouldBe("testArray.length", String(i))
+

Modified: trunk/LayoutTests/js/script-tests/destructuring-assignment.js (159138 => 159139)


--- trunk/LayoutTests/js/script-tests/destructuring-assignment.js	2013-11-12 20:50:45 UTC (rev 159138)
+++ trunk/LayoutTests/js/script-tests/destructuring-assignment.js	2013-11-12 20:55:29 UTC (rev 159139)
@@ -7,6 +7,9 @@
     debug("Function as String: " + functionString);
     shouldBe(functionString + "(" + _expression_ + ")", result);
     shouldBe("(" + eval(functionString) + ")(" + _expression_ + ")", result);
+    shouldBe("(" + pattern + "=" + _expression_ + "); var r="+expr+"; r", result);
+    if (pattern[0] == '[')
+        shouldBe("" + pattern + "=" + _expression_ + "; var r="+expr+"; r", result);
 }
 
 testDestructuring("[a,b]", "['1','2']", "'12'");

Modified: trunk/LayoutTests/js/script-tests/parser-syntax-check.js (159138 => 159139)


--- trunk/LayoutTests/js/script-tests/parser-syntax-check.js	2013-11-12 20:50:45 UTC (rev 159138)
+++ trunk/LayoutTests/js/script-tests/parser-syntax-check.js	2013-11-12 20:55:29 UTC (rev 159139)
@@ -387,6 +387,8 @@
 invalid("for (var of[of] in of){}")
 valid("for (var [of] in of){}")
 valid("for (var {of} in of){}")
+valid("for ([of] in of){}")
+valid("for ({of} in of){}")
 
 
 invalid("for (of of of of){}")
@@ -422,7 +424,11 @@
 valid("[...bar, a]")
 valid("[...bar,,,,]")
 valid("[,,,,...bar]")
-valid("({x: 1})")
+valid("({1: x})")
+valid("({1: x}=1)")
+valid("var {1:x}=1")
+valid("[x]=1")
+valid("var [x]=1")
 valid("({[x]: 1})")
 invalid("({get [x](){}})")
 invalid("({set [x](){}})")

Modified: trunk/Source/_javascript_Core/ChangeLog (159138 => 159139)


--- trunk/Source/_javascript_Core/ChangeLog	2013-11-12 20:50:45 UTC (rev 159138)
+++ trunk/Source/_javascript_Core/ChangeLog	2013-11-12 20:55:29 UTC (rev 159139)
@@ -1,3 +1,33 @@
+2013-11-11  Oliver Hunt  <[email protected]>
+
+        Support unprefixed deconstructing assignment
+        https://bugs.webkit.org/show_bug.cgi?id=124172
+
+        Reviewed by Mark Lam.
+
+        Add support for unprefixed descontructive assignment.
+
+        Happily non-reference types on the left hand side of an assignment
+        are a runtime error, so we're able to defer validation of the binding
+        pattern to codegen time when we're already doing a lot more work.
+
+        We're also able to predicate our attempt to parse on the existence of
+        '[' or '{' as they are not as common as other constructs. 
+
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::ArrayPatternNode::emitDirectBinding):
+        * parser/ASTBuilder.h:
+        * parser/Parser.cpp:
+        (JSC::::createBindingPattern):
+        (JSC::::tryParseDeconstructionPatternExpression):
+        (JSC::::parseDeconstructionPattern):
+        (JSC::::parseForStatement):
+        (JSC::::parseAssignmentExpression):
+        * parser/Parser.h:
+        (JSC::Parser::createSavePoint):
+        (JSC::Parser::restoreSavePoint):
+        * parser/SyntaxChecker.h:
+
 2013-11-12  Andy Estes  <[email protected]>
 
         Run _javascript_Core Objective-C API tests on all supported platforms

Modified: trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp (159138 => 159139)


--- trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp	2013-11-12 20:50:45 UTC (rev 159138)
+++ trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp	2013-11-12 20:55:29 UTC (rev 159139)
@@ -2333,11 +2333,17 @@
             generator.emitGetArgumentByVal(temp.get(), generator.uncheckedRegisterForArguments(), temp.get());
             target->bindValue(generator, temp.get());
         }
-        return generator.emitLoad(generator.finalDestination(dst), jsUndefined());
+        if (dst == generator.ignoredResult() || !dst)
+            return generator.emitLoad(generator.finalDestination(dst), jsUndefined());
+        Local local = generator.local(generator.vm()->propertyNames->arguments);
+        return generator.moveToDestinationIfNeeded(dst, local.get());
     }
     if (!rhs->isSimpleArray())
         return 0;
 
+    RefPtr<RegisterID> resultRegister;
+    if (dst && dst != generator.ignoredResult())
+        resultRegister = generator.emitNewArray(generator.newTemporary(), 0, 0);
     ElementNode* elementNodes = static_cast<ArrayNode*>(rhs)->elements();
     Vector<ExpressionNode*> elements;
     for (; elementNodes; elementNodes = elementNodes->next())
@@ -2349,13 +2355,16 @@
     for (size_t i = 0; i < m_targetPatterns.size(); i++) {
         registers.uncheckedAppend(generator.newTemporary());
         generator.emitNode(registers.last().get(), elements[i]);
+        if (resultRegister)
+            generator.emitPutByIndex(resultRegister.get(), i, registers.last().get());
     }
     
     for (size_t i = 0; i < m_targetPatterns.size(); i++) {
         if (m_targetPatterns[i])
             m_targetPatterns[i]->bindValue(generator, registers[i].get());
     }
-
+    if (resultRegister)
+        return generator.moveToDestinationIfNeeded(dst, resultRegister.get());
     return generator.emitLoad(generator.finalDestination(dst), jsUndefined());
 }
 

Modified: trunk/Source/_javascript_Core/parser/Parser.cpp (159138 => 159139)


--- trunk/Source/_javascript_Core/parser/Parser.cpp	2013-11-12 20:50:45 UTC (rev 159138)
+++ trunk/Source/_javascript_Core/parser/Parser.cpp	2013-11-12 20:55:29 UTC (rev 159139)
@@ -400,7 +400,8 @@
                 semanticFail("Cannot deconstruct to a parameter named '", name.impl(), "'");
             }
         }
-        context.addVar(&name, kind == DeconstructToParameters ? 0 : DeclarationStacks::HasInitializer);
+        if (kind != DeconstructToExpressions)
+            context.addVar(&name, kind == DeconstructToParameters ? 0 : DeclarationStacks::HasInitializer);
     } else {
         if (kind == DeconstructToVariables) {
             failIfFalseIfStrict(declareVariable(&name), "Cannot declare a variable named '", name.impl(), "' in strict mode");
@@ -424,6 +425,12 @@
 }
 
 template <typename LexerType>
+template <class TreeBuilder> TreeDeconstructionPattern Parser<LexerType>::tryParseDeconstructionPatternExpression(TreeBuilder& context)
+{
+    return parseDeconstructionPattern<DeconstructToExpressions>(context);
+}
+
+template <typename LexerType>
 template <DeconstructionKind kind, class TreeBuilder> TreeDeconstructionPattern Parser<LexerType>::parseDeconstructionPattern(TreeBuilder& context, int depth)
 {
     failIfStackOverflow();
@@ -433,6 +440,8 @@
     case OPENBRACKET: {
         auto arrayPattern = context.createArrayPattern(m_token.m_location);
         next();
+        if (kind == DeconstructToExpressions && match(CLOSEBRACKET))
+            return 0;
         failIfTrue(match(CLOSEBRACKET), "There must be at least one bound property in an array deconstruction pattern");
         do {
             while (match(COMMA)) {
@@ -442,15 +451,25 @@
             propagateError();
             JSTokenLocation location = m_token.m_location;
             auto innerPattern = parseDeconstructionPattern<kind>(context, depth + 1);
+            if (kind == DeconstructToExpressions && !innerPattern)
+                return 0;
             failIfFalse(innerPattern, "Cannot parse this deconstruction pattern");
             context.appendArrayPatternEntry(arrayPattern, location, innerPattern);
         } while (consume(COMMA));
+        
+        if (kind == DeconstructToExpressions && !match(CLOSEBRACKET))
+            return 0;
+
         consumeOrFail(CLOSEBRACKET, "Expected either a closing ']' or a ',' following an element deconstruction pattern");
         pattern = arrayPattern;
         break;
     }
     case OPENBRACE: {
         next();
+        
+        if (kind == DeconstructToExpressions && match(CLOSEBRACE))
+            return 0;
+
         failIfTrue(match(CLOSEBRACE), "There must be at least one bound property in an object deconstruction pattern");
         auto objectPattern = context.createObjectPattern(m_token.m_location);
         bool wasString = false;
@@ -476,13 +495,18 @@
                     wasString = true;
                     break;
                 default:
-                    if (m_token.m_type != RESERVED && m_token.m_type != RESERVED_IF_STRICT && !(m_token.m_type & KeywordTokenFlag))
+                    if (m_token.m_type != RESERVED && m_token.m_type != RESERVED_IF_STRICT && !(m_token.m_type & KeywordTokenFlag)) {
+                        if (kind == DeconstructToExpressions)
+                            return 0;
                         failWithMessage("Expected a property name");
+                    }
                     propertyName = *m_token.m_data.ident;
                     break;
                 }
                 next();
                 if (!consume(COLON)) {
+                    if (kind == DeconstructToExpressions)
+                        return 0;
                     semanticFailIfTrue(tokenType == RESERVED, "Cannot use abbreviated deconstruction syntax for reserved name '", propertyName.impl(), "'");
                     semanticFailIfTrue(tokenType == RESERVED_IF_STRICT, "Cannot use abbreviated deconstruction syntax for reserved name '", propertyName.impl(), "' in strict mode");
                     semanticFailIfTrue(tokenType & KeywordTokenFlag, "Cannot use abbreviated deconstruction syntax for keyword '", propertyName.impl(), "'");
@@ -491,9 +515,13 @@
                 }
                 innerPattern = parseDeconstructionPattern<kind>(context, depth + 1);
             }
+            if (kind == DeconstructToExpressions && !innerPattern)
+                return 0;
             failIfFalse(innerPattern, "Cannot parse this deconstruction pattern");
             context.appendObjectPatternEntry(objectPattern, location, wasString, propertyName, innerPattern);
         } while (consume(COMMA));
+        if (kind == DeconstructToExpressions && !match(CLOSEBRACE))
+            return 0;
         consumeOrFail(CLOSEBRACE, "Expected either a closing '}' or an ',' after a property deconstruction pattern");
         pattern = objectPattern;
         break;
@@ -501,6 +529,8 @@
 
     default: {
         if (!match(IDENT)) {
+            if (kind == DeconstructToExpressions)
+                return 0;
             semanticFailureDueToKeyword("variable name");
             failWithMessage("Expected a parameter pattern or a ')' in parameter list");
         }
@@ -554,6 +584,7 @@
     JSTextPosition declsStart;
     JSTextPosition declsEnd;
     TreeExpression decls = 0;
+    TreeDeconstructionPattern pattern = 0;
     if (match(VAR)) {
         /*
          for (var IDENT in _expression_) statement
@@ -603,6 +634,16 @@
     }
     
     if (!match(SEMICOLON)) {
+        if (match(OPENBRACE) || match(OPENBRACKET)) {
+            SavePoint savePoint = createSavePoint();
+            declsStart = tokenStartPosition();
+            pattern = tryParseDeconstructionPatternExpression(context);
+            declsEnd = lastTokenEndPosition();
+            if (pattern && (match(INTOKEN) || (match(IDENT) && *m_token.m_data.ident == m_vm->propertyNames->of)))
+                goto enumerationLoop;
+            pattern = 0;
+            restoreSavePoint(savePoint);
+        }
         m_allowsIn = false;
         declsStart = tokenStartPosition();
         decls = parseExpression(context);
@@ -639,6 +680,7 @@
     }
     
     // For-in loop
+enumerationLoop:
     failIfFalse(nonLHSCount == m_nonLHSCount, "Expected a reference on the left hand side of an enumeration statement");
     bool isOfEnumeration = false;
     if (!consume(INTOKEN)) {
@@ -657,6 +699,12 @@
     TreeStatement statement = parseStatement(context, unused);
     endLoop();
     failIfFalse(statement, "Expected a statement as the body of a for-", isOfEnumeration ? "of" : "in", "loop");
+    if (pattern) {
+        ASSERT(!decls);
+        if (isOfEnumeration)
+            return context.createForOfLoop(location, pattern, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine);
+        return context.createForInLoop(location, pattern, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine);
+    }
     if (isOfEnumeration)
         return context.createForOfLoop(location, decls, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine);
     return context.createForInLoop(location, decls, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine);
@@ -1368,6 +1416,16 @@
     JSTokenLocation location(tokenLocation());
     int initialAssignmentCount = m_assignmentCount;
     int initialNonLHSCount = m_nonLHSCount;
+    if (match(OPENBRACE) || match(OPENBRACKET)) {
+        SavePoint savePoint = createSavePoint();
+        auto pattern = tryParseDeconstructionPatternExpression(context);
+        if (pattern && consume(EQUAL)) {
+            auto rhs = parseAssignmentExpression(context);
+            if (rhs)
+                return context.createDeconstructingAssignment(location, pattern, rhs);
+        }
+        restoreSavePoint(savePoint);
+    }
     TreeExpression lhs = parseConditionalExpression(context);
     failIfFalse(lhs, "Cannot parse _expression_");
     if (initialNonLHSCount != m_nonLHSCount) {

Modified: trunk/Source/_javascript_Core/parser/Parser.h (159138 => 159139)


--- trunk/Source/_javascript_Core/parser/Parser.h	2013-11-12 20:50:45 UTC (rev 159138)
+++ trunk/Source/_javascript_Core/parser/Parser.h	2013-11-12 20:55:29 UTC (rev 159139)
@@ -731,6 +731,7 @@
 
     template <DeconstructionKind, class TreeBuilder> ALWAYS_INLINE TreeDeconstructionPattern createBindingPattern(TreeBuilder&, const Identifier&, int depth);
     template <DeconstructionKind, class TreeBuilder> TreeDeconstructionPattern parseDeconstructionPattern(TreeBuilder&, int depth = 0);
+    template <class TreeBuilder> NEVER_INLINE TreeDeconstructionPattern tryParseDeconstructionPatternExpression(TreeBuilder&);
     template <FunctionRequirements, FunctionParseMode, bool nameIsInContainingScope, class TreeBuilder> bool parseFunctionInfo(TreeBuilder&, const Identifier*&, TreeFormalParameterList&, TreeFunctionBody&, unsigned& openBraceOffset, unsigned& closeBraceOffset, int& bodyStartLine, unsigned& bodyStartColumn);
     ALWAYS_INLINE int isBinaryOperator(JSTokenType);
     bool allowAutomaticSemicolon();
@@ -768,6 +769,7 @@
     
     ALWAYS_INLINE SavePoint createSavePoint()
     {
+        ASSERT(!hasError());
         SavePoint result;
         result.startOffset = m_token.m_location.startOffset;
         result.oldLineStartOffset = m_token.m_location.lineStartOffset;
@@ -778,6 +780,7 @@
     
     ALWAYS_INLINE void restoreSavePoint(const SavePoint& savePoint)
     {
+        m_errorMessage = String();
         m_lexer->setOffset(savePoint.startOffset, savePoint.oldLineStartOffset);
         next();
         m_lexer->setLastLineNumber(savePoint.oldLastLineNumber);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to