| // Copyright 2015 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // script-level tests |
| var ox, oy = {}, oz; |
| ({ |
| x: ox, |
| y: oy.value, |
| y2: oy["value2"], |
| z: ({ set v(val) { oz = val; } }).v |
| } = { |
| x: "value of x", |
| y: "value of y1", |
| y2: "value of y2", |
| z: "value of z" |
| }); |
| assertEquals("value of x", ox); |
| assertEquals("value of y1", oy.value); |
| assertEquals("value of y2", oy.value2); |
| assertEquals("value of z", oz); |
| |
| [ox, oy.value, oy["value2"], ...{ set v(val) { oz = val; } }.v] = [ |
| 1007, |
| 798432, |
| 555, |
| 1, 2, 3, 4, 5 |
| ]; |
| assertEquals(ox, 1007); |
| assertEquals(oy.value, 798432); |
| assertEquals(oy.value2, 555); |
| assertEquals(oz, [1, 2, 3, 4, 5]); |
| |
| |
| (function testInFunction() { |
| var x, y = {}, z; |
| ({ |
| x: x, |
| y: y.value, |
| y2: y["value2"], |
| z: ({ set v(val) { z = val; } }).v |
| } = { |
| x: "value of x", |
| y: "value of y1", |
| y2: "value of y2", |
| z: "value of z" |
| }); |
| assertEquals("value of x", x); |
| assertEquals("value of y1", y.value); |
| assertEquals("value of y2", y.value2); |
| assertEquals("value of z", z); |
| |
| [x, y.value, y["value2"], ...{ set v(val) { z = val; } }.v] = [ |
| 1007, |
| 798432, |
| 555, |
| 1, 2, 3, 4, 5 |
| ]; |
| assertEquals(x, 1007); |
| assertEquals(y.value, 798432); |
| assertEquals(y.value2, 555); |
| assertEquals(z, [1, 2, 3, 4, 5]); |
| })(); |
| |
| |
| (function testArrowFunctionInitializers() { |
| var fn = (config = { |
| value: defaults.value, |
| nada: { nada: defaults.nada } = { nada: "nothing" } |
| } = { value: "BLAH" }) => config; |
| var defaults = {}; |
| assertEquals({ value: "BLAH" }, fn()); |
| assertEquals("BLAH", defaults.value); |
| assertEquals("nothing", defaults.nada); |
| })(); |
| |
| |
| (function testArrowFunctionInitializers2() { |
| var fn = (config = [ |
| defaults.value, |
| { nada: defaults.nada } = { nada: "nothing" } |
| ] = ["BLAH"]) => config; |
| var defaults = {}; |
| assertEquals(["BLAH"], fn()); |
| assertEquals("BLAH", defaults.value); |
| assertEquals("nothing", defaults.nada); |
| })(); |
| |
| |
| (function testFunctionInitializers() { |
| function fn(config = { |
| value: defaults.value, |
| nada: { nada: defaults.nada } = { nada: "nothing" } |
| } = { value: "BLAH" }) { |
| return config; |
| } |
| var defaults = {}; |
| assertEquals({ value: "BLAH" }, fn()); |
| assertEquals("BLAH", defaults.value); |
| assertEquals("nothing", defaults.nada); |
| })(); |
| |
| |
| (function testFunctionInitializers2() { |
| function fn(config = [ |
| defaults.value, |
| { nada: defaults.nada } = { nada: "nothing" } |
| ] = ["BLAH"]) { return config; } |
| var defaults = {}; |
| assertEquals(["BLAH"], fn()); |
| assertEquals("BLAH", defaults.value); |
| assertEquals("nothing", defaults.nada); |
| })(); |
| |
| |
| (function testDeclarationInitializers() { |
| var defaults = {}; |
| var { value } = { value: defaults.value } = { value: "BLAH" }; |
| assertEquals("BLAH", value); |
| assertEquals("BLAH", defaults.value); |
| })(); |
| |
| |
| (function testDeclarationInitializers2() { |
| var defaults = {}; |
| var [value] = [defaults.value] = ["BLAH"]; |
| assertEquals("BLAH", value); |
| assertEquals("BLAH", defaults.value); |
| })(); |
| |
| |
| (function testObjectLiteralProperty() { |
| var ext = {}; |
| var obj = { |
| a: { b: ext.b, c: ext["c"], d: { set v(val) { ext.d = val; } }.v } = { |
| b: "b", c: "c", d: "d" } |
| }; |
| assertEquals({ b: "b", c: "c", d: "d" }, ext); |
| assertEquals({ a: { b: "b", c: "c", d: "d" } }, obj); |
| })(); |
| |
| |
| (function testArrayLiteralProperty() { |
| var ext = {}; |
| var obj = [ |
| ...[ ext.b, ext["c"], { set v(val) { ext.d = val; } }.v ] = [ |
| "b", "c", "d" ] |
| ]; |
| assertEquals({ b: "b", c: "c", d: "d" }, ext); |
| assertEquals([ "b", "c", "d" ], obj); |
| })(); |
| |
| |
| // TODO(caitp): add similar test for ArrayPatterns, once Proxies support |
| // delegating symbol-keyed get/set. |
| (function testObjectPatternOperationOrder() { |
| var steps = []; |
| var store = {}; |
| function computePropertyName(name) { |
| steps.push("compute name: " + name); |
| return name; |
| } |
| function loadValue(descr, value) { |
| steps.push("load: " + descr + " > " + value); |
| return value; |
| } |
| function storeValue(descr, name, value) { |
| steps.push("store: " + descr + " = " + value); |
| store[name] = value; |
| } |
| var result = { |
| get a() { assertUnreachable(); }, |
| set a(value) { storeValue("result.a", "a", value); }, |
| get b() { assertUnreachable(); }, |
| set b(value) { storeValue("result.b", "b", value); } |
| }; |
| |
| ({ |
| obj: { |
| x: result.a = 10, |
| [computePropertyName("y")]: result.b = false, |
| } = {} |
| } = { obj: { |
| get x() { return loadValue(".temp.obj.x", undefined); }, |
| set x(value) { assertUnreachable(); }, |
| get y() { return loadValue(".temp.obj.y", undefined); }, |
| set y(value) { assertUnreachable(); } |
| }}); |
| |
| assertPropertiesEqual({ |
| a: 10, |
| b: false |
| }, store); |
| |
| assertArrayEquals([ |
| "load: .temp.obj.x > undefined", |
| "store: result.a = 10", |
| |
| "compute name: y", |
| "load: .temp.obj.y > undefined", |
| "store: result.b = false" |
| ], steps); |
| |
| steps = []; |
| |
| ({ |
| obj: { |
| x: result.a = 50, |
| [computePropertyName("y")]: result.b = "hello", |
| } = {} |
| } = { obj: { |
| get x() { return loadValue(".temp.obj.x", 20); }, |
| set x(value) { assertUnreachable(); }, |
| get y() { return loadValue(".temp.obj.y", true); }, |
| set y(value) { assertUnreachable(); } |
| }}); |
| |
| assertPropertiesEqual({ |
| a: 20, |
| b: true |
| }, store); |
| |
| assertArrayEquals([ |
| "load: .temp.obj.x > 20", |
| "store: result.a = 20", |
| "compute name: y", |
| "load: .temp.obj.y > true", |
| "store: result.b = true", |
| ], steps); |
| })(); |
| |
| // Credit to Mike Pennisi and other Test262 contributors for originally writing |
| // the testse the following are based on. |
| (function testArrayElision() { |
| var value = [1, 2, 3, 4, 5, 6, 7, 8, 9]; |
| var a, obj = {}; |
| var result = [, a, , obj.b, , ...obj["rest"]] = value; |
| |
| assertEquals(result, value); |
| assertEquals(2, a); |
| assertEquals(4, obj.b); |
| assertArrayEquals([6, 7, 8, 9], obj.rest); |
| })(); |
| |
| (function testArrayElementInitializer() { |
| function test(value, initializer, expected) { |
| var a, obj = {}; |
| var initialized = false; |
| var shouldBeInitialized = value[0] === undefined; |
| assertEquals(value, [ a = (initialized = true, initializer) ] = value); |
| assertEquals(expected, a); |
| assertEquals(shouldBeInitialized, initialized); |
| |
| var initialized2 = false; |
| assertEquals(value, [ obj.a = (initialized2 = true, initializer) ] = value); |
| assertEquals(expected, obj.a); |
| assertEquals(shouldBeInitialized, initialized2); |
| } |
| |
| test([], "BAM!", "BAM!"); |
| test([], "BOOP!", "BOOP!"); |
| test([null], 123, null); |
| test([undefined], 456, 456); |
| test([,], "PUPPIES", "PUPPIES"); |
| |
| (function accept_IN() { |
| var value = [], x; |
| assertEquals(value, [ x = 'x' in {} ] = value); |
| assertEquals(false, x); |
| })(); |
| |
| (function ordering() { |
| var x = 0, a, b, value = []; |
| assertEquals(value, [ a = x += 1, b = x *= 2 ] = value); |
| assertEquals(1, a); |
| assertEquals(2, b); |
| assertEquals(2, x); |
| })(); |
| |
| (function yieldExpression() { |
| var value = [], it, result, x; |
| it = (function*() { |
| result = [ x = yield ] = value; |
| })(); |
| var next = it.next(); |
| |
| assertEquals(undefined, result); |
| assertEquals(undefined, next.value); |
| assertEquals(false, next.done); |
| assertEquals(undefined, x); |
| |
| next = it.next(86); |
| |
| assertEquals(value, result); |
| assertEquals(undefined, next.value); |
| assertEquals(true, next.done); |
| assertEquals(86, x); |
| })(); |
| |
| (function yieldIdentifier() { |
| var value = [], yield = "BOOP!", x; |
| assertEquals(value, [ x = yield ] = value); |
| assertEquals("BOOP!", x); |
| })(); |
| |
| assertThrows(function let_TDZ() { |
| "use strict"; |
| var x; |
| [ x = y ] = []; |
| let y; |
| }, ReferenceError); |
| })(); |
| |
| |
| (function testArrayElementNestedPattern() { |
| assertThrows(function nestedArrayRequireObjectCoercibleNull() { |
| var x; [ [ x ] ] = [ null ]; |
| }, TypeError); |
| |
| assertThrows(function nestedArrayRequireObjectCoercibleUndefined() { |
| var x; [ [ x ] ] = [ undefined ]; |
| }, TypeError); |
| |
| assertThrows(function nestedArrayRequireObjectCoercibleUndefined2() { |
| var x; [ [ x ] ] = [ ]; |
| }, TypeError); |
| |
| assertThrows(function nestedArrayRequireObjectCoercibleUndefined3() { |
| var x; [ [ x ] ] = [ , ]; |
| }, TypeError); |
| |
| assertThrows(function nestedObjectRequireObjectCoercibleNull() { |
| var x; [ { x } ] = [ null ]; |
| }, TypeError); |
| |
| assertThrows(function nestedObjectRequireObjectCoercibleUndefined() { |
| var x; [ { x } ] = [ undefined ]; |
| }, TypeError); |
| |
| assertThrows(function nestedObjectRequireObjectCoercibleUndefined2() { |
| var x; [ { x } ] = [ ]; |
| }, TypeError); |
| |
| assertThrows(function nestedObjectRequireObjectCoercibleUndefined3() { |
| var x; [ { x } ] = [ , ]; |
| }, TypeError); |
| |
| (function nestedArray() { |
| var x, value = [ [ "zap", "blonk" ] ]; |
| assertEquals(value, [ [ , x ] ] = value); |
| assertEquals("blonk", x); |
| })(); |
| |
| (function nestedObject() { |
| var x, value = [ { a: "zap", b: "blonk" } ]; |
| assertEquals(value, [ { b: x } ] = value); |
| assertEquals("blonk", x); |
| })(); |
| })(); |
| |
| (function testArrayRestElement() { |
| (function testBasic() { |
| var x, rest, array = [1, 2, 3]; |
| assertEquals(array, [x, ...rest] = array); |
| assertEquals(1, x); |
| assertEquals([2, 3], rest); |
| |
| array = [4, 5, 6]; |
| assertEquals(array, [, ...rest] = array); |
| assertEquals([5, 6], rest); |
| |
| })(); |
| |
| (function testNestedRestObject() { |
| var value = [1, 2, 3], x; |
| assertEquals(value, [...{ 1: x }] = value); |
| assertEquals(2, x); |
| })(); |
| |
| (function iterable() { |
| var count = 0; |
| var x, y, z; |
| function* g() { |
| count++; |
| yield; |
| count++; |
| yield; |
| count++; |
| yield; |
| } |
| var it = g(); |
| assertEquals(it, [...x] = it); |
| assertEquals([undefined, undefined, undefined], x); |
| assertEquals(3, count); |
| |
| it = [g()]; |
| assertEquals(it, [ [...y] ] = it); |
| assertEquals([undefined, undefined, undefined], y); |
| assertEquals(6, count); |
| |
| it = { a: g() }; |
| assertEquals(it, { a: [...z] } = it); |
| assertEquals([undefined, undefined, undefined], z); |
| assertEquals(9, count); |
| })(); |
| })(); |
| |
| (function testRequireObjectCoercible() { |
| assertThrows(() => ({} = undefined), TypeError); |
| assertThrows(() => ({} = null), TypeError); |
| assertThrows(() => [] = undefined, TypeError); |
| assertThrows(() => [] = null, TypeError); |
| assertEquals("test", ({} = "test")); |
| assertEquals("test", [] = "test"); |
| assertEquals(123, ({} = 123)); |
| })(); |
| |
| (function testConstReassignment() { |
| "use strict"; |
| const c = "untouchable"; |
| assertThrows(() => { [ c ] = [ "nope!" ]; }, TypeError); |
| assertThrows(() => { [ [ c ] ] = [ [ "nope!" ] ]; }, TypeError); |
| assertThrows(() => { [ { c } ] = [ { c: "nope!" } ]; }, TypeError); |
| assertThrows(() => { ({ c } = { c: "nope!" }); }, TypeError); |
| assertThrows(() => { ({ a: { c } } = { a: { c: "nope!" } }); }, TypeError); |
| assertThrows(() => { ({ a: [ c ] } = { a: [ "nope!" ] }); }, TypeError); |
| assertEquals("untouchable", c); |
| })(); |
| |
| (function testForIn() { |
| var log = []; |
| var x = {}; |
| var object = { |
| "Apenguin": 1, |
| "\u{1F382}cake": 2, |
| "Bpuppy": 3, |
| "Cspork": 4 |
| }; |
| for ([x.firstLetter, ...x.rest] in object) { |
| if (x.firstLetter === "A") { |
| assertEquals(["p", "e", "n", "g", "u", "i", "n"], x.rest); |
| continue; |
| } |
| if (x.firstLetter === "C") { |
| assertEquals(["s", "p", "o", "r", "k"], x.rest); |
| break; |
| } |
| log.push({ firstLetter: x.firstLetter, rest: x.rest }); |
| } |
| assertEquals([ |
| { firstLetter: "\u{1F382}", rest: ["c", "a", "k", "e"] }, |
| { firstLetter: "B", rest: ["p", "u", "p", "p", "y"] }, |
| ], log); |
| })(); |
| |
| (function testForOf() { |
| var log = []; |
| var x = {}; |
| var names = [ |
| "Apenguin", |
| "\u{1F382}cake", |
| "Bpuppy", |
| "Cspork" |
| ]; |
| for ([x.firstLetter, ...x.rest] of names) { |
| if (x.firstLetter === "A") { |
| assertEquals(["p", "e", "n", "g", "u", "i", "n"], x.rest); |
| continue; |
| } |
| if (x.firstLetter === "C") { |
| assertEquals(["s", "p", "o", "r", "k"], x.rest); |
| break; |
| } |
| log.push({ firstLetter: x.firstLetter, rest: x.rest }); |
| } |
| assertEquals([ |
| { firstLetter: "\u{1F382}", rest: ["c", "a", "k", "e"] }, |
| { firstLetter: "B", rest: ["p", "u", "p", "p", "y"] }, |
| ], log); |
| })(); |
| |
| (function testNewTarget() { |
| assertThrows("(function() { [...new.target] = []; })", SyntaxError); |
| assertThrows("(function() { [a] = [...new.target] = []; })", SyntaxError); |
| assertThrows("(function() { [new.target] = []; })", SyntaxError); |
| assertThrows("(function() { [a] = [new.target] = []; })", SyntaxError); |
| assertThrows("(function() { ({ a: new.target] = {a: 0}); })", SyntaxError); |
| assertThrows("(function() { ({ a } = { a: new.target } = {}); })", |
| SyntaxError); |
| |
| function ReturnNewTarget1() { |
| var result; |
| [result = new.target] = []; |
| return result; |
| } |
| |
| function ReturnNewTarget2() { |
| var result; |
| [result] = [new.target]; |
| return result; |
| } |
| |
| function ReturnNewTarget3() { |
| var result; |
| ({ result = new.target } = {}); |
| return result; |
| } |
| |
| function ReturnNewTarget4() { |
| var result; |
| ({ result } = { result: new.target }); |
| return result; |
| } |
| |
| function FakeNewTarget() {} |
| |
| function construct() { |
| assertEquals(undefined, ReturnNewTarget1()); |
| assertEquals(ReturnNewTarget1, new ReturnNewTarget1()); |
| assertEquals(FakeNewTarget, |
| Reflect.construct(ReturnNewTarget1, [], FakeNewTarget)); |
| |
| assertEquals(undefined, ReturnNewTarget2()); |
| assertEquals(ReturnNewTarget2, new ReturnNewTarget2()); |
| assertEquals(FakeNewTarget, |
| Reflect.construct(ReturnNewTarget2, [], FakeNewTarget)); |
| |
| assertEquals(undefined, ReturnNewTarget3()); |
| assertEquals(ReturnNewTarget3, new ReturnNewTarget3()); |
| assertEquals(FakeNewTarget, |
| Reflect.construct(ReturnNewTarget3, [], FakeNewTarget)); |
| |
| assertEquals(undefined, ReturnNewTarget4()); |
| assertEquals(ReturnNewTarget4, new ReturnNewTarget4()); |
| assertEquals(FakeNewTarget, |
| Reflect.construct(ReturnNewTarget4, [], FakeNewTarget)); |
| } |
| construct(); |
| FakeNewTarget.prototype = 1; |
| construct(); |
| })(); |
| |
| (function testSuperCall() { |
| function ctor(body) { |
| return () => eval("(class extends Object { \n" + |
| " constructor() {\n" + |
| body + |
| "\n }\n" + |
| "})"); |
| } |
| assertThrows(ctor("({ new: super() } = {})"), SyntaxError); |
| assertThrows(ctor("({ new: x } = { new: super() } = {})"), SyntaxError); |
| assertThrows(ctor("[super()] = []"), SyntaxError); |
| assertThrows(ctor("[x] = [super()] = []"), SyntaxError); |
| assertThrows(ctor("[...super()] = []"), SyntaxError); |
| assertThrows(ctor("[x] = [...super()] = []"), SyntaxError); |
| |
| class Base { get foo() { return 1; } } |
| function ext(body) { |
| return eval("new (class extends Base {\n" + |
| " constructor() {\n" + |
| body + ";\n" + |
| " return { x: super.foo }" + |
| "\n }\n" + |
| "})"); |
| } |
| assertEquals(1, ext("let x; [x = super()] = []").x); |
| assertEquals(1, ext("let x, y; [y] = [x = super()] = []").x); |
| assertEquals(1, ext("let x; [x] = [super()]").x); |
| assertEquals(1, ext("let x, y; [y] = [x] = [super()]").x); |
| |
| assertEquals(1, ext("let x; ({x = super()} = {})").x); |
| assertEquals(1, ext("let x, y; ({ x: y } = { x = super() } = {})").x); |
| assertEquals(1, ext("let x; ({x} = { x: super() })").x); |
| assertEquals(1, ext("let x, y; ({ x: y } = { x } = { x: super() })").x); |
| })(); |
| |
| (function testInvalidReturn() { |
| function* g() { yield 1; } |
| |
| let executed_x_setter; |
| let executed_return; |
| var a = { |
| set x(val) { |
| executed_x_setter = true; |
| throw 3; |
| } |
| }; |
| |
| // The exception from the execution of g().return() should be suppressed by |
| // the setter error. |
| executed_x_setter = false; |
| executed_return = false; |
| g.prototype.return = function() { |
| executed_return = true; |
| throw 4; |
| }; |
| assertThrowsEquals("[a.x] = g()", 3); |
| assertTrue(executed_x_setter); |
| assertTrue(executed_return); |
| |
| // The exception from g().return() not returning an object should be |
| // suppressed by the setter error. |
| executed_x_setter = false; |
| executed_return = false; |
| g.prototype.return = function() { |
| assertTrue(executed_return); |
| return null; |
| }; |
| assertThrowsEquals("[a.x] = g()", 3); |
| assertTrue(executed_x_setter); |
| assertTrue(executed_return); |
| |
| // The TypeError from g().return not being a method should suppress the setter |
| // error. |
| executed_x_setter = false; |
| g.prototype.return = "not a method"; |
| assertThrows("[a.x] = g()", TypeError); |
| assertTrue(executed_x_setter); |
| |
| // The exception from the access of g().return should suppress the setter |
| // error. |
| executed_x_setter = false; |
| Object.setPrototypeOf(g.prototype, { |
| get return() { |
| throw 4; |
| } |
| }); |
| assertThrowsEquals("[a.x] = g()", 4); |
| assertTrue(executed_x_setter); |
| }) |