More set methods tests (#3966)

* add tests for difference, intersection, symmetricDifference

* add tests for predicates

* more tests for ordering of calls

* fix copyrights

* add missed -0 test

* pass argument to methods, not just this

* assert methods return new objects

* typo in filename

* fix parens

* Update test/built-ins/Set/prototype/difference/add-not-called.js

Co-authored-by: Jordan Harband <ljharb@gmail.com>

* fix isSupersetOf/set-like-array.js

* add tests for negative zero with isDisjointFrom and isSupersetOf

* fix message in isSupersetOf/converts-negative-zero.js

* address comments from review

---------

Co-authored-by: Jordan Harband <ljharb@gmail.com>
This commit is contained in:
Kevin Gibbons 2024-01-10 12:52:41 -08:00 committed by GitHub
parent 80590ce79a
commit c8cd136888
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
164 changed files with 5455 additions and 39 deletions

View File

@ -0,0 +1,27 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.difference
description: Set.prototype.difference should not call Set.prototype.add
features: [set-methods]
includes: [compareArray.js]
---*/
const s1 = new Set([1, 2]);
const s2 = new Set([2, 3]);
const expected = [1];
const originalAdd = Set.prototype.add;
let count = 0;
Set.prototype.add = function (...rest) {
count++;
return originalAdd.apply(this, rest);
};
const combined = s1.difference(s2);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");
assert.sameValue(count, 0, "Add is never called");
Set.prototype.add = originalAdd;

View File

@ -0,0 +1,35 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.difference
description: GetSetRecord allows instances of Set-like classes
info: |
1. If obj is not an Object, throw a TypeError exception.
2. Let rawSize be ? Get(obj, "size").
...
7. Let has be ? Get(obj, "has").
...
9. Let keys be ? Get(obj, "keys").
features: [set-methods]
includes: [compareArray.js]
---*/
const s1 = new Set([1, 2]);
const s2 = new class {
get size() {
return 2;
}
has(v) {
if (v === 1) return false;
if (v === 2) return true;
throw new Test262Error("Set.prototype.difference should only call its argument's has method with contents of this");
}
* keys() {
throw new Test262Error("Set.prototype.difference should not call its argument's keys iterator when this.size ≤ arg.size");
}
};
const expected = [1];
const combined = s1.difference(s2);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");

View File

@ -0,0 +1,33 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.difference
description: GetSetRecord allows Set-like objects
info: |
1. If obj is not an Object, throw a TypeError exception.
2. Let rawSize be ? Get(obj, "size").
...
7. Let has be ? Get(obj, "has").
...
9. Let keys be ? Get(obj, "keys").
features: [set-methods]
includes: [compareArray.js]
---*/
const s1 = new Set([1, 2]);
const s2 = {
size: 2,
has: (v) => {
if (v === 1) return false;
if (v === 2) return true;
throw new Test262Error("Set.prototype.difference should only call its argument's has method with contents of this");
},
keys: function* keys() {
throw new Test262Error("Set.prototype.difference should not call its argument's keys iterator when this.size ≤ arg.size");
},
};
const expected = [1];
const combined = s1.difference(s2);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");

View File

@ -0,0 +1,17 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.difference
description: Set.prototype.difference doesn't work with arrays
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = [3];
assert.throws(
TypeError,
function () {
s1.difference(s2);
},
"Throws an error when an array is used"
);

View File

@ -0,0 +1,25 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.difference
description: Tests that Set.prototype.difference meets the requirements for built-in objects
features: [set-methods]
---*/
assert.sameValue(
Object.isExtensible(Set.prototype.difference),
true,
"Built-in objects must be extensible."
);
assert.sameValue(
Object.prototype.toString.call(Set.prototype.difference),
"[object Function]",
"Object.prototype.toString"
);
assert.sameValue(
Object.getPrototypeOf(Set.prototype.difference),
Function.prototype,
"prototype"
);

View File

@ -0,0 +1,66 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-getsetrecord
description: GetSetRecord throws if obj is not an object
info: |
1. If obj is not an Object, throw a TypeError exception.
features: [set-methods]
---*/
let s1 = new Set([1]);
assert.throws(
TypeError,
function () {
s1.difference(1);
},
"number"
);
assert.throws(
TypeError,
function () {
s1.difference("");
},
"string"
);
assert.throws(
TypeError,
function () {
s1.difference(1n);
},
"bigint"
);
assert.throws(
TypeError,
function () {
s1.difference(false);
},
"boolean"
);
assert.throws(
TypeError,
function () {
s1.difference(undefined);
},
"undefined"
);
assert.throws(
TypeError,
function () {
s1.difference(null);
},
"null"
);
assert.throws(
TypeError,
function () {
s1.difference(Symbol("test"));
},
"symbol"
);

View File

@ -0,0 +1,19 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.difference
description: Set.prototype.difference combines with Map
features: [set-methods]
includes: [compareArray.js]
---*/
const s1 = new Set([1, 2]);
const m1 = new Map([
[2, "two"],
[3, "three"],
]);
const expected = [1];
const combined = s1.difference(m1);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");

View File

@ -0,0 +1,32 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.difference
description: Set.prototype.difference can combine empty Sets
features: [set-methods]
includes: [compareArray.js]
---*/
const s1 = new Set([]);
const s2 = new Set([1, 2]);
let expected = [];
let combined = s1.difference(s2);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");
const s3 = new Set([1, 2]);
const s4 = new Set([]);
expected = [1, 2];
combined = s3.difference(s4);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");
const s5 = new Set([]);
const s6 = new Set([]);
expected = [];
combined = s5.difference(s6);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");

View File

@ -0,0 +1,16 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.difference
description: Set.prototype.difference is successful when called on itself
features: [set-methods]
includes: [compareArray.js]
---*/
const s1 = new Set([1, 2]);
const expected = [];
const combined = s1.difference(s1);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");
assert.sameValue(combined === s1, false, "The returned object is a new object");

View File

@ -0,0 +1,18 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.difference
description: Set.prototype.difference can combine Sets that have the same content
features: [set-methods]
includes: [compareArray.js]
---*/
const s1 = new Set([1, 2]);
const s2 = new Set([1, 2]);
const expected = [];
const combined = s1.difference(s2);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");
assert.sameValue(combined === s1, false, "The returned object is a new object");
assert.sameValue(combined === s2, false, "The returned object is a new object");

View File

@ -0,0 +1,16 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.difference
description: Set.prototype.difference combines Sets
features: [set-methods]
includes: [compareArray.js]
---*/
const s1 = new Set([1, 2]);
const s2 = new Set([2, 3]);
const expected = [1];
const combined = s1.difference(s2);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");

View File

@ -0,0 +1,28 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.difference
description: Set.prototype.difference converts -0𝔽 to +0𝔽
info: |
7.b.ii If nextValue is -0𝔽, set nextValue to +0𝔽.
features: [set-methods]
includes: [compareArray.js]
---*/
const setlikeWithMinusZero = {
size: 1,
has: function () {
throw new Test262Error("Set.prototype.difference should not call its argument's has method when this.size > arg.size");
},
keys: function () {
// we use an array here because the Set constructor would normalize away -0
return [-0].values();
},
};
const s1 = new Set([+0, 1]);
let expected = [1];
let combined = s1.difference(setlikeWithMinusZero);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");

View File

@ -0,0 +1,20 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.difference
description: Set.prototype.difference properties
includes: [propertyHelper.js]
features: [set-methods]
---*/
assert.sameValue(
typeof Set.prototype.difference,
"function",
"`typeof Set.prototype.difference` is `'function'`"
);
verifyProperty(Set.prototype, "difference", {
enumerable: false,
writable: true,
configurable: true,
});

View File

@ -0,0 +1,36 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-getsetrecord
description: GetSetRecord throws an exception if the Set-like object's 'has' property is not callable
info: |
7. Let has be ? Get(obj, "has").
8. If IsCallable(has) is false, throw a TypeError exception.
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = {
size: 2,
has: undefined,
keys: function* keys() {
yield 2;
yield 3;
},
};
assert.throws(
TypeError,
function () {
s1.difference(s2);
},
"GetSetRecord throws an error when has is undefined"
);
s2.has = {};
assert.throws(
TypeError,
function () {
s1.difference(s2);
},
"GetSetRecord throws an error when has is not callable"
);

View File

@ -0,0 +1,33 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-getsetrecord
description: GetSetRecord throws an exception if the Set-like object's 'keys' property is not callable
info: |
9. Let keys be ? Get(obj, "keys").
10. If IsCallable(keys) is false, throw a TypeError exception.
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = {
size: 2,
has: () => {},
keys: undefined,
};
assert.throws(
TypeError,
function () {
s1.difference(s2);
},
"GetSetRecord throws an error when keys is undefined"
);
s2.keys = {};
assert.throws(
TypeError,
function () {
s1.difference(s2);
},
"GetSetRecord throws an error when keys is not callable"
);

View File

@ -0,0 +1,18 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.difference
description: Set.prototype.difference length property
info: |
Set.prototype.difference ( other )
includes: [propertyHelper.js]
features: [set-methods]
---*/
assert.sameValue(typeof Set.prototype.difference, "function");
verifyProperty(Set.prototype.difference, "length", {
enumerable: false,
writable: false,
configurable: true,
value: 1,
});

View File

@ -0,0 +1,18 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.difference
description: Set.prototype.difference name property
info: |
Set.prototype.difference ( other )
includes: [propertyHelper.js]
features: [set-methods]
---*/
assert.sameValue(typeof Set.prototype.difference, "function");
verifyProperty(Set.prototype.difference, "name", {
enumerable: false,
writable: false,
configurable: true,
value: "difference",
});

View File

@ -0,0 +1,22 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.difference
description: Set.prototype.difference does not implement [[Construct]], is not new-able
includes: [isConstructor.js]
features: [Reflect.construct, set-methods]
---*/
assert.sameValue(
isConstructor(Set.prototype.difference),
false,
"isConstructor(Set.prototype.difference) must return false"
);
assert.throws(
TypeError,
() => {
new Set.prototype.difference();
},
"`new Set.prototype.difference()` throws TypeError"
);

View File

@ -0,0 +1,44 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.difference
description: Set.prototype.difference throws when receiver is not a Set
features: [set-methods]
---*/
class MySetLike {
constructor() {
this.size = 2;
this.has = () => {};
this.keys = function* keys() {
yield 2;
yield 3;
};
}
}
const s1 = new MySetLike();
const s2 = new Set();
assert.throws(
TypeError,
() => {
Set.prototype.difference.call(s1, s2);
},
"Set-like class"
);
const s3 = {
size: 2,
has: () => {},
keys: function* keys() {
yield 2;
yield 3;
},
};
assert.throws(
TypeError,
() => {
Set.prototype.difference.call(s3, s2);
},
"Set-like object"
);

View File

@ -0,0 +1,27 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.difference
description: Set.prototype.difference RequireInternalSlot
info: |
2. Perform ? RequireInternalSlot(O, [[SetData]])
features: [set-methods]
---*/
const difference = Set.prototype.difference;
assert.sameValue(typeof difference, "function");
const realSet = new Set([]);
assert.throws(TypeError, () => difference.call(undefined, realSet), "undefined");
assert.throws(TypeError, () => difference.call(null, realSet), "null");
assert.throws(TypeError, () => difference.call(true, realSet), "true");
assert.throws(TypeError, () => difference.call("", realSet), "empty string");
assert.throws(TypeError, () => difference.call(Symbol(), realSet), "symbol");
assert.throws(TypeError, () => difference.call(1, realSet), "1");
assert.throws(TypeError, () => difference.call(1n, realSet), "1n");
assert.throws(TypeError, () => difference.call({}, realSet), "plain object");
assert.throws(TypeError, () => difference.call([], realSet), "array");
assert.throws(TypeError, () => difference.call(new Map(), realSet), "map");
assert.throws(TypeError, () => difference.call(Set.prototype, realSet), "Set.prototype");

View File

@ -0,0 +1,37 @@
// Copyright (C) 2023 Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.difference
description: Set.prototype.difference orders results as in this, regardless of sizes
features: [set-methods]
includes: [compareArray.js]
---*/
{
const s1 = new Set([1, 2, 3, 4]);
const s2 = new Set([6, 5, 3, 2]);
assert.compareArray([...s1.difference(s2)], [1, 4]);
}
{
const s1 = new Set([6, 5, 3, 2]);
const s2 = new Set([1, 2, 3, 4]);
assert.compareArray([...s1.difference(s2)], [6, 5]);
}
{
const s1 = new Set([1, 2, 3, 4]);
const s2 = new Set([7, 6, 5, 3, 2]);
assert.compareArray([...s1.difference(s2)], [1, 4]);
}
{
const s1 = new Set([7, 6, 5, 3, 2]);
const s2 = new Set([1, 2, 3, 4]);
assert.compareArray([...s1.difference(s2)], [7, 6, 5]);
}

View File

@ -0,0 +1,26 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.difference
description: Set.prototype.difference consumes a set-like array as a set-like, not an array
features: [set-methods]
includes: [compareArray.js]
---*/
const s1 = new Set([1, 2]);
const s2 = [5, 6];
s2.size = 3;
s2.has = function (v) {
if (v === 1) return false;
if (v === 2) return true;
throw new Test262Error("Set.prototype.difference should only call its argument's has method with contents of this");
};
s2.keys = function () {
throw new Test262Error("Set.prototype.difference should not call its argument's keys iterator when this.size ≤ arg.size");
};
const expected = [1];
const combined = s1.difference(s2);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");

View File

@ -0,0 +1,49 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.difference
description: Set.prototype.difference maintains values even when a custom Set-like class mutates the receiver
features: [set-methods]
includes: [compareArray.js]
---*/
const baseSet = new Set(["a", "b", "c", "d", "e"]);
function mutatingIterator() {
let index = 0;
let values = ["x", "b", "b"];
return {
next() {
if (index === 0) {
baseSet.delete("b");
baseSet.delete("c");
baseSet.add("b");
baseSet.add("d");
}
return {
done: index >= values.length,
value: values[index++],
};
},
};
}
const evilSetLike = {
size: 3,
get has() {
baseSet.add("q");
return function () {
throw new Test262Error("Set.prototype.difference should not invoke .has on its argument when this.size > other.size");
};
},
keys() {
return mutatingIterator();
},
};
const combined = baseSet.difference(evilSetLike);
const expectedCombined = ["a", "c", "d", "e", "q"];
assert.compareArray([...combined], expectedCombined);
const expectedNewBase = ["a", "d", "e", "q", "b"];
assert.compareArray([...baseSet], expectedNewBase);

View File

@ -0,0 +1,140 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.difference
description: Set.prototype.difference calls a Set-like class's methods in order
features: [set-methods]
includes: [compareArray.js]
---*/
let observedOrder = [];
function observableIterator() {
let values = ["a", "b", "c"];
let index = 0;
return {
get next() {
observedOrder.push("getting next");
return function () {
observedOrder.push("calling next");
return {
get done() {
observedOrder.push("getting done");
return index >= values.length;
},
get value() {
observedOrder.push("getting value");
return values[index++];
},
};
};
},
};
}
class MySetLike {
get size() {
observedOrder.push("getting size");
return {
valueOf: function () {
observedOrder.push("ToNumber(size)");
return 3;
},
};
}
get has() {
observedOrder.push("getting has");
return function (v) {
observedOrder.push("calling has");
return ["a", "b", "c"].indexOf(v) !== -1;
};
}
get keys() {
observedOrder.push("getting keys");
return function () {
observedOrder.push("calling keys");
return observableIterator();
};
}
}
// this is smaller than argument
{
observedOrder = [];
const s1 = new Set(["a", "d"]);
const s2 = new MySetLike();
const combined = s1.difference(s2);
const expectedOrder = [
"getting size",
"ToNumber(size)",
"getting has",
"getting keys",
// two calls to has
"calling has",
"calling has",
];
assert.compareArray([...combined], ["d"]);
assert.compareArray(observedOrder, expectedOrder);
}
// this is same size as argument
{
observedOrder = [];
const s1 = new Set(["a", "b", "d"]);
const s2 = new MySetLike();
const combined = s1.difference(s2);
const expectedOrder = [
"getting size",
"ToNumber(size)",
"getting has",
"getting keys",
// three calls to has
"calling has",
"calling has",
"calling has",
];
assert.compareArray([...combined], ["d"]);
assert.compareArray(observedOrder, expectedOrder);
}
// this is larger than argument
{
observedOrder = [];
const s1 = new Set(["a", "b", "c", "d"]);
const s2 = new MySetLike();
const combined = s1.difference(s2);
const expectedOrder = [
"getting size",
"ToNumber(size)",
"getting has",
"getting keys",
"calling keys",
"getting next",
// first iteration, has value
"calling next",
"getting done",
"getting value",
// second iteration, has value
"calling next",
"getting done",
"getting value",
// third iteration, has value
"calling next",
"getting done",
"getting value",
// fourth iteration, no value; ends
"calling next",
"getting done",
];
assert.compareArray([...combined], ["d"]);
assert.compareArray(observedOrder, expectedOrder);
}

View File

@ -0,0 +1,72 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-getsetrecord
description: GetSetRecord throws an exception if the Set-like object has a size that is coerced to NaN
info: |
2. Let rawSize be ? Get(obj, "size").
3. Let numSize be ? ToNumber(rawSize).
4. NOTE: If rawSize is undefined, then numSize will be NaN.
5. If numSize is NaN, throw a TypeError exception.
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = {
size: undefined,
has: () => {},
keys: function* keys() {
yield 2;
yield 3;
},
};
assert.throws(
TypeError,
function () {
s1.difference(s2);
},
"GetSetRecord throws an error when size is undefined"
);
s2.size = NaN;
assert.throws(
TypeError,
function () {
s1.difference(s2);
},
"GetSetRecord throws an error when size is NaN"
);
let coercionCalls = 0;
s2.size = {
valueOf: function() {
++coercionCalls;
return NaN;
},
};
assert.throws(
TypeError,
function () {
s1.difference(s2);
},
"GetSetRecord throws an error when size coerces to NaN"
);
assert.sameValue(coercionCalls, 1, "GetSetRecord coerces size");
s2.size = 0n;
assert.throws(
TypeError,
function () {
s1.difference(s2);
},
"GetSetRecord throws an error when size is a BigInt"
);
s2.size = "string";
assert.throws(
TypeError,
function () {
s1.difference(s2);
},
"GetSetRecord throws an error when size is a non-numeric string"
);

View File

@ -0,0 +1,45 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.difference
description: Set.prototype.difference works on subclasses of Set, but never calls the receiver's size/has/keys methods
features: [set-methods]
includes: [compareArray.js]
---*/
let sizeCount = 0;
let hasCount = 0;
let keysCount = 0;
class MySet extends Set {
size(...rest) {
sizeCount++;
return super.size(...rest);
}
has(...rest) {
hasCount++;
return super.has(...rest);
}
keys(...rest) {
keysCount++;
return super.keys(...rest);
}
}
const s1 = new MySet([1, 2]);
const s2 = new Set([2, 3]);
const expected = [2];
const combined = s1.difference(s2);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");
assert.sameValue(
combined instanceof MySet,
false,
"The returned object is a Set, not a subclass"
);
assert.sameValue(sizeCount, 0, "size should not be called on the receiver");
assert.sameValue(hasCount, 0, "has should not be called on the receiver");
assert.sameValue(keysCount, 0, "keys should not be called on the receiver");

View File

@ -0,0 +1,29 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.difference
description: Set.prototype.difference works on subclasses of Set, but returns an instance of Set even when Symbol.species is overridden.
features: [set-methods]
includes: [compareArray.js]
---*/
var count = 0;
class MySet extends Set {
static get [Symbol.species]() {
count++;
return Set;
}
}
const s1 = new MySet([1, 2]);
const s2 = new Set([2, 3]);
const expected = [1];
const combined = s1.difference(s2);
assert.compareArray([...combined], expected);
assert.sameValue(count, 0, "Symbol.species is never called");
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");
assert.sameValue(
combined instanceof MySet,
false,
"The returned object is a Set, not a subclass"
);

View File

@ -0,0 +1,23 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.difference
description: Set.prototype.difference works on subclasses of Set, but returns an instance of Set
features: [set-methods]
includes: [compareArray.js]
---*/
class MySet extends Set {}
const s1 = new MySet([1, 2]);
const s2 = new Set([2, 3]);
const expected = [1];
const combined = s1.difference(s2);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");
assert.sameValue(
combined instanceof MySet,
false,
"The returned object is a Set, not a subclass"
);

View File

@ -0,0 +1,27 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.intersection
description: Set.prototype.intersection should not call Set.prototype.add
features: [set-methods]
includes: [compareArray.js]
---*/
const s1 = new Set([1, 2]);
const s2 = new Set([2, 3]);
const expected = [2];
const originalAdd = Set.prototype.add;
let count = 0;
Set.prototype.add = function (...rest) {
count++;
return originalAdd.apply(this, rest);
};
const combined = s1.intersection(s2);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");
assert.sameValue(count, 0, "Add is never called");
Set.prototype.add = originalAdd;

View File

@ -0,0 +1,35 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.intersection
description: GetSetRecord allows instances of Set-like classes
info: |
1. If obj is not an Object, throw a TypeError exception.
2. Let rawSize be ? Get(obj, "size").
...
7. Let has be ? Get(obj, "has").
...
9. Let keys be ? Get(obj, "keys").
features: [set-methods]
includes: [compareArray.js]
---*/
const s1 = new Set([1, 2]);
const s2 = new class {
get size() {
return 2;
}
has(v) {
if (v === 1) return false;
if (v === 2) return true;
throw new Test262Error("Set.prototype.intersection should only call its argument's has method with contents of this");
}
* keys() {
throw new Test262Error("Set.prototype.intersection should not call its argument's keys iterator when this.size ≤ arg.size");
}
};
const expected = [2];
const combined = s1.intersection(s2);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");

View File

@ -0,0 +1,33 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.intersection
description: GetSetRecord allows Set-like objects
info: |
1. If obj is not an Object, throw a TypeError exception.
2. Let rawSize be ? Get(obj, "size").
...
7. Let has be ? Get(obj, "has").
...
9. Let keys be ? Get(obj, "keys").
features: [set-methods]
includes: [compareArray.js]
---*/
const s1 = new Set([1, 2]);
const s2 = {
size: 2,
has: (v) => {
if (v === 1) return false;
if (v === 2) return true;
throw new Test262Error("Set.prototype.intersection should only call its argument's has method with contents of this");
},
keys: function* keys() {
throw new Test262Error("Set.prototype.intersection should not call its argument's keys iterator when this.size ≤ arg.size");
},
};
const expected = [2];
const combined = s1.intersection(s2);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");

View File

@ -0,0 +1,17 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.intersection
description: Set.prototype.intersection doesn't work with arrays
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = [3];
assert.throws(
TypeError,
function () {
s1.intersection(s2);
},
"Throws an error when an array is used"
);

View File

@ -0,0 +1,25 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.intersection
description: Tests that Set.prototype.intersection meets the requirements for built-in objects
features: [set-methods]
---*/
assert.sameValue(
Object.isExtensible(Set.prototype.intersection),
true,
"Built-in objects must be extensible."
);
assert.sameValue(
Object.prototype.toString.call(Set.prototype.intersection),
"[object Function]",
"Object.prototype.toString"
);
assert.sameValue(
Object.getPrototypeOf(Set.prototype.intersection),
Function.prototype,
"prototype"
);

View File

@ -0,0 +1,66 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-getsetrecord
description: GetSetRecord throws if obj is not an object
info: |
1. If obj is not an Object, throw a TypeError exception.
features: [set-methods]
---*/
let s1 = new Set([1]);
assert.throws(
TypeError,
function () {
s1.intersection(1);
},
"number"
);
assert.throws(
TypeError,
function () {
s1.intersection("");
},
"string"
);
assert.throws(
TypeError,
function () {
s1.intersection(1n);
},
"bigint"
);
assert.throws(
TypeError,
function () {
s1.intersection(false);
},
"boolean"
);
assert.throws(
TypeError,
function () {
s1.intersection(undefined);
},
"undefined"
);
assert.throws(
TypeError,
function () {
s1.intersection(null);
},
"null"
);
assert.throws(
TypeError,
function () {
s1.intersection(Symbol("test"));
},
"symbol"
);

View File

@ -0,0 +1,19 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.intersection
description: Set.prototype.intersection combines with Map
features: [set-methods]
includes: [compareArray.js]
---*/
const s1 = new Set([1, 2]);
const m1 = new Map([
[2, "two"],
[3, "three"],
]);
const expected = [2];
const combined = s1.intersection(m1);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");

View File

@ -0,0 +1,32 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.intersection
description: Set.prototype.intersection can combine empty Sets
features: [set-methods]
includes: [compareArray.js]
---*/
const s1 = new Set([]);
const s2 = new Set([1, 2]);
let expected = [];
let combined = s1.intersection(s2);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");
const s3 = new Set([1, 2]);
const s4 = new Set([]);
expected = [];
combined = s3.intersection(s4);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");
const s5 = new Set([]);
const s6 = new Set([]);
expected = [];
combined = s5.intersection(s6);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");

View File

@ -0,0 +1,16 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.intersection
description: Set.prototype.intersection is successful when called on itself
features: [set-methods]
includes: [compareArray.js]
---*/
const s1 = new Set([1, 2]);
const expected = [1, 2];
const combined = s1.intersection(s1);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");
assert.sameValue(combined === s1, false, "The returned object is a new object");

View File

@ -0,0 +1,18 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.intersection
description: Set.prototype.intersection can combine Sets that have the same content
features: [set-methods]
includes: [compareArray.js]
---*/
const s1 = new Set([1, 2]);
const s2 = new Set([1, 2]);
const expected = [1, 2];
const combined = s1.intersection(s2);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");
assert.sameValue(combined === s1, false, "The returned object is a new object");
assert.sameValue(combined === s2, false, "The returned object is a new object");

View File

@ -0,0 +1,16 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.intersection
description: Set.prototype.intersection combines Sets
features: [set-methods]
includes: [compareArray.js]
---*/
const s1 = new Set([1, 2]);
const s2 = new Set([2, 3]);
const expected = [2];
const combined = s1.intersection(s2);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");

View File

@ -0,0 +1,28 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.intersection
description: Set.prototype.intersection converts -0𝔽 to +0𝔽
info: |
7.c.ii.2 If nextValue is -0𝔽, set nextValue to +0𝔽.
features: [set-methods]
includes: [compareArray.js]
---*/
const setlikeWithMinusZero = {
size: 1,
has: function () {
throw new Test262Error("Set.prototype.intersection should not invoke .has on its argument when this.size > arg.size");
},
keys: function () {
// we use an array here because the Set constructor would normalize away -0
return [-0].values();
},
};
const s1 = new Set([0, 1, 2]);
let expected = [+0];
let combined = s1.intersection(setlikeWithMinusZero);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");

View File

@ -0,0 +1,36 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-getsetrecord
description: GetSetRecord throws an exception if the Set-like object's 'has' property is not callable
info: |
7. Let has be ? Get(obj, "has").
8. If IsCallable(has) is false, throw a TypeError exception.
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = {
size: 2,
has: undefined,
keys: function* keys() {
yield 2;
yield 3;
},
};
assert.throws(
TypeError,
function () {
s1.intersection(s2);
},
"GetSetRecord throws an error when has is undefined"
);
s2.has = {};
assert.throws(
TypeError,
function () {
s1.intersection(s2);
},
"GetSetRecord throws an error when has is not callable"
);

View File

@ -0,0 +1,20 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.intersection
description: Set.prototype.intersection properties
includes: [propertyHelper.js]
features: [set-methods]
---*/
assert.sameValue(
typeof Set.prototype.intersection,
"function",
"`typeof Set.prototype.intersection` is `'function'`"
);
verifyProperty(Set.prototype, "intersection", {
enumerable: false,
writable: true,
configurable: true,
});

View File

@ -0,0 +1,33 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-getsetrecord
description: GetSetRecord throws an exception if the Set-like object's 'keys' property is not callable
info: |
9. Let keys be ? Get(obj, "keys").
10. If IsCallable(keys) is false, throw a TypeError exception.
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = {
size: 2,
has: () => {},
keys: undefined,
};
assert.throws(
TypeError,
function () {
s1.intersection(s2);
},
"GetSetRecord throws an error when keys is undefined"
);
s2.keys = {};
assert.throws(
TypeError,
function () {
s1.intersection(s2);
},
"GetSetRecord throws an error when keys is not callable"
);

View File

@ -0,0 +1,18 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.intersection
description: Set.prototype.intersection length property
info: |
Set.prototype.intersection ( other )
includes: [propertyHelper.js]
features: [set-methods]
---*/
assert.sameValue(typeof Set.prototype.intersection, "function");
verifyProperty(Set.prototype.intersection, "length", {
enumerable: false,
writable: false,
configurable: true,
value: 1,
});

View File

@ -0,0 +1,18 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.intersection
description: Set.prototype.intersection name property
info: |
Set.prototype.intersection ( other )
includes: [propertyHelper.js]
features: [set-methods]
---*/
assert.sameValue(typeof Set.prototype.intersection, "function");
verifyProperty(Set.prototype.intersection, "name", {
enumerable: false,
writable: false,
configurable: true,
value: "intersection",
});

View File

@ -0,0 +1,22 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.intersection
description: Set.prototype.intersection does not implement [[Construct]], is not new-able
includes: [isConstructor.js]
features: [Reflect.construct, set-methods]
---*/
assert.sameValue(
isConstructor(Set.prototype.intersection),
false,
"isConstructor(Set.prototype.intersection) must return false"
);
assert.throws(
TypeError,
() => {
new Set.prototype.intersection();
},
"`new Set.prototype.intersection()` throws TypeError"
);

View File

@ -0,0 +1,44 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.intersection
description: Set.prototype.intersection throws when receiver is not a Set
features: [set-methods]
---*/
class MySetLike {
constructor() {
this.size = 2;
this.has = () => {};
this.keys = function* keys() {
yield 2;
yield 3;
};
}
}
const s1 = new MySetLike();
const s2 = new Set();
assert.throws(
TypeError,
() => {
Set.prototype.intersection.call(s1, s2);
},
"Set-like class"
);
const s3 = {
size: 2,
has: () => {},
keys: function* keys() {
yield 2;
yield 3;
},
};
assert.throws(
TypeError,
() => {
Set.prototype.intersection.call(s3, s2);
},
"Set-like object"
);

View File

@ -0,0 +1,27 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.intersection
description: Set.prototype.intersection RequireInternalSlot
info: |
2. Perform ? RequireInternalSlot(O, [[SetData]])
features: [set-methods]
---*/
const intersection = Set.prototype.intersection;
assert.sameValue(typeof intersection, "function");
const realSet = new Set([]);
assert.throws(TypeError, () => intersection.call(undefined, realSet), "undefined");
assert.throws(TypeError, () => intersection.call(null, realSet), "null");
assert.throws(TypeError, () => intersection.call(true, realSet), "true");
assert.throws(TypeError, () => intersection.call("", realSet), "empty string");
assert.throws(TypeError, () => intersection.call(Symbol(), realSet), "symbol");
assert.throws(TypeError, () => intersection.call(1, realSet), "1");
assert.throws(TypeError, () => intersection.call(1n, realSet), "1n");
assert.throws(TypeError, () => intersection.call({}, realSet), "plain object");
assert.throws(TypeError, () => intersection.call([], realSet), "array");
assert.throws(TypeError, () => intersection.call(new Map(), realSet), "map");
assert.throws(TypeError, () => intersection.call(Set.prototype, realSet), "Set.prototype");

View File

@ -0,0 +1,53 @@
// Copyright (C) 2023 Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.intersection
description: Set.prototype.intersection result ordering
features: [set-methods]
includes: [compareArray.js]
---*/
// when this.size ≤ other.size, results are ordered as in this
{
const s1 = new Set([1, 3, 5]);
const s2 = new Set([3, 2, 1]);
assert.compareArray([...s1.intersection(s2)], [1, 3]);
}
{
const s1 = new Set([3, 2, 1]);
const s2 = new Set([1, 3, 5]);
assert.compareArray([...s1.intersection(s2)], [3, 1]);
}
{
const s1 = new Set([1, 3, 5]);
const s2 = new Set([3, 2, 1, 0]);
assert.compareArray([...s1.intersection(s2)], [1, 3]);
}
{
const s1 = new Set([3, 2, 1]);
const s2 = new Set([1, 3, 5, 7]);
assert.compareArray([...s1.intersection(s2)], [3, 1]);
}
// when this.size > other.size, results are ordered as in other
{
const s1 = new Set([3, 2, 1, 0]);
const s2 = new Set([1, 3, 5]);
assert.compareArray([...s1.intersection(s2)], [1, 3]);
}
{
const s1 = new Set([1, 3, 5, 7]);
const s2 = new Set([3, 2, 1]);
assert.compareArray([...s1.intersection(s2)], [3, 1]);
}

View File

@ -0,0 +1,26 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.intersection
description: Set.prototype.intersection consumes a set-like array as a set-like, not an array
features: [set-methods]
includes: [compareArray.js]
---*/
const s1 = new Set([1, 2]);
const s2 = [5, 6];
s2.size = 3;
s2.has = function (v) {
if (v === 1) return false;
if (v === 2) return true;
throw new Test262Error("Set.prototype.intersection should only call its argument's has method with contents of this");
};
s2.keys = function () {
throw new Test262Error("Set.prototype.intersection should not call its argument's keys iterator when this.size ≤ arg.size");
};
const expected = [2];
const combined = s1.intersection(s2);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");

View File

@ -0,0 +1,49 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.intersection
description: Set.prototype.intersection maintains values even when a custom Set-like class mutates the receiver
features: [set-methods]
includes: [compareArray.js]
---*/
const baseSet = new Set(["a", "b", "c", "d", "e"]);
function mutatingIterator() {
let index = 0;
let values = ["x", "b", "b"];
return {
next() {
if (index === 0) {
baseSet.delete("b");
baseSet.delete("c");
baseSet.add("b");
baseSet.add("d");
}
return {
done: index >= values.length,
value: values[index++],
};
},
};
}
const evilSetLike = {
size: 3,
get has() {
baseSet.add("q");
return function () {
throw new Test262Error("Set.prototype.intersection should not invoke .has on its argument when this.size > other.size");
};
},
keys() {
return mutatingIterator();
},
};
const combined = baseSet.intersection(evilSetLike);
const expectedCombined = ["b"];
assert.compareArray([...combined], expectedCombined);
const expectedNewBase = ["a", "d", "e", "q", "b"];
assert.compareArray([...baseSet], expectedNewBase);

View File

@ -0,0 +1,140 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.intersection
description: Set.prototype.intersection calls a Set-like class's methods in order
features: [set-methods]
includes: [compareArray.js]
---*/
let observedOrder = [];
function observableIterator() {
let values = ["a", "b", "c"];
let index = 0;
return {
get next() {
observedOrder.push("getting next");
return function () {
observedOrder.push("calling next");
return {
get done() {
observedOrder.push("getting done");
return index >= values.length;
},
get value() {
observedOrder.push("getting value");
return values[index++];
},
};
};
},
};
}
class MySetLike {
get size() {
observedOrder.push("getting size");
return {
valueOf: function () {
observedOrder.push("ToNumber(size)");
return 3;
},
};
}
get has() {
observedOrder.push("getting has");
return function (v) {
observedOrder.push("calling has");
return ["a", "b", "c"].indexOf(v) !== -1;
};
}
get keys() {
observedOrder.push("getting keys");
return function () {
observedOrder.push("calling keys");
return observableIterator();
};
}
}
// this is smaller than argument
{
observedOrder = [];
const s1 = new Set(["a", "d"]);
const s2 = new MySetLike();
const combined = s1.intersection(s2);
const expectedOrder = [
"getting size",
"ToNumber(size)",
"getting has",
"getting keys",
// two calls to has
"calling has",
"calling has",
];
assert.compareArray([...combined], ["a"]);
assert.compareArray(observedOrder, expectedOrder);
}
// this is same size as argument
{
observedOrder = [];
const s1 = new Set(["a", "b", "d"]);
const s2 = new MySetLike();
const combined = s1.intersection(s2);
const expectedOrder = [
"getting size",
"ToNumber(size)",
"getting has",
"getting keys",
// three calls to has
"calling has",
"calling has",
"calling has",
];
assert.compareArray([...combined], ["a", "b"]);
assert.compareArray(observedOrder, expectedOrder);
}
// this is larger than argument
{
observedOrder = [];
const s1 = new Set(["a", "b", "c", "d"]);
const s2 = new MySetLike();
const combined = s1.intersection(s2);
const expectedOrder = [
"getting size",
"ToNumber(size)",
"getting has",
"getting keys",
"calling keys",
"getting next",
// first iteration, has value
"calling next",
"getting done",
"getting value",
// second iteration, has value
"calling next",
"getting done",
"getting value",
// third iteration, has value
"calling next",
"getting done",
"getting value",
// fourth iteration, no value; ends
"calling next",
"getting done",
];
assert.compareArray([...combined], ["a", "b", "c"]);
assert.compareArray(observedOrder, expectedOrder);
}

View File

@ -0,0 +1,72 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-getsetrecord
description: GetSetRecord throws an exception if the Set-like object has a size that is coerced to NaN
info: |
2. Let rawSize be ? Get(obj, "size").
3. Let numSize be ? ToNumber(rawSize).
4. NOTE: If rawSize is undefined, then numSize will be NaN.
5. If numSize is NaN, throw a TypeError exception.
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = {
size: undefined,
has: () => {},
keys: function* keys() {
yield 2;
yield 3;
},
};
assert.throws(
TypeError,
function () {
s1.intersection(s2);
},
"GetSetRecord throws an error when size is undefined"
);
s2.size = NaN;
assert.throws(
TypeError,
function () {
s1.intersection(s2);
},
"GetSetRecord throws an error when size is NaN"
);
let coercionCalls = 0;
s2.size = {
valueOf: function() {
++coercionCalls;
return NaN;
},
};
assert.throws(
TypeError,
function () {
s1.intersection(s2);
},
"GetSetRecord throws an error when size coerces to NaN"
);
assert.sameValue(coercionCalls, 1, "GetSetRecord coerces size");
s2.size = 0n;
assert.throws(
TypeError,
function () {
s1.intersection(s2);
},
"GetSetRecord throws an error when size is a BigInt"
);
s2.size = "string";
assert.throws(
TypeError,
function () {
s1.intersection(s2);
},
"GetSetRecord throws an error when size is a non-numeric string"
);

View File

@ -0,0 +1,45 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.intersection
description: Set.prototype.intersection works on subclasses of Set, but never calls the receiver's size/has/keys methods
features: [set-methods]
includes: [compareArray.js]
---*/
let sizeCount = 0;
let hasCount = 0;
let keysCount = 0;
class MySet extends Set {
size(...rest) {
sizeCount++;
return super.size(...rest);
}
has(...rest) {
hasCount++;
return super.has(...rest);
}
keys(...rest) {
keysCount++;
return super.keys(...rest);
}
}
const s1 = new MySet([1, 2]);
const s2 = new Set([2, 3]);
const expected = [2];
const combined = s1.intersection(s2);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");
assert.sameValue(
combined instanceof MySet,
false,
"The returned object is a Set, not a subclass"
);
assert.sameValue(sizeCount, 0, "size should not be called on the receiver");
assert.sameValue(hasCount, 0, "has should not be called on the receiver");
assert.sameValue(keysCount, 0, "keys should not be called on the receiver");

View File

@ -0,0 +1,29 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.intersection
description: Set.prototype.intersection works on subclasses of Set, but returns an instance of Set even when Symbol.species is overridden.
features: [set-methods]
includes: [compareArray.js]
---*/
var count = 0;
class MySet extends Set {
static get [Symbol.species]() {
count++;
return Set;
}
}
const s1 = new MySet([1, 2]);
const s2 = new Set([2, 3]);
const expected = [2];
const combined = s1.intersection(s2);
assert.compareArray([...combined], expected);
assert.sameValue(count, 0, "Symbol.species is never called");
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");
assert.sameValue(
combined instanceof MySet,
false,
"The returned object is a Set, not a subclass"
);

View File

@ -0,0 +1,23 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.intersection
description: Set.prototype.intersection works on subclasses of Set, but returns an instance of Set
features: [set-methods]
includes: [compareArray.js]
---*/
class MySet extends Set {}
const s1 = new MySet([1, 2]);
const s2 = new Set([2, 3]);
const expected = [2];
const combined = s1.intersection(s2);
assert.compareArray([...combined], expected);
assert.sameValue(combined instanceof Set, true, "The returned object is a Set");
assert.sameValue(
combined instanceof MySet,
false,
"The returned object is a Set, not a subclass"
);

View File

@ -0,0 +1,30 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.isdisjointfrom
description: GetSetRecord allows instances of Set-like classes
info: |
1. If obj is not an Object, throw a TypeError exception.
2. Let rawSize be ? Get(obj, "size").
...
7. Let has be ? Get(obj, "has").
...
9. Let keys be ? Get(obj, "keys").
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = new class {
get size() {
return 2;
}
has(v) {
if (v === 1 || v === 2) return false;
throw new Test262Error("Set.prototype.isDisjointFrom should only call its argument's has method with contents of this");
}
* keys() {
throw new Test262Error("Set.prototype.isDisjointFrom should not call its argument's keys iterator when this.size ≤ arg.size");
}
};
assert.sameValue(s1.isDisjointFrom(s2), true);

View File

@ -0,0 +1,28 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.isdisjointfrom
description: GetSetRecord allows Set-like objects
info: |
1. If obj is not an Object, throw a TypeError exception.
2. Let rawSize be ? Get(obj, "size").
...
7. Let has be ? Get(obj, "has").
...
9. Let keys be ? Get(obj, "keys").
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = {
size: 2,
has: (v) => {
if (v === 1 || v === 2) return false;
throw new Test262Error("Set.prototype.isDisjointFrom should only call its argument's has method with contents of this");
},
keys: function* keys() {
throw new Test262Error("Set.prototype.isDisjointFrom should not call its argument's keys iterator when this.size ≤ arg.size");
},
};
assert.sameValue(s1.isDisjointFrom(s2), true);

View File

@ -0,0 +1,17 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.isdisjointfrom
description: Set.prototype.isDisjointFrom doesn't work with arrays
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = [3];
assert.throws(
TypeError,
function () {
s1.isDisjointFrom(s2);
},
"Throws an error when an array is used"
);

View File

@ -0,0 +1,25 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.isdisjointfrom
description: Tests that Set.prototype.isDisjointFrom meets the requirements for built-in objects
features: [set-methods]
---*/
assert.sameValue(
Object.isExtensible(Set.prototype.isDisjointFrom),
true,
"Built-in objects must be extensible."
);
assert.sameValue(
Object.prototype.toString.call(Set.prototype.isDisjointFrom),
"[object Function]",
"Object.prototype.toString"
);
assert.sameValue(
Object.getPrototypeOf(Set.prototype.isDisjointFrom),
Function.prototype,
"prototype"
);

View File

@ -0,0 +1,66 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-getsetrecord
description: GetSetRecord throws if obj is not an object
info: |
1. If obj is not an Object, throw a TypeError exception.
features: [set-methods]
---*/
let s1 = new Set([1]);
assert.throws(
TypeError,
function () {
s1.isDisjointFrom(1);
},
"number"
);
assert.throws(
TypeError,
function () {
s1.isDisjointFrom("");
},
"string"
);
assert.throws(
TypeError,
function () {
s1.isDisjointFrom(1n);
},
"bigint"
);
assert.throws(
TypeError,
function () {
s1.isDisjointFrom(false);
},
"boolean"
);
assert.throws(
TypeError,
function () {
s1.isDisjointFrom(undefined);
},
"undefined"
);
assert.throws(
TypeError,
function () {
s1.isDisjointFrom(null);
},
"null"
);
assert.throws(
TypeError,
function () {
s1.isDisjointFrom(Symbol("test"));
},
"symbol"
);

View File

@ -0,0 +1,15 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.isdisjointfrom
description: Set.prototype.isDisjointFrom compares with Map
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const m1 = new Map([
[2, "two"],
[3, "three"],
]);
assert.sameValue(s1.isDisjointFrom(m1), false);

View File

@ -0,0 +1,22 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.isdisjointfrom
description: Set.prototype.isDisjointFrom can compare empty Sets
features: [set-methods]
---*/
const s1 = new Set([]);
const s2 = new Set([1, 2]);
assert.sameValue(s1.isDisjointFrom(s2), true);
const s3 = new Set([1, 2]);
const s4 = new Set([]);
assert.sameValue(s3.isDisjointFrom(s4), true);
const s5 = new Set([]);
const s6 = new Set([]);
assert.sameValue(s5.isDisjointFrom(s6), true);

View File

@ -0,0 +1,11 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.isdisjointfrom
description: Set.prototype.isDisjointFrom is successful when called on itself
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
assert.sameValue(s1.isDisjointFrom(s1), false);

View File

@ -0,0 +1,12 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.isdisjointfrom
description: Set.prototype.isDisjointFrom can compare Sets that have the same content
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = new Set([1, 2]);
assert.sameValue(s1.isDisjointFrom(s2), false);

View File

@ -0,0 +1,16 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.isdisjointfrom
description: Set.prototype.isDisjointFrom compares Sets
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = new Set([2, 3]);
assert.sameValue(s1.isDisjointFrom(s2), false);
const s3 = new Set([3]);
assert.sameValue(s1.isDisjointFrom(s3), true);

View File

@ -0,0 +1,22 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.isdisjointfrom
description: Set.prototype.isDisjointFrom converts -0𝔽 to +0𝔽
features: [set-methods]
---*/
const setlikeWithMinusZero = {
size: 1,
has: function () {
throw new Test262Error("Set.prototype.isDisjointFrom should not call its argument's has method when this.size > arg.size");
},
keys: function () {
// we use an array here because the Set constructor would normalize away -0
return [-0].values();
},
};
const s1 = new Set([+0, 1]);
assert.sameValue(s1.isDisjointFrom(setlikeWithMinusZero), false);

View File

@ -0,0 +1,36 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-getsetrecord
description: GetSetRecord throws an exception if the Set-like object's 'has' property is not callable
info: |
7. Let has be ? Get(obj, "has").
8. If IsCallable(has) is false, throw a TypeError exception.
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = {
size: 2,
has: undefined,
keys: function* keys() {
yield 2;
yield 3;
},
};
assert.throws(
TypeError,
function () {
s1.isDisjointFrom(s2);
},
"GetSetRecord throws an error when has is undefined"
);
s2.has = {};
assert.throws(
TypeError,
function () {
s1.isDisjointFrom(s2);
},
"GetSetRecord throws an error when has is not callable"
);

View File

@ -0,0 +1,20 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.isdisjointfrom
description: Set.prototype.isDisjointFrom properties
includes: [propertyHelper.js]
features: [set-methods]
---*/
assert.sameValue(
typeof Set.prototype.isDisjointFrom,
"function",
"`typeof Set.prototype.isDisjointFrom` is `'function'`"
);
verifyProperty(Set.prototype, "isDisjointFrom", {
enumerable: false,
writable: true,
configurable: true,
});

View File

@ -0,0 +1,33 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-getsetrecord
description: GetSetRecord throws an exception if the Set-like object's 'keys' property is not callable
info: |
9. Let keys be ? Get(obj, "keys").
10. If IsCallable(keys) is false, throw a TypeError exception.
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = {
size: 2,
has: () => {},
keys: undefined,
};
assert.throws(
TypeError,
function () {
s1.isDisjointFrom(s2);
},
"GetSetRecord throws an error when keys is undefined"
);
s2.keys = {};
assert.throws(
TypeError,
function () {
s1.isDisjointFrom(s2);
},
"GetSetRecord throws an error when keys is not callable"
);

View File

@ -0,0 +1,18 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.isdisjointfrom
description: Set.prototype.isDisjointFrom length property
info: |
Set.prototype.isDisjointFrom ( other )
includes: [propertyHelper.js]
features: [set-methods]
---*/
assert.sameValue(typeof Set.prototype.isDisjointFrom, "function");
verifyProperty(Set.prototype.isDisjointFrom, "length", {
enumerable: false,
writable: false,
configurable: true,
value: 1,
});

View File

@ -0,0 +1,18 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.isdisjointfrom
description: Set.prototype.isDisjointFrom name property
info: |
Set.prototype.isDisjointFrom ( other )
includes: [propertyHelper.js]
features: [set-methods]
---*/
assert.sameValue(typeof Set.prototype.isDisjointFrom, "function");
verifyProperty(Set.prototype.isDisjointFrom, "name", {
enumerable: false,
writable: false,
configurable: true,
value: "isDisjointFrom",
});

View File

@ -0,0 +1,22 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.isdisjointfrom
description: Set.prototype.isDisjointFrom does not implement [[Construct]], is not new-able
includes: [isConstructor.js]
features: [Reflect.construct, set-methods]
---*/
assert.sameValue(
isConstructor(Set.prototype.isDisjointFrom),
false,
"isConstructor(Set.prototype.isDisjointFrom) must return false"
);
assert.throws(
TypeError,
() => {
new Set.prototype.isDisjointFrom();
},
"`new Set.prototype.isDisjointFrom()` throws TypeError"
);

View File

@ -0,0 +1,44 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.isdisjointfrom
description: Set.prototype.isDisjointFrom throws when receiver is not a Set
features: [set-methods]
---*/
class MySetLike {
constructor() {
this.size = 2;
this.has = () => {};
this.keys = function* keys() {
yield 2;
yield 3;
};
}
}
const s1 = new MySetLike();
const s2 = new Set();
assert.throws(
TypeError,
() => {
Set.prototype.isDisjointFrom.call(s1, s2);
},
"Set-like class"
);
const s3 = {
size: 2,
has: () => {},
keys: function* keys() {
yield 2;
yield 3;
},
};
assert.throws(
TypeError,
() => {
Set.prototype.isDisjointFrom.call(s3, s2);
},
"Set-like object"
);

View File

@ -0,0 +1,27 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.isdisjointfrom
description: Set.prototype.isDisjointFrom RequireInternalSlot
info: |
2. Perform ? RequireInternalSlot(O, [[SetData]])
features: [set-methods]
---*/
const isDisjointFrom = Set.prototype.isDisjointFrom;
assert.sameValue(typeof isDisjointFrom, "function");
const realSet = new Set([]);
assert.throws(TypeError, () => isDisjointFrom.call(undefined, realSet), "undefined");
assert.throws(TypeError, () => isDisjointFrom.call(null, realSet), "null");
assert.throws(TypeError, () => isDisjointFrom.call(true, realSet), "true");
assert.throws(TypeError, () => isDisjointFrom.call("", realSet), "empty string");
assert.throws(TypeError, () => isDisjointFrom.call(Symbol(), realSet), "symbol");
assert.throws(TypeError, () => isDisjointFrom.call(1, realSet), "1");
assert.throws(TypeError, () => isDisjointFrom.call(1n, realSet), "1n");
assert.throws(TypeError, () => isDisjointFrom.call({}, realSet), "plain object");
assert.throws(TypeError, () => isDisjointFrom.call([], realSet), "array");
assert.throws(TypeError, () => isDisjointFrom.call(new Map(), realSet), "map");
assert.throws(TypeError, () => isDisjointFrom.call(Set.prototype, realSet), "Set.prototype");

View File

@ -0,0 +1,20 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.isdisjointfrom
description: Set.prototype.isDisjointFrom consumes a set-like array as a set-like, not an array
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = [5, 6];
s2.size = 3;
s2.has = function (v) {
if (v === 1 || v === 2) return true;
throw new Test262Error("Set.prototype.isDisjointFrom should only call its argument's has method with contents of this");
};
s2.keys = function () {
throw new Test262Error("Set.prototype.isDisjointFrom should not call its argument's keys iterator when this.size ≤ arg.size");
};
assert.sameValue(s1.isDisjointFrom(s2), false);

View File

@ -0,0 +1,38 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.isdisjointfrom
description: Set.prototype.isDisjointFrom behavior when a custom Set-like class mutates the receiver
features: [set-methods]
includes: [compareArray.js]
---*/
const baseSet = new Set(["a", "b", "c"]);
const evilSetLike = {
size: 3,
has(v) {
if (v === "a") {
baseSet.delete("b");
baseSet.delete("c");
baseSet.add("b");
return false;
}
if (v === "b") {
return false;
}
if (v === "c") {
throw new Test262Error("Set.prototype.isDisjointFrom should not call its argument's has method with values from this which have been deleted before visiting");
}
throw new Test262Error("Set.prototype.isDisjointFrom should only call its argument's has method with contents of this");
},
* keys() {
throw new Test262Error("Set.prototype.isDisjointFrom should not call its argument's keys iterator when this.size ≤ arg.size");
},
};
const result = baseSet.isDisjointFrom(evilSetLike);
assert.sameValue(result, true);
const expectedNewBase = ["a", "b"];
assert.compareArray([...baseSet], expectedNewBase);

View File

@ -0,0 +1,191 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.isdisjointfrom
description: Set.prototype.isDisjointFrom calls a Set-like class's methods in order
features: [set-methods]
includes: [compareArray.js]
---*/
let observedOrder = [];
function observableIterator() {
let values = ["a", "b", "c"];
let index = 0;
return {
get next() {
observedOrder.push("getting next");
return function () {
observedOrder.push("calling next");
return {
get done() {
observedOrder.push("getting done");
return index >= values.length;
},
get value() {
observedOrder.push("getting value");
return values[index++];
},
};
};
},
};
}
class MySetLike {
get size() {
observedOrder.push("getting size");
return {
valueOf: function () {
observedOrder.push("ToNumber(size)");
return 3;
},
};
}
get has() {
observedOrder.push("getting has");
return function (v) {
observedOrder.push("calling has");
return ["a", "b", "c"].indexOf(v) !== -1;
};
}
get keys() {
observedOrder.push("getting keys");
return function () {
observedOrder.push("calling keys");
return observableIterator();
};
}
}
// this is smaller than argument
{
observedOrder = [];
const s1 = new Set(["x", "b"]);
const s2 = new MySetLike();
const result = s1.isDisjointFrom(s2);
const expectedOrder = [
"getting size",
"ToNumber(size)",
"getting has",
"getting keys",
// two calls to has
"calling has",
"calling has",
];
assert.sameValue(result, false);
assert.compareArray(observedOrder, expectedOrder);
}
// this is same size as argument - stops eagerly
{
observedOrder = [];
const s1 = new Set(["x", "b", "y"]);
const s2 = new MySetLike();
const result = s1.isDisjointFrom(s2);
const expectedOrder = [
"getting size",
"ToNumber(size)",
"getting has",
"getting keys",
// two calls to has
"calling has",
"calling has",
];
assert.sameValue(result, false);
assert.compareArray(observedOrder, expectedOrder);
}
// this is same size as argument - full run
{
observedOrder = [];
const s1 = new Set(["x", "y", "z"]);
const s2 = new MySetLike();
const result = s1.isDisjointFrom(s2);
const expectedOrder = [
"getting size",
"ToNumber(size)",
"getting has",
"getting keys",
// three calls to has
"calling has",
"calling has",
"calling has",
];
assert.sameValue(result, true);
assert.compareArray(observedOrder, expectedOrder);
}
// this is larger than argument - stops eagerly
{
observedOrder = [];
const s1 = new Set(["x", "b", "y", "z"]);
const s2 = new MySetLike();
const result = s1.isDisjointFrom(s2);
const expectedOrder = [
"getting size",
"ToNumber(size)",
"getting has",
"getting keys",
"calling keys",
"getting next",
// first iteration, has value
"calling next",
"getting done",
"getting value",
// second iteration, has value
"calling next",
"getting done",
"getting value",
];
assert.sameValue(result, false);
assert.compareArray(observedOrder, expectedOrder);
}
// this is larger than argument - full run
{
observedOrder = [];
const s1 = new Set(["x", "y", "z", "w"]);
const s2 = new MySetLike();
const result = s1.isDisjointFrom(s2);
const expectedOrder = [
"getting size",
"ToNumber(size)",
"getting has",
"getting keys",
"calling keys",
"getting next",
// first iteration, has value
"calling next",
"getting done",
"getting value",
// second iteration, has value
"calling next",
"getting done",
"getting value",
// third iteration, has value
"calling next",
"getting done",
"getting value",
// fourth iteration, done
"calling next",
"getting done",
];
assert.sameValue(result, true);
assert.compareArray(observedOrder, expectedOrder);
}

View File

@ -0,0 +1,72 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-getsetrecord
description: GetSetRecord throws an exception if the Set-like object has a size that is coerced to NaN
info: |
2. Let rawSize be ? Get(obj, "size").
3. Let numSize be ? ToNumber(rawSize).
4. NOTE: If rawSize is undefined, then numSize will be NaN.
5. If numSize is NaN, throw a TypeError exception.
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = {
size: undefined,
has: () => {},
keys: function* keys() {
yield 2;
yield 3;
},
};
assert.throws(
TypeError,
function () {
s1.isDisjointFrom(s2);
},
"GetSetRecord throws an error when size is undefined"
);
s2.size = NaN;
assert.throws(
TypeError,
function () {
s1.isDisjointFrom(s2);
},
"GetSetRecord throws an error when size is NaN"
);
let coercionCalls = 0;
s2.size = {
valueOf: function() {
++coercionCalls;
return NaN;
},
};
assert.throws(
TypeError,
function () {
s1.isDisjointFrom(s2);
},
"GetSetRecord throws an error when size coerces to NaN"
);
assert.sameValue(coercionCalls, 1, "GetSetRecord coerces size");
s2.size = 0n;
assert.throws(
TypeError,
function () {
s1.isDisjointFrom(s2);
},
"GetSetRecord throws an error when size is a BigInt"
);
s2.size = "string";
assert.throws(
TypeError,
function () {
s1.isDisjointFrom(s2);
},
"GetSetRecord throws an error when size is a non-numeric string"
);

View File

@ -0,0 +1,37 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.isdisjointfrom
description: Set.prototype.isDisjointFrom works on subclasses of Set, but never calls the receiver's size/has/keys methods
features: [set-methods]
---*/
let sizeCount = 0;
let hasCount = 0;
let keysCount = 0;
class MySet extends Set {
size(...rest) {
sizeCount++;
return super.size(...rest);
}
has(...rest) {
hasCount++;
return super.has(...rest);
}
keys(...rest) {
keysCount++;
return super.keys(...rest);
}
}
const s1 = new MySet([1, 2]);
const s2 = new Set([2, 3]);
const result = s1.isDisjointFrom(s2);
assert.sameValue(result, false);
assert.sameValue(sizeCount, 0, "size should not be called on the receiver");
assert.sameValue(hasCount, 0, "has should not be called on the receiver");
assert.sameValue(keysCount, 0, "keys should not be called on the receiver");

View File

@ -0,0 +1,31 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.issubsetof
description: GetSetRecord allows instances of Set-like classes
info: |
1. If obj is not an Object, throw a TypeError exception.
2. Let rawSize be ? Get(obj, "size").
...
7. Let has be ? Get(obj, "has").
...
9. Let keys be ? Get(obj, "keys").
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = new class {
get size() {
return 3;
}
has(v) {
if (v === 1) return false;
if (v === 2) return true;
throw new Test262Error("Set.prototype.isSubsetOf should only call its argument's has method with contents of this");
}
* keys() {
throw new Test262Error("Set.prototype.isSubsetOf should not call its argument's keys iterator");
}
};
assert.sameValue(s1.isSubsetOf(s2), false);

View File

@ -0,0 +1,29 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.issubsetof
description: GetSetRecord allows Set-like objects
info: |
1. If obj is not an Object, throw a TypeError exception.
2. Let rawSize be ? Get(obj, "size").
...
7. Let has be ? Get(obj, "has").
...
9. Let keys be ? Get(obj, "keys").
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = {
size: 2,
has: (v) => {
if (v === 1) return false;
if (v === 2) return true;
throw new Test262Error("Set.prototype.isSubsetOf should only call its argument's has method with contents of this");
},
keys: function* keys() {
throw new Test262Error("Set.prototype.isSubsetOf should not call its argument's keys iterator");
},
};
assert.sameValue(s1.isSubsetOf(s2), false);

View File

@ -0,0 +1,17 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.issubsetof
description: Set.prototype.isSubsetOf doesn't work with arrays
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = [3];
assert.throws(
TypeError,
function () {
s1.isSubsetOf(s2);
},
"Throws an error when an array is used"
);

View File

@ -0,0 +1,25 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.issubsetof
description: Tests that Set.prototype.isSubsetOf meets the requirements for built-in objects
features: [set-methods]
---*/
assert.sameValue(
Object.isExtensible(Set.prototype.isSubsetOf),
true,
"Built-in objects must be extensible."
);
assert.sameValue(
Object.prototype.toString.call(Set.prototype.isSubsetOf),
"[object Function]",
"Object.prototype.toString"
);
assert.sameValue(
Object.getPrototypeOf(Set.prototype.isSubsetOf),
Function.prototype,
"prototype"
);

View File

@ -0,0 +1,66 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-getsetrecord
description: GetSetRecord throws if obj is not an object
info: |
1. If obj is not an Object, throw a TypeError exception.
features: [set-methods]
---*/
let s1 = new Set([1]);
assert.throws(
TypeError,
function () {
s1.isSubsetOf(1);
},
"number"
);
assert.throws(
TypeError,
function () {
s1.isSubsetOf("");
},
"string"
);
assert.throws(
TypeError,
function () {
s1.isSubsetOf(1n);
},
"bigint"
);
assert.throws(
TypeError,
function () {
s1.isSubsetOf(false);
},
"boolean"
);
assert.throws(
TypeError,
function () {
s1.isSubsetOf(undefined);
},
"undefined"
);
assert.throws(
TypeError,
function () {
s1.isSubsetOf(null);
},
"null"
);
assert.throws(
TypeError,
function () {
s1.isSubsetOf(Symbol("test"));
},
"symbol"
);

View File

@ -0,0 +1,15 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.issubsetof
description: Set.prototype.isSubsetOf compares with Map
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const m1 = new Map([
[2, "two"],
[3, "three"],
]);
assert.sameValue(s1.isSubsetOf(m1), false);

View File

@ -0,0 +1,22 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.issubsetof
description: Set.prototype.isSubsetOf can compare empty Sets
features: [set-methods]
---*/
const s1 = new Set([]);
const s2 = new Set([1, 2]);
assert.sameValue(s1.isSubsetOf(s2), true);
const s3 = new Set([1, 2]);
const s4 = new Set([]);
assert.sameValue(s3.isSubsetOf(s4), false);
const s5 = new Set([]);
const s6 = new Set([]);
assert.sameValue(s5.isSubsetOf(s6), true);

View File

@ -0,0 +1,11 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.issubsetof
description: Set.prototype.isSubsetOf is successful when called on itself
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
assert.sameValue(s1.isSubsetOf(s1), true);

View File

@ -0,0 +1,12 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.issubsetof
description: Set.prototype.isSubsetOf can compare Sets that have the same content
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = new Set([1, 2]);
assert.sameValue(s1.isSubsetOf(s2), true);

View File

@ -0,0 +1,16 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.issubsetof
description: Set.prototype.isSubsetOf compares Sets
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = new Set([2, 3]);
assert.sameValue(s1.isSubsetOf(s2), false);
const s3 = new Set([1, 2, 3]);
assert.sameValue(s1.isSubsetOf(s3), true);

View File

@ -0,0 +1,36 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-getsetrecord
description: GetSetRecord throws an exception if the Set-like object's 'has' property is not callable
info: |
7. Let has be ? Get(obj, "has").
8. If IsCallable(has) is false, throw a TypeError exception.
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = {
size: 2,
has: undefined,
keys: function* keys() {
yield 2;
yield 3;
},
};
assert.throws(
TypeError,
function () {
s1.isSubsetOf(s2);
},
"GetSetRecord throws an error when has is undefined"
);
s2.has = {};
assert.throws(
TypeError,
function () {
s1.isSubsetOf(s2);
},
"GetSetRecord throws an error when has is not callable"
);

View File

@ -0,0 +1,20 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.issubsetof
description: Set.prototype.isSubsetOf properties
includes: [propertyHelper.js]
features: [set-methods]
---*/
assert.sameValue(
typeof Set.prototype.isSubsetOf,
"function",
"`typeof Set.prototype.isSubsetOf` is `'function'`"
);
verifyProperty(Set.prototype, "isSubsetOf", {
enumerable: false,
writable: true,
configurable: true,
});

View File

@ -0,0 +1,33 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-getsetrecord
description: GetSetRecord throws an exception if the Set-like object's 'keys' property is not callable
info: |
9. Let keys be ? Get(obj, "keys").
10. If IsCallable(keys) is false, throw a TypeError exception.
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = {
size: 2,
has: () => {},
keys: undefined,
};
assert.throws(
TypeError,
function () {
s1.isSubsetOf(s2);
},
"GetSetRecord throws an error when keys is undefined"
);
s2.keys = {};
assert.throws(
TypeError,
function () {
s1.isSubsetOf(s2);
},
"GetSetRecord throws an error when keys is not callable"
);

View File

@ -0,0 +1,18 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.issubsetof
description: Set.prototype.isSubsetOf length property
info: |
Set.prototype.isSubsetOf ( other )
includes: [propertyHelper.js]
features: [set-methods]
---*/
assert.sameValue(typeof Set.prototype.isSubsetOf, "function");
verifyProperty(Set.prototype.isSubsetOf, "length", {
enumerable: false,
writable: false,
configurable: true,
value: 1,
});

View File

@ -0,0 +1,18 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.issubsetof
description: Set.prototype.isSubsetOf name property
info: |
Set.prototype.isSubsetOf ( other )
includes: [propertyHelper.js]
features: [set-methods]
---*/
assert.sameValue(typeof Set.prototype.isSubsetOf, "function");
verifyProperty(Set.prototype.isSubsetOf, "name", {
enumerable: false,
writable: false,
configurable: true,
value: "isSubsetOf",
});

View File

@ -0,0 +1,22 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.issubsetof
description: Set.prototype.isSubsetOf does not implement [[Construct]], is not new-able
includes: [isConstructor.js]
features: [Reflect.construct, set-methods]
---*/
assert.sameValue(
isConstructor(Set.prototype.isSubsetOf),
false,
"isConstructor(Set.prototype.isSubsetOf) must return false"
);
assert.throws(
TypeError,
() => {
new Set.prototype.isSubsetOf();
},
"`new Set.prototype.isSubsetOf()` throws TypeError"
);

View File

@ -0,0 +1,44 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.issubsetof
description: Set.prototype.isSubsetOf throws when receiver is not a Set
features: [set-methods]
---*/
class MySetLike {
constructor() {
this.size = 2;
this.has = () => {};
this.keys = function* keys() {
yield 2;
yield 3;
};
}
}
const s1 = new MySetLike();
const s2 = new Set();
assert.throws(
TypeError,
() => {
Set.prototype.isSubsetOf.call(s1, s2);
},
"Set-like class"
);
const s3 = {
size: 2,
has: () => {},
keys: function* keys() {
yield 2;
yield 3;
},
};
assert.throws(
TypeError,
() => {
Set.prototype.isSubsetOf.call(s3, s2);
},
"Set-like object"
);

View File

@ -0,0 +1,27 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.issubsetof
description: Set.prototype.isSubsetOf RequireInternalSlot
info: |
2. Perform ? RequireInternalSlot(O, [[SetData]])
features: [set-methods]
---*/
const isSubsetOf = Set.prototype.isSubsetOf;
assert.sameValue(typeof isSubsetOf, "function");
const realSet = new Set([]);
assert.throws(TypeError, () => isSubsetOf.call(undefined, realSet), "undefined");
assert.throws(TypeError, () => isSubsetOf.call(null, realSet), "null");
assert.throws(TypeError, () => isSubsetOf.call(true, realSet), "true");
assert.throws(TypeError, () => isSubsetOf.call("", realSet), "empty string");
assert.throws(TypeError, () => isSubsetOf.call(Symbol(), realSet), "symbol");
assert.throws(TypeError, () => isSubsetOf.call(1, realSet), "1");
assert.throws(TypeError, () => isSubsetOf.call(1n, realSet), "1n");
assert.throws(TypeError, () => isSubsetOf.call({}, realSet), "plain object");
assert.throws(TypeError, () => isSubsetOf.call([], realSet), "array");
assert.throws(TypeError, () => isSubsetOf.call(new Map(), realSet), "map");
assert.throws(TypeError, () => isSubsetOf.call(Set.prototype, realSet), "Set.prototype");

View File

@ -0,0 +1,21 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.issubsetof
description: Set.prototype.isSubsetOf consumes a set-like array as a set-like, not an array
features: [set-methods]
---*/
const s1 = new Set([1, 2]);
const s2 = [5, 6];
s2.size = 3;
s2.has = function (v) {
if (v === 1) return true;
if (v === 2) return true;
throw new Test262Error("Set.prototype.isSubsetOf should only call its argument's has method with contents of this");
};
s2.keys = function () {
throw new Test262Error("Set.prototype.isSubsetOf should not call its argument's keys iterator when this.size ≤ arg.size");
};
assert.sameValue(s1.isSubsetOf(s2), true);

View File

@ -0,0 +1,29 @@
// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-set.prototype.issubsetof
description: Set.prototype.isSubsetOf behavior when a custom Set-like class mutates the receiver
features: [set-methods]
includes: [compareArray.js]
---*/
const baseSet = new Set(["a", "b", "c"]);
const evilSetLike = {
size: 3,
has(v) {
if (v === "a") {
baseSet.delete("c");
}
return ["x", "a", "b"].includes(v);
},
* keys() {
throw new Test262Error("Set.prototype.isSubsetOf should not call its argument's keys iterator");
},
};
const result = baseSet.isSubsetOf(evilSetLike);
assert.sameValue(result, true);
const expectedNewBase = ["a", "b"];
assert.compareArray([...baseSet], expectedNewBase);

Some files were not shown because too many files have changed in this diff Show More