[ES6] Allow trailing comma in ArrayBindingPattern and ObjectBindingPattern
https://bugs.webkit.org/show_bug.cgi?id=146192
Reviewed by Darin Adler.
Source/JavaScriptCore:
According to the ES6 spec, trailing comma in ArrayBindingPattern and ObjectBindingPattern is allowed.
And empty ArrayBindingPattern and ObjectBindingPattern is also allowed.
This patch allows trailing comma and empty binding patterns.
* bytecompiler/NodesCodegen.cpp:
(JSC::ArrayPatternNode::bindValue):
* parser/Parser.cpp:
(JSC::Parser<LexerType>::parseDeconstructionPattern):
* tests/stress/trailing-comma-in-patterns.js: Added.
(shouldBe):
(iterator):
LayoutTests:
* js/object-literal-syntax-expected.txt:
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@185853 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 5dc0d5c..7d96e88 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,23 @@
+2015-06-22 Yusuke Suzuki <utatane.tea@gmail.com>
+
+ [ES6] Allow trailing comma in ArrayBindingPattern and ObjectBindingPattern
+ https://bugs.webkit.org/show_bug.cgi?id=146192
+
+ Reviewed by Darin Adler.
+
+ According to the ES6 spec, trailing comma in ArrayBindingPattern and ObjectBindingPattern is allowed.
+ And empty ArrayBindingPattern and ObjectBindingPattern is also allowed.
+
+ This patch allows trailing comma and empty binding patterns.
+
+ * bytecompiler/NodesCodegen.cpp:
+ (JSC::ArrayPatternNode::bindValue):
+ * parser/Parser.cpp:
+ (JSC::Parser<LexerType>::parseDeconstructionPattern):
+ * tests/stress/trailing-comma-in-patterns.js: Added.
+ (shouldBe):
+ (iterator):
+
2015-06-20 Yusuke Suzuki <utatane.tea@gmail.com>
[ES6] Destructuring assignment need to accept iterables
diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
index 7643e0b..81a5819 100644
--- a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
+++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
@@ -3139,8 +3139,12 @@
generator.emitCall(iterator.get(), iterator.get(), NoExpectedFunction, args, divot(), divotStart(), divotEnd());
}
+ if (m_targetPatterns.isEmpty()) {
+ generator.emitIteratorClose(iterator.get(), this);
+ return;
+ }
+
RefPtr<RegisterID> done;
- ASSERT(!m_targetPatterns.isEmpty());
for (auto& target : m_targetPatterns) {
RefPtr<RegisterID> value = generator.newTemporary();
diff --git a/Source/JavaScriptCore/parser/Parser.cpp b/Source/JavaScriptCore/parser/Parser.cpp
index f9d1d84..dab6b1a 100644
--- a/Source/JavaScriptCore/parser/Parser.cpp
+++ b/Source/JavaScriptCore/parser/Parser.cpp
@@ -600,15 +600,17 @@
JSTextPosition divotStart = tokenStartPosition();
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)) {
context.appendArrayPatternSkipEntry(arrayPattern, m_token.m_location);
next();
}
propagateError();
+
+ if (match(CLOSEBRACKET))
+ break;
+
JSTokenLocation location = m_token.m_location;
auto innerPattern = parseDeconstructionPattern(context, kind, depth + 1);
if (kind == DeconstructToExpressions && !innerPattern)
@@ -618,25 +620,24 @@
failIfTrue(kind == DeconstructToParameters && defaultValue, "Default values in destructuring parameters are currently not supported");
context.appendArrayPatternEntry(arrayPattern, location, innerPattern, defaultValue);
} while (consume(COMMA));
-
+
if (kind == DeconstructToExpressions && !match(CLOSEBRACKET))
return 0;
-
consumeOrFail(CLOSEBRACKET, "Expected either a closing ']' or a ',' following an element deconstruction pattern");
context.finishArrayPattern(arrayPattern, divotStart, divotStart, lastTokenEndPosition());
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;
+ next();
+
do {
+ bool wasString = false;
+
+ if (match(CLOSEBRACE))
+ break;
+
Identifier propertyName;
TreeDeconstructionPattern innerPattern = 0;
JSTokenLocation location = m_token.m_location;
@@ -687,6 +688,7 @@
failIfTrue(kind == DeconstructToParameters && defaultValue, "Default values in destructuring parameters are currently not supported");
context.appendObjectPatternEntry(objectPattern, location, wasString, propertyName, innerPattern, defaultValue);
} while (consume(COMMA));
+
if (kind == DeconstructToExpressions && !match(CLOSEBRACE))
return 0;
consumeOrFail(CLOSEBRACE, "Expected either a closing '}' or an ',' after a property deconstruction pattern");
diff --git a/Source/JavaScriptCore/tests/stress/trailing-comma-in-patterns.js b/Source/JavaScriptCore/tests/stress/trailing-comma-in-patterns.js
new file mode 100644
index 0000000..7353e90
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/trailing-comma-in-patterns.js
@@ -0,0 +1,157 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function iterator(array) {
+ var nextCount = 0;
+ var returnCount = 0;
+ var original = array.values();
+ return {
+ [Symbol.iterator]() {
+ return this;
+ },
+
+ next() {
+ ++nextCount;
+ return original.next();
+ },
+
+ return() {
+ ++returnCount;
+ return { done: true };
+ },
+
+ reportNext() {
+ return nextCount;
+ },
+
+ reportReturn() {
+ return returnCount;
+ }
+ };
+};
+
+(function () {
+ var iter = iterator([1, 2, 3]);
+ var [] = iter;
+ shouldBe(iter.reportNext(), 0);
+ shouldBe(iter.reportReturn(), 1);
+}());
+
+(function () {
+ var iter = iterator([1, 2, 3]);
+ var [,] = iter;
+ shouldBe(iter.reportNext(), 1);
+ shouldBe(iter.reportReturn(), 1);
+}());
+
+(function () {
+ var iter = iterator([1, 2, 3]);
+ var [,,] = iter;
+ shouldBe(iter.reportNext(), 2);
+ shouldBe(iter.reportReturn(), 1);
+}());
+
+(function () {
+ var iter = iterator([1, 2, 3]);
+ var [,,,] = iter;
+ shouldBe(iter.reportNext(), 3);
+ shouldBe(iter.reportReturn(), 1);
+}());
+
+(function () {
+ var iter = iterator([1, 2, 3]);
+ var [,,,,] = iter;
+ shouldBe(iter.reportNext(), 4);
+ shouldBe(iter.reportReturn(), 0);
+}());
+
+(function () {
+ var iter = iterator([1, 2, 3]);
+ var [,,,,,] = iter;
+ shouldBe(iter.reportNext(), 4);
+ shouldBe(iter.reportReturn(), 0);
+}());
+
+(function () {
+ var iter = iterator([1, 2, 3]);
+ var [,a,] = iter;
+ shouldBe(iter.reportNext(), 2);
+ shouldBe(iter.reportReturn(), 1);
+ shouldBe(a, 2);
+}());
+
+(function () {
+ var iter = iterator([1, 2, 3]);
+ var [a,] = iter;
+ shouldBe(iter.reportNext(), 1);
+ shouldBe(iter.reportReturn(), 1);
+ shouldBe(a, 1);
+}());
+
+(function () {
+ var iter = iterator([1, 2, 3]);
+ var [a,,] = iter;
+ shouldBe(iter.reportNext(), 2);
+ shouldBe(iter.reportReturn(), 1);
+ shouldBe(a, 1);
+}());
+
+(function () {
+ var iter = iterator([1, 2, 3]);
+ var [a,b = 42,] = iter;
+ shouldBe(iter.reportNext(), 2);
+ shouldBe(iter.reportReturn(), 1);
+ shouldBe(a, 1);
+ shouldBe(b, 2);
+}());
+
+(function () {
+ var {} = { Cocoa: 15, Cappuccino: 13 };
+}());
+
+(function () {
+ var {Cocoa,} = { Cocoa: 15, Cappuccino: 13 };
+ shouldBe(Cocoa, 15);
+}());
+
+(function () {
+ var {Cocoa = 'Cocoa',} = { Cocoa: 15, Cappuccino: 13 };
+ shouldBe(Cocoa, 15);
+}());
+
+(function () {
+ var {Cocoa, Kilimanjaro = 'Coffee'} = { Cocoa: 15, Cappuccino: 13 };
+ shouldBe(Cocoa, 15);
+ shouldBe(Kilimanjaro, 'Coffee');
+}());
+
+(function () {
+ var {Cocoa, Kilimanjaro = 'Coffee'} = {};
+ shouldBe(Cocoa, undefined);
+ shouldBe(Kilimanjaro, 'Coffee');
+}());
+
+(function () {
+ var {Cocoa, Kilimanjaro = 'Coffee',} = { Cocoa: 15, Cappuccino: 13 };
+ shouldBe(Cocoa, 15);
+ shouldBe(Kilimanjaro, 'Coffee');
+}());
+
+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));
+}
+
+testSyntaxError(String.raw`var {,} = {Cocoa: 15}`, String.raw`SyntaxError: Unexpected token ','. Expected a property name.`);
+testSyntaxError(String.raw`var {,} = {}`, String.raw`SyntaxError: Unexpected token ','. Expected a property name.`);