v8/test/mjsunit/es6/destructuring-assignment.js
Leszek Swirski 7fbbce5fa1 [ignition] Fix iteration finalization exception suppression
The IteratorClose spec specifies that exceptions in
%GetMethod(iterator.return) are not suppressed by exceptions in the
given continuation (body of a loop, assignments in destructuring),
while exceptions in the execution of iterator.return() are.

This means that we have to split out the property access + a typeof
check to be outside the try-catch, and keep the call inside of it.

The non-split version is only for cases when there is no 'throws'
continuation (as is the case for yield* calling IteratorClose), so
the existing BuildIteratorClose can be renamed to reflect this.

Change-Id: Id71aea4fddd6ffb986bd9aaa09d29615a8800f71
Reviewed-on: https://chromium-review.googlesource.com/c/1402789
Reviewed-by: Georg Neis <neis@chromium.org>
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58694}
2019-01-10 11:29:12 +00:00

632 lines
17 KiB
JavaScript

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