1// Copyright JS Foundation and other contributors, http://js.foundation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15function checkSyntax (str) {
16  try {
17    eval (str);
18    assert (false);
19  } catch (e) {
20    assert (e instanceof SyntaxError);
21  }
22}
23
24function assertArrayEqual (actual, expected) {
25  assert (actual.length === expected.length);
26
27  for (var i = 0; i < actual.length; i++) {
28    assert (actual[i] === expected[i]);
29  }
30}
31
32function mustThrow (str) {
33  try {
34    eval (str);
35    assert (false);
36  } catch (e) {
37    assert (e instanceof TypeError);
38  }
39}
40
41checkSyntax ("var [a]");
42checkSyntax ("var [a, o.a]");
43checkSyntax ("var [a, ...b,]");
44checkSyntax ("var [a, ...b = 4]");
45checkSyntax ("var [a, ...[b] = 4]");
46checkSyntax ("var [let]");
47checkSyntax ("var [get = []");
48checkSyntax ("var [get : 5]");
49checkSyntax ("var [[a = {},]");
50checkSyntax ("let [a,a] = []");
51checkSyntax ("let [a, ...a] = []");
52checkSyntax ("const [a,a] = []");
53checkSyntax ("const [a, ...a] = []");
54checkSyntax ("[new Object()] = []");
55checkSyntax ("[Object()] = []");
56checkSyntax ("[(a, b, d, c)] = []");
57checkSyntax ("[super] = []");
58checkSyntax ("[this] = []");
59checkSyntax ("[()] = []");
60checkSyntax ("try { let [$] = $;");
61checkSyntax ("let a, [ b.c ] = [6];");
62checkSyntax ("let [(a)] = [1]");
63
64mustThrow ("var [a] = 4");
65mustThrow ("var [a] = 5");
66mustThrow ("var [a] = {}");
67mustThrow ("var [a] = { get [Symbol.iterator] () { throw new TypeError } }");
68mustThrow ("var [a] = { [Symbol.iterator] () {} }");
69mustThrow ("var [a] = { [Symbol.iterator] () { return {} } }");
70mustThrow ("var [a] = { [Symbol.iterator] () { return { next: 5 } } }");
71mustThrow ("var [a] = { [Symbol.iterator] () { return { next: 5 } } }");
72mustThrow ("var [a] = { [Symbol.iterator] () { return { get next() { throw new TypeError } } } }");
73mustThrow ("var [a] = { [Symbol.iterator] () { return { next () { } } } }");
74mustThrow ("var [a] = { [Symbol.iterator] () { return { next () { } } } }");
75mustThrow ("var [a] = { [Symbol.iterator] () { return { next () { return { get value () { throw new TypeError }}}}}}");
76mustThrow ("var [a] = { [Symbol.iterator] () { return { next () { return { get done () { throw new TypeError }}}}}}");
77
78// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
79
80// Basic variable assignment
81(function () {
82  var foo = ["one", "two", "three"];
83
84  var [red, yellow, green] = foo;
85  assert (red === "one");
86  assert (yellow === "two");
87  assert (green === "three");
88}) ();
89
90// Assignment separate from declaration
91(function () {
92  var a, b;
93
94  [a, b] = [1, 2];
95  assert (a === 1);
96  assert (b === 2);
97}) ();
98
99// Default values
100(function () {
101  var a, b;
102  [a = 5, b = 7] = [1];
103
104  assert (a === 1);
105  assert (b === 7);
106}) ();
107
108// Swapping variables
109(function () {
110  var a = 1;
111  var b = 3;
112
113  [a, b] = [b, a];
114  assert (a === 3);
115  assert (b === 1);
116
117  var arr = [1,2,3];
118  [arr[2], arr[1]] = [arr[1], arr[2]];
119  assertArrayEqual (arr, [1, 3, 2]);
120}) ();
121
122// Parsing an array returned from a function
123(function () {
124  function f() {
125    return [1, 2];
126  }
127
128  var a, b;
129  [a, b] = f();
130  assert (a === 1);
131  assert (b === 2);
132}) ();
133
134// Ignoring some returned values
135(function () {
136  function f() {
137    return [1, 2, 3];
138  }
139
140  var a, b;
141  [a, ,b] = f();
142  assert (a === 1);
143  assert (b === 3);
144}) ();
145
146// Ignoring some returned values
147(function () {
148  var [a, ...b] = [1, 2, 3];
149  assert (a === 1);
150  assertArrayEqual (b, [2, 3]);
151}) ();
152
153// Unpacking values from a regular expression match
154(function () {
155  function parseProtocol(url) {
156    var parsedURL = /^(\w+)\:\/\/([^\/]+)\/(.*)$/.exec(url);
157    if (!parsedURL) {
158      return false;
159    }
160
161    var [, protocol, fullhost, fullpath] = parsedURL;
162    return protocol;
163  }
164
165  assert (parseProtocol("https://developer.mozilla.org/en-US/Web/JavaScript") === "https");
166}) ();
167
168// Test inner patterns I.
169(function () {
170  let [a, [b, [c = 4, d = 5]], [e] = [6]] = [1, [2, [3,undefined]]];
171
172  assert (a === 1);
173  assert (b === 2);
174  assert (c === 3);
175  assert (d === 5);
176  assert (e === 6);
177}) ();
178
179// Test inner patterns II.
180(function () {
181  var o = {};
182  [a, b, c, o.a = 4, o.b, o.c = 3] = ["1", "2", "3", undefined, "8", "6"];
183
184  assert (a === "1");
185  assert (b === "2");
186  assert (c === "3");
187  assert (o.a === 4);
188  assert (o.b === "8");
189  assert (o.c === "6");
190}) ();
191
192// Test rest element I.
193(function () {
194  var o = {};
195  [...o.a] = ["1", "2", "3"];
196
197  assertArrayEqual (o.a, ["1", "2", "3"]);
198}) ();
199
200// Test rest element II.
201(function () {
202  [...[a,b,c]] = ["1", "2", "3"];
203
204  assert (a === "1");
205  assert (b === "2");
206  assert (c === "3");
207}) ();
208
209// Test inner object pattern I.
210(function () {
211  [{f : a, g : b}, , , ...[c, d, e]] = [{ f : "1", g : "2"}, 3, 4, 5, 6, 7];
212
213  assert (a === "1");
214  assert (b === "2");
215  assert (c === 5);
216  assert (d === 6);
217  assert (e === 7);
218}) ();
219
220// Multiple declaration
221(function () {
222  var [a] = [1], [b] = [2];
223
224  assert (a === 1);
225  assert (b === 2);
226}) ();
227
228// Force the creation of lexical environment I.
229(function () {
230  const [a] = [1];
231  eval();
232
233  assert (a === 1);
234}) ();
235
236// Force the creation of lexical environment II.
237(function () {
238  let [a] = [1];
239  eval();
240
241  assert (a === 1);
242}) ();
243
244// Check the parsing of AssignmentElement
245(function () {
246  var a = 6;
247  [((a))] = [7];
248  assert (a === 7);
249}) ();
250
251// Test iterator closing
252function __createIterableObject (arr, methods) {
253  methods = methods || {};
254  if (typeof Symbol !== 'function' || !Symbol.iterator) {
255    return {};
256  }
257  arr.length++;
258  var iterator = {
259    next: function() {
260      return { value: arr.shift(), done: arr.length <= 0 };
261    },
262    'return': methods['return'],
263    'throw': methods['throw']
264  };
265  var iterable = {};
266  iterable[Symbol.iterator] = function () { return iterator; };
267  return iterable;
268};
269
270(function () {
271  var closed = false;
272  var iter = __createIterableObject([1, 2, 3], {
273    'return': function() { closed = true; return {}; }
274  });
275  var [a, b] = iter;
276  assert (closed === true);
277  assert (a === 1);
278  assert (b === 2);
279}) ();
280
281mustThrow ("var iter = __createIterableObject([], "
282           + "{ get 'return'() { throw new TypeError() }});"
283           + "var [a] = iter");
284
285mustThrow ("var iter = __createIterableObject([], "
286           + "{ 'return': 5 });"
287           + "var [a] = iter");
288
289mustThrow ("var iter = __createIterableObject([], "
290           + "{ 'return': function() { return 5; }});"
291           + "var [a] = iter");
292
293mustThrow ("try { throw 5 } catch (e) {"
294           + "var iter = __createIterableObject([], "
295           + "{ get 'return'() { throw new TypeError() }});"
296           + "var [a] = iter }");
297
298mustThrow ("try { throw 5 } catch (e) {"
299           + "var iter = __createIterableObject([], "
300           + "{ 'return': 5 });"
301           + "var [a] = iter }");
302
303mustThrow ("try { throw 5 } catch (e) {"
304           + "var iter = __createIterableObject([], "
305           + "{ 'return': function() { return 5; }});"
306           + "var [a] = iter }");
307
308try {
309  eval ("var a = 0; 1 + [a] = [1]");
310  assert (false);
311} catch (e) {
312  assert (e instanceof ReferenceError);
313}
314