Source code

Revision control

Copy as Markdown

Other Tools

var BUGNUMBER = 1180306;
var summary = 'Map/Set/WeakMap/WeakSet constructor should close iterator on error';
print(BUGNUMBER + ": " + summary);
function test(ctors, { nextVal=undefined,
nextThrowVal=undefined,
modifier=undefined,
exceptionVal=undefined,
exceptionType=undefined,
closed=true }) {
function getIterable() {
let iterable = {
closed: false,
[Symbol.iterator]() {
let iterator = {
first: true,
next() {
if (this.first) {
this.first = false;
if (nextThrowVal)
throw nextThrowVal;
return nextVal;
}
return { value: undefined, done: true };
},
return() {
iterable.closed = true;
return {};
}
};
if (modifier)
modifier(iterator, iterable);
return iterator;
}
};
return iterable;
}
for (let ctor of ctors) {
let iterable = getIterable();
if (exceptionVal) {
let caught = false;
try {
new ctor(iterable);
} catch (e) {
assertEq(e, exceptionVal);
caught = true;
}
assertEq(caught, true);
} else if (exceptionType) {
assertThrowsInstanceOf(() => new ctor(iterable), exceptionType);
} else {
new ctor(iterable);
}
assertEq(iterable.closed, closed);
}
}
// == Error cases with close ==
// ES 2017 draft 23.1.1.1 Map step 8.d.ii.
// ES 2017 draft 23.3.1.1 WeakMap step 8.d.ii.
test([Map, WeakMap], {
nextVal: { value: "non object", done: false },
exceptionType: TypeError,
closed: true,
});
// ES 2017 draft 23.1.1.1 Map step 8.f.
// ES 2017 draft 23.3.1.1 WeakMap step 8.f.
test([Map, WeakMap], {
nextVal: { value: { get 0() { throw "0 getter throws"; } }, done: false },
exceptionVal: "0 getter throws",
closed: true,
});
// ES 2017 draft 23.1.1.1 Map step 8.h.
// ES 2017 draft 23.3.1.1 WeakMap step 8.h.
test([Map, WeakMap], {
nextVal: { value: { 0: {}, get 1() { throw "1 getter throws"; } }, done: false },
exceptionVal: "1 getter throws",
closed: true,
});
// ES 2017 draft 23.1.1.1 Map step 8.j.
// ES 2017 draft 23.3.1.1 WeakMap step 8.j.
class MyMap extends Map {
set(k, v) {
throw "setter throws";
}
}
class MyWeakMap extends WeakMap {
set(k, v) {
throw "setter throws";
}
}
test([MyMap, MyWeakMap], {
nextVal: { value: [{}, {}], done: false },
exceptionVal: "setter throws",
closed: true,
});
// ES 2017 draft 23.2.1.1 Set step 8.e.
// ES 2017 draft 23.4.1.1 WeakSet step 8.e.
class MySet extends Set {
add(v) {
throw "adder throws";
}
}
class MyWeakSet extends WeakSet {
add(v) {
throw "adder throws";
}
}
test([MySet, MyWeakSet], {
nextVal: { value: {}, done: false },
exceptionVal: "adder throws",
closed: true,
});
// ES 2021 draft 7.4.6 step 5.
// if GetMethod fails, the thrown value should be ignored.
test([MyMap, MyWeakMap], {
nextVal: { value: [{}, {}], done: false },
modifier: (iterator, iterable) => {
Object.defineProperty(iterator, "return", {
get: function() {
iterable.closed = true;
throw "return getter throws";
}
});
},
exceptionVal: "setter throws",
closed: true,
});
test([MySet, MyWeakSet], {
nextVal: { value: [{}, {}], done: false },
modifier: (iterator, iterable) => {
Object.defineProperty(iterator, "return", {
get: function() {
iterable.closed = true;
throw "return getter throws";
}
});
},
exceptionVal: "adder throws",
closed: true,
});
test([MyMap, MyWeakMap], {
nextVal: { value: [{}, {}], done: false },
modifier: (iterator, iterable) => {
Object.defineProperty(iterator, "return", {
get: function() {
iterable.closed = true;
return "non object";
}
});
},
exceptionVal: "setter throws",
closed: true,
});
test([MySet, MyWeakSet], {
nextVal: { value: [{}, {}], done: false },
modifier: (iterator, iterable) => {
Object.defineProperty(iterator, "return", {
get: function() {
iterable.closed = true;
return "non object";
}
});
},
exceptionVal: "adder throws",
closed: true,
});
test([MyMap, MyWeakMap], {
nextVal: { value: [{}, {}], done: false },
modifier: (iterator, iterable) => {
Object.defineProperty(iterator, "return", {
get: function() {
iterable.closed = true;
// Non callable.
return {};
}
});
},
exceptionVal: "setter throws",
closed: true,
});
test([MySet, MyWeakSet], {
nextVal: { value: [{}, {}], done: false },
modifier: (iterator, iterable) => {
Object.defineProperty(iterator, "return", {
get: function() {
iterable.closed = true;
// Non callable.
return {};
}
});
},
exceptionVal: "adder throws",
closed: true,
});
// ES 2017 draft 7.4.6 steps 6.
// if return method throws, the thrown value should be ignored.
test([MyMap, MyWeakMap], {
nextVal: { value: [{}, {}], done: false },
modifier: (iterator, iterable) => {
iterator.return = function() {
iterable.closed = true;
throw "return throws";
};
},
exceptionVal: "setter throws",
closed: true,
});
test([MySet, MyWeakSet], {
nextVal: { value: [{}, {}], done: false },
modifier: (iterator, iterable) => {
iterator.return = function() {
iterable.closed = true;
throw "return throws";
};
},
exceptionVal: "adder throws",
closed: true,
});
test([MyMap, MyWeakMap], {
nextVal: { value: [{}, {}], done: false },
modifier: (iterator, iterable) => {
iterator.return = function() {
iterable.closed = true;
return "non object";
};
},
exceptionVal: "setter throws",
closed: true,
});
test([MySet, MyWeakSet], {
nextVal: { value: [{}, {}], done: false },
modifier: (iterator, iterable) => {
iterator.return = function() {
iterable.closed = true;
return "non object";
};
},
exceptionVal: "adder throws",
closed: true,
});
// == Error cases without close ==
// ES 2017 draft 23.1.1.1 Map step 8.a.
// ES 2017 draft 23.3.1.1 WeakMap step 8.a.
// ES 2017 draft 23.2.1.1 Set step 8.a.
// ES 2017 draft 23.4.1.1 WeakSet step 8.a.
test([Map, WeakMap, Set, WeakSet], {
nextThrowVal: "next throws",
exceptionVal: "next throws",
closed: false,
});
test([Map, WeakMap, Set, WeakSet], {
nextVal: { value: {}, get done() { throw "done getter throws"; } },
exceptionVal: "done getter throws",
closed: false,
});
// ES 2017 draft 23.1.1.1 Map step 8.c.
// ES 2017 draft 23.3.1.1 WeakMap step 8.c.
// ES 2017 draft 23.2.1.1 Set step 8.c.
// ES 2017 draft 23.4.1.1 WeakSet step 8.c.
test([Map, WeakMap, Set, WeakSet], {
nextVal: { get value() { throw "value getter throws"; }, done: false },
exceptionVal: "value getter throws",
closed: false,
});
// == Successful cases ==
test([Map, WeakMap], {
nextVal: { value: [{}, {}], done: false },
closed: false,
});
test([Set, WeakSet], {
nextVal: { value: {}, done: false },
closed: false,
});
if (typeof reportCompare === 'function')
reportCompare(true, true);