1// Copyright 2015 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Flags: --harmony-destructuring-assignment --harmony-destructuring-bind
6
7// script-level tests
8var ox, oy = {}, oz;
9({
10  x: ox,
11  y: oy.value,
12  y2: oy["value2"],
13  z: ({ set v(val) { oz = val; } }).v
14} = {
15  x: "value of x",
16  y: "value of y1",
17  y2: "value of y2",
18  z: "value of z"
19});
20assertEquals("value of x", ox);
21assertEquals("value of y1", oy.value);
22assertEquals("value of y2", oy.value2);
23assertEquals("value of z", oz);
24
25[ox, oy.value, oy["value2"], ...{ set v(val) { oz = val; } }.v] = [
26  1007,
27  798432,
28  555,
29  1, 2, 3, 4, 5
30];
31assertEquals(ox, 1007);
32assertEquals(oy.value, 798432);
33assertEquals(oy.value2, 555);
34assertEquals(oz, [1, 2, 3, 4, 5]);
35
36
37(function testInFunction() {
38  var x, y = {}, z;
39  ({
40    x: x,
41    y: y.value,
42    y2: y["value2"],
43    z: ({ set v(val) { z = val; } }).v
44  } = {
45    x: "value of x",
46    y: "value of y1",
47    y2: "value of y2",
48    z: "value of z"
49  });
50  assertEquals("value of x", x);
51  assertEquals("value of y1", y.value);
52  assertEquals("value of y2", y.value2);
53  assertEquals("value of z", z);
54
55  [x, y.value, y["value2"], ...{ set v(val) { z = val; } }.v] = [
56    1007,
57    798432,
58    555,
59    1, 2, 3, 4, 5
60  ];
61  assertEquals(x, 1007);
62  assertEquals(y.value, 798432);
63  assertEquals(y.value2, 555);
64  assertEquals(z, [1, 2, 3, 4, 5]);
65})();
66
67
68(function testArrowFunctionInitializers() {
69  var fn = (config = {
70    value: defaults.value,
71    nada: { nada: defaults.nada } = { nada: "nothing" }
72  } = { value: "BLAH" }) => config;
73  var defaults = {};
74  assertEquals({ value: "BLAH" }, fn());
75  assertEquals("BLAH", defaults.value);
76  assertEquals("nothing", defaults.nada);
77})();
78
79
80(function testArrowFunctionInitializers2() {
81  var fn = (config = [
82    defaults.value,
83    { nada: defaults.nada } = { nada: "nothing" }
84  ] = ["BLAH"]) => config;
85  var defaults = {};
86  assertEquals(["BLAH"], fn());
87  assertEquals("BLAH", defaults.value);
88  assertEquals("nothing", defaults.nada);
89})();
90
91
92(function testFunctionInitializers() {
93  function fn(config = {
94    value: defaults.value,
95    nada: { nada: defaults.nada } = { nada: "nothing" }
96  } = { value: "BLAH" }) {
97    return config;
98  }
99  var defaults = {};
100  assertEquals({ value: "BLAH" }, fn());
101  assertEquals("BLAH", defaults.value);
102  assertEquals("nothing", defaults.nada);
103})();
104
105
106(function testFunctionInitializers2() {
107  function fn(config = [
108    defaults.value,
109    { nada: defaults.nada } = { nada: "nothing" }
110  ] = ["BLAH"]) { return config; }
111  var defaults = {};
112  assertEquals(["BLAH"], fn());
113  assertEquals("BLAH", defaults.value);
114  assertEquals("nothing", defaults.nada);
115})();
116
117
118(function testDeclarationInitializers() {
119  var defaults = {};
120  var { value } = { value: defaults.value } = { value: "BLAH" };
121  assertEquals("BLAH", value);
122  assertEquals("BLAH", defaults.value);
123})();
124
125
126(function testDeclarationInitializers2() {
127  var defaults = {};
128  var [value] = [defaults.value] = ["BLAH"];
129  assertEquals("BLAH", value);
130  assertEquals("BLAH", defaults.value);
131})();
132
133
134(function testObjectLiteralProperty() {
135  var ext = {};
136  var obj = {
137    a: { b: ext.b, c: ext["c"], d: { set v(val) { ext.d = val; } }.v } = {
138      b: "b", c: "c", d: "d" }
139  };
140  assertEquals({ b: "b", c: "c", d: "d" }, ext);
141  assertEquals({ a: { b: "b", c: "c", d: "d" } }, obj);
142})();
143
144
145(function testArrayLiteralProperty() {
146  var ext = {};
147  var obj = [
148    ...[ ext.b, ext["c"], { set v(val) { ext.d = val; } }.v ] = [
149      "b", "c", "d" ]
150  ];
151  assertEquals({ b: "b", c: "c", d: "d" }, ext);
152  assertEquals([ "b", "c", "d" ], obj);
153})();
154
155
156// TODO(caitp): add similar test for ArrayPatterns, once Proxies support
157// delegating symbol-keyed get/set.
158(function testObjectPatternOperationOrder() {
159  var steps = [];
160  var store = {};
161  function computePropertyName(name) {
162    steps.push("compute name: " + name);
163    return name;
164  }
165  function loadValue(descr, value) {
166    steps.push("load: " + descr + " > " + value);
167    return value;
168  }
169  function storeValue(descr, name, value) {
170    steps.push("store: " + descr + " = " + value);
171    store[name] = value;
172  }
173  var result = {
174    get a() { assertUnreachable(); },
175    set a(value) { storeValue("result.a", "a", value); },
176    get b() { assertUnreachable(); },
177    set b(value) { storeValue("result.b", "b", value); }
178  };
179
180  ({
181    obj: {
182      x: result.a = 10,
183      [computePropertyName("y")]: result.b = false,
184    } = {}
185  } = { obj: {
186    get x() { return loadValue(".temp.obj.x", undefined); },
187    set x(value) { assertUnreachable(); },
188    get y() { return loadValue(".temp.obj.y", undefined); },
189    set y(value) { assertUnreachable(); }
190  }});
191
192  assertPropertiesEqual({
193    a: 10,
194    b: false
195  }, store);
196
197  assertArrayEquals([
198    "load: .temp.obj.x > undefined",
199    "store: result.a = 10",
200
201    "compute name: y",
202    "load: .temp.obj.y > undefined",
203    "store: result.b = false"
204  ], steps);
205
206  steps = [];
207
208  ({
209    obj: {
210      x: result.a = 50,
211      [computePropertyName("y")]: result.b = "hello",
212    } = {}
213  } = { obj: {
214    get x() { return loadValue(".temp.obj.x", 20); },
215    set x(value) { assertUnreachable(); },
216    get y() { return loadValue(".temp.obj.y", true); },
217    set y(value) { assertUnreachable(); }
218  }});
219
220  assertPropertiesEqual({
221    a: 20,
222    b: true
223  }, store);
224
225  assertArrayEquals([
226    "load: .temp.obj.x > 20",
227    "store: result.a = 20",
228    "compute name: y",
229    "load: .temp.obj.y > true",
230    "store: result.b = true",
231  ], steps);
232})();
233
234// Credit to Mike Pennisi and other Test262 contributors for originally writing
235// the testse the following are based on.
236(function testArrayElision() {
237  var value = [1, 2, 3, 4, 5, 6, 7, 8, 9];
238  var a, obj = {};
239  var result = [, a, , obj.b, , ...obj["rest"]] = value;
240
241  assertEquals(result, value);
242  assertEquals(2, a);
243  assertEquals(4, obj.b);
244  assertArrayEquals([6, 7, 8, 9], obj.rest);
245})();
246
247(function testArrayElementInitializer() {
248  function test(value, initializer, expected) {
249    var a, obj = {};
250    var initialized = false;
251    var shouldBeInitialized = value[0] === undefined;
252    assertEquals(value, [ a = (initialized = true, initializer) ] = value);
253    assertEquals(expected, a);
254    assertEquals(shouldBeInitialized, initialized);
255
256    var initialized2 = false;
257    assertEquals(value, [ obj.a = (initialized2 = true, initializer) ] = value);
258    assertEquals(expected, obj.a);
259    assertEquals(shouldBeInitialized, initialized2);
260  }
261
262  test([], "BAM!", "BAM!");
263  test([], "BOOP!", "BOOP!");
264  test([null], 123, null);
265  test([undefined], 456, 456);
266  test([,], "PUPPIES", "PUPPIES");
267
268  (function accept_IN() {
269    var value = [], x;
270    assertEquals(value, [ x = 'x' in {} ] = value);
271    assertEquals(false, x);
272  })();
273
274  (function ordering() {
275    var x = 0, a, b, value = [];
276    assertEquals(value, [ a = x += 1, b = x *= 2 ] = value);
277    assertEquals(1, a);
278    assertEquals(2, b);
279    assertEquals(2, x);
280  })();
281
282  (function yieldExpression() {
283    var value = [], it, result, x;
284    it = (function*() {
285      result = [ x = yield ] = value;
286    })();
287    var next = it.next();
288
289    assertEquals(undefined, result);
290    assertEquals(undefined, next.value);
291    assertEquals(false, next.done);
292    assertEquals(undefined, x);
293
294    next = it.next(86);
295
296    assertEquals(value, result);
297    assertEquals(undefined, next.value);
298    assertEquals(true, next.done);
299    assertEquals(86, x);
300  })();
301
302  (function yieldIdentifier() {
303    var value = [], yield = "BOOP!", x;
304    assertEquals(value, [ x = yield ] = value);
305    assertEquals("BOOP!", x);
306  })();
307
308  assertThrows(function let_TDZ() {
309    "use strict";
310    var x;
311    [ x = y ] = [];
312    let y;
313  }, ReferenceError);
314})();
315
316
317(function testArrayElementNestedPattern() {
318  assertThrows(function nestedArrayRequireObjectCoercibleNull() {
319    var x; [ [ x ] ] = [ null ];
320  }, TypeError);
321
322  assertThrows(function nestedArrayRequireObjectCoercibleUndefined() {
323    var x; [ [ x ] ] = [ undefined ];
324  }, TypeError);
325
326  assertThrows(function nestedArrayRequireObjectCoercibleUndefined2() {
327    var x; [ [ x ] ] = [ ];
328  }, TypeError);
329
330  assertThrows(function nestedArrayRequireObjectCoercibleUndefined3() {
331    var x; [ [ x ] ] = [ , ];
332  }, TypeError);
333
334  assertThrows(function nestedObjectRequireObjectCoercibleNull() {
335    var x; [ { x } ] = [ null ];
336  }, TypeError);
337
338  assertThrows(function nestedObjectRequireObjectCoercibleUndefined() {
339    var x; [ { x } ] = [ undefined ];
340  }, TypeError);
341
342  assertThrows(function nestedObjectRequireObjectCoercibleUndefined2() {
343    var x; [ { x } ] = [ ];
344  }, TypeError);
345
346  assertThrows(function nestedObjectRequireObjectCoercibleUndefined3() {
347    var x; [ { x } ] = [ , ];
348  }, TypeError);
349
350  (function nestedArray() {
351    var x, value = [ [ "zap", "blonk" ] ];
352    assertEquals(value, [ [ , x ] ] = value);
353    assertEquals("blonk", x);
354  })();
355
356  (function nestedObject() {
357    var x, value = [ { a: "zap", b: "blonk" } ];
358    assertEquals(value, [ { b: x } ] = value);
359    assertEquals("blonk", x);
360  })();
361})();
362
363(function testArrayRestElement() {
364  (function testBasic() {
365    var x, rest, array = [1, 2, 3];
366    assertEquals(array, [x, ...rest] = array);
367    assertEquals(1, x);
368    assertEquals([2, 3], rest);
369
370    array = [4, 5, 6];
371    assertEquals(array, [, ...rest] = array);
372    assertEquals([5, 6], rest);
373
374  })();
375
376  (function testNestedRestObject() {
377    var value = [1, 2, 3], x;
378    assertEquals(value, [...{ 1: x }] = value);
379    assertEquals(2, x);
380  })();
381
382  (function iterable() {
383    var count = 0;
384    var x, y, z;
385    function* g() {
386      count++;
387      yield;
388      count++;
389      yield;
390      count++;
391      yield;
392    }
393    var it = g();
394    assertEquals(it, [...x] = it);
395    assertEquals([undefined, undefined, undefined], x);
396    assertEquals(3, count);
397
398    it = [g()];
399    assertEquals(it, [ [...y] ] = it);
400    assertEquals([undefined, undefined, undefined], y);
401    assertEquals(6, count);
402
403    it = { a: g() };
404    assertEquals(it, { a: [...z] } = it);
405    assertEquals([undefined, undefined, undefined], z);
406    assertEquals(9, count);
407  })();
408})();
409
410(function testRequireObjectCoercible() {
411  assertThrows(() => ({} = undefined), TypeError);
412  assertThrows(() => ({} = null), TypeError);
413  assertThrows(() => [] = undefined, TypeError);
414  assertThrows(() => [] = null, TypeError);
415  assertEquals("test", ({} = "test"));
416  assertEquals("test", [] = "test");
417  assertEquals(123, ({} = 123));
418})();
419
420(function testConstReassignment() {
421  "use strict";
422  const c = "untouchable";
423  assertThrows(() => { [ c ] = [ "nope!" ]; }, TypeError);
424  assertThrows(() => { [ [ c ] ]  = [ [ "nope!" ] ]; }, TypeError);
425  assertThrows(() => { [ { c } ]  = [ { c: "nope!" } ]; }, TypeError);
426  assertThrows(() => { ({ c } = { c: "nope!" }); }, TypeError);
427  assertThrows(() => { ({ a: { c } } = { a: { c: "nope!" } }); }, TypeError);
428  assertThrows(() => { ({ a: [ c ] } = { a: [ "nope!" ] }); }, TypeError);
429  assertEquals("untouchable", c);
430})();
431
432(function testForIn() {
433  var log = [];
434  var x = {};
435  var object = {
436    "Apenguin": 1,
437    "\u{1F382}cake": 2,
438    "Bpuppy": 3,
439    "Cspork": 4
440  };
441  for ([x.firstLetter, ...x.rest] in object) {
442    if (x.firstLetter === "A") {
443      assertEquals(["p", "e", "n", "g", "u", "i", "n"], x.rest);
444      continue;
445    }
446    if (x.firstLetter === "C") {
447      assertEquals(["s", "p", "o", "r", "k"], x.rest);
448      break;
449    }
450    log.push({ firstLetter: x.firstLetter, rest: x.rest });
451  }
452  assertEquals([
453    { firstLetter: "\u{1F382}", rest: ["c", "a", "k", "e"] },
454    { firstLetter: "B", rest: ["p", "u", "p", "p", "y"] },
455  ], log);
456})();
457
458(function testForOf() {
459  var log = [];
460  var x = {};
461  var names = [
462    "Apenguin",
463    "\u{1F382}cake",
464    "Bpuppy",
465    "Cspork"
466  ];
467  for ([x.firstLetter, ...x.rest] of names) {
468    if (x.firstLetter === "A") {
469      assertEquals(["p", "e", "n", "g", "u", "i", "n"], x.rest);
470      continue;
471    }
472    if (x.firstLetter === "C") {
473      assertEquals(["s", "p", "o", "r", "k"], x.rest);
474      break;
475    }
476    log.push({ firstLetter: x.firstLetter, rest: x.rest });
477  }
478  assertEquals([
479    { firstLetter: "\u{1F382}", rest: ["c", "a", "k", "e"] },
480    { firstLetter: "B", rest: ["p", "u", "p", "p", "y"] },
481  ], log);
482})();
483