|  | // 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); | 
|  | }) |