diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 0d232a324..a3e19e19d 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -3761,7 +3761,6 @@ BytecodeEmitter::emitDestructuringDeclsWithEmitter(JSOp prologueOp, ParseNode* p continue; ParseNode* target = element; if (element->isKind(PNK_SPREAD)) { - MOZ_ASSERT(element->pn_kid->isKind(PNK_NAME)); target = element->pn_kid; } if (target->isKind(PNK_ASSIGN)) diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index a612975e0..22ff1ae4d 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -4276,11 +4276,6 @@ Parser::checkDestructuringArray(BindData* da return false; } target = element->pn_kid; - - if (handler.isUnparenthesizedDestructuringPattern(target)) { - report(ParseError, false, target, JSMSG_BAD_DESTRUCT_TARGET); - return false; - } } else if (handler.isUnparenthesizedAssignment(element)) { target = element->pn_left; } else { diff --git a/js/src/jit-test/tests/basic/destructuring-iterator.js b/js/src/jit-test/tests/basic/destructuring-iterator.js index a9403ed38..8d6b9e6e9 100644 --- a/js/src/jit-test/tests/basic/destructuring-iterator.js +++ b/js/src/jit-test/tests/basic/destructuring-iterator.js @@ -49,6 +49,23 @@ assertIterable([5,5,4,4], it => { var [,,...rest] = it; return rest; }, [3,4]); +// the iterator should be exhausted before any error is thrown +if(0) { +// XXX: this doesn't throw right now. just test syntax below. +// plan to reenable this for issue 485 +assertIterable([5,5,4,4], + it => { + assertThrowsInstanceOf(function () { + "use strict"; + [...{0: "".x}] = it; + }, TypeError); + return []; + }, + []); +} else { +assertIterable([5,5,4,4], it => { [...{0: "".x}] = it; return []; }, []); +} + var arraycalls = 0; var ArrayIterator = Array.prototype[Symbol.iterator]; Array.prototype[Symbol.iterator] = function () { @@ -58,7 +75,11 @@ Array.prototype[Symbol.iterator] = function () { // [...rest] should not call Array#@@iterator for the LHS var [...rest] = iterable; assertEq(arraycalls, 0, 'calls to Array#@@iterator'); - +// [...[...rest]] should do so, since it creates an implicit array for the +// first rest pattern, then destructures that again using @@iterator() for the +// second rest pattern. +var [...[...rest]] = iterable; +assertEq(arraycalls, 1, 'calls to Array#@@iterator'); // loop `fn` a few times, to get it JIT-compiled function loop(fn) { @@ -67,7 +88,7 @@ function loop(fn) { } loop(() => { doneafter = 4; var [a] = iterable; return a; }); -loop(() => { doneafter = 4; var [a,b,...rest] = iterable; return rest; }); +loop(() => { doneafter = 4; var [a,b,...[...rest]] = iterable; return rest; }); // destructuring assignment should always use iterators and not optimize diff --git a/js/src/jit-test/tests/basic/destructuring-rest.js b/js/src/jit-test/tests/basic/destructuring-rest.js index 30ac1b002..4b86bd2d0 100644 --- a/js/src/jit-test/tests/basic/destructuring-rest.js +++ b/js/src/jit-test/tests/basic/destructuring-rest.js @@ -4,8 +4,6 @@ load(libdir + 'eqArrayHelper.js'); assertThrowsInstanceOf(() => new Function('[...a, ,] = []'), SyntaxError, 'trailing elision'); assertThrowsInstanceOf(() => new Function('[a, ...b, c] = []'), SyntaxError, 'trailing param'); -assertThrowsInstanceOf(() => new Function('[...[a]] = []'), SyntaxError, 'nested arraypattern'); -assertThrowsInstanceOf(() => new Function('[...{a}] = []'), SyntaxError, 'nested objectpattern'); assertThrowsInstanceOf(() => new Function('[...a=b] = []'), SyntaxError, 'assignment expression'); assertThrowsInstanceOf(() => new Function('[...a()] = []'), SyntaxError, 'call expression'); assertThrowsInstanceOf(() => new Function('[...(a,b)] = []'), SyntaxError, 'comma expression'); @@ -22,6 +20,14 @@ assertThrowsInstanceOf(() => assertThrowsInstanceOf(() => new Function('[...b,] = []'), SyntaxError) , Error); +assertThrowsInstanceOf(() => { + try { + eval('let [...[...x]] = (() => { throw "foo"; } )();'); + } catch(e) { + assertEq(e, "foo"); + } + x; +}, ReferenceError); var inputArray = [1, 2, 3]; var inputDeep = [1, inputArray]; @@ -45,6 +51,11 @@ function testAll(fn) { assertEqArray(fn('[, ...(o.prop)]', inputArray, 'o.prop'), expected); o.prop = null; assertEqArray(fn('[, ...(o.call().prop)]', inputArray, 'o.prop'), expected); + + o.prop = null; + assertEqArray(fn('[, ...[...(o.prop)]]', inputArray, 'o.prop'), expected); + o.prop = null; + assertEqArray(fn('[, ...[...(o.call().prop)]]', inputArray, 'o.prop'), expected); } function testDeclaration(fn) { testStr(fn); @@ -53,10 +64,24 @@ function testDeclaration(fn) { assertEqArray(fn('[, ...rest]', inputGenerator()), expected); assertEqArray(fn('[, [, ...rest]]', inputDeep), expected); assertEqArray(fn('{a: [, ...rest]}', inputObject), expected); + + assertEqArray(fn('[, ...[...rest]]', inputArray), expected); + assertEqArray(fn('[, ...[...rest]]', inputGenerator()), expected); + assertEqArray(fn('[, [, ...[...rest]]]', inputDeep), expected); + assertEqArray(fn('{a: [, ...[...rest]]}', inputObject), expected); + + assertEqArray(fn('[, ...{0: a, 1: b}]', inputArray, '[a, b]'), expected); + assertEqArray(fn('[, ...{0: a, 1: b}]', inputGenerator(), '[a, b]'), expected); + assertEqArray(fn('[, [, ...{0: a, 1: b}]]', inputDeep, '[a, b]'), expected); + assertEqArray(fn('{a: [, ...{0: a, 1: b}]}', inputObject, '[a, b]'), expected); } function testStr(fn) { assertEqArray(fn('[, ...rest]', inputStr), expectedStr); + + assertEqArray(fn('[, ...[...rest]]', inputStr), expectedStr); + + assertEqArray(fn('[, ...{0: a, 1: b}]', inputStr, '[a, b]'), expectedStr); } function testForIn(pattern, input, binding) { @@ -88,8 +113,9 @@ testAll(testGlobal); function testClosure(pattern, input, binding) { binding = binding || 'rest'; + const decl = binding.replace('[', '').replace(']', ''); return new Function('input', - 'var ' + binding + '; (function () {' + + 'var ' + decl + '; (function () {' + '(' + pattern + ' = input);' + '})();' + 'return ' + binding