summaryrefslogtreecommitdiffstats
path: root/js/src/tests/non262/Set
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /js/src/tests/non262/Set
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/tests/non262/Set')
-rw-r--r--js/src/tests/non262/Set/browser.js0
-rw-r--r--js/src/tests/non262/Set/difference.js462
-rw-r--r--js/src/tests/non262/Set/intersection.js478
-rw-r--r--js/src/tests/non262/Set/is-disjoint-from.js448
-rw-r--r--js/src/tests/non262/Set/is-subset-of.js333
-rw-r--r--js/src/tests/non262/Set/is-superset-of.js360
-rw-r--r--js/src/tests/non262/Set/shell.js102
-rw-r--r--js/src/tests/non262/Set/symmetric-difference.js303
-rw-r--r--js/src/tests/non262/Set/union.js300
9 files changed, 2786 insertions, 0 deletions
diff --git a/js/src/tests/non262/Set/browser.js b/js/src/tests/non262/Set/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/non262/Set/browser.js
diff --git a/js/src/tests/non262/Set/difference.js b/js/src/tests/non262/Set/difference.js
new file mode 100644
index 0000000000..2f42f14e96
--- /dev/null
+++ b/js/src/tests/non262/Set/difference.js
@@ -0,0 +1,462 @@
+// |reftest| shell-option(--enable-new-set-methods) skip-if(!Set.prototype.difference)
+
+assertEq(typeof Set.prototype.difference, "function");
+assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.difference, "length"), {
+ value: 1, writable: false, enumerable: false, configurable: true,
+});
+assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.difference, "name"), {
+ value: "difference", writable: false, enumerable: false, configurable: true,
+});
+
+const emptySet = new Set();
+const emptySetLike = new SetLike();
+const emptyMap = new Map();
+
+function asMap(values) {
+ return new Map(values.map(v => [v, v]));
+}
+
+// Difference of two empty sets is an empty set.
+assertSetContainsExactOrderedItems(emptySet.difference(emptySet), []);
+assertSetContainsExactOrderedItems(emptySet.difference(emptySetLike), []);
+assertSetContainsExactOrderedItems(emptySet.difference(emptyMap), []);
+
+// Test native Set, Set-like, and Map objects.
+for (let values of [
+ [], [1], [1, 2], [1, true, null, {}],
+]) {
+ // Difference with an empty set.
+ assertSetContainsExactOrderedItems(new Set(values).difference(emptySet), values);
+ assertSetContainsExactOrderedItems(new Set(values).difference(emptySetLike), values);
+ assertSetContainsExactOrderedItems(new Set(values).difference(emptyMap), values);
+ assertSetContainsExactOrderedItems(emptySet.difference(new Set(values)), []);
+ assertSetContainsExactOrderedItems(emptySet.difference(new SetLike(values)), []);
+ assertSetContainsExactOrderedItems(emptySet.difference(asMap(values)), []);
+
+ // Two sets with the exact same values.
+ assertSetContainsExactOrderedItems(new Set(values).difference(new Set(values)), []);
+ assertSetContainsExactOrderedItems(new Set(values).difference(new SetLike(values)), []);
+ assertSetContainsExactOrderedItems(new Set(values).difference(asMap(values)), []);
+
+ // Difference of the same set object.
+ let set = new Set(values);
+ assertSetContainsExactOrderedItems(set.difference(set), []);
+}
+
+// Check property accesses are in the correct order.
+{
+ let log = [];
+
+ let sizeValue = 0;
+
+ let {proxy: keysIter} = LoggingProxy({
+ next() {
+ log.push("next()");
+ return {done: true};
+ }
+ }, log);
+
+ let {proxy: setLike} = LoggingProxy({
+ size: {
+ valueOf() {
+ log.push("valueOf()");
+ return sizeValue;
+ }
+ },
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ keys() {
+ log.push("keys()");
+ return keysIter;
+ },
+ }, log);
+
+ log.length = 0;
+ assertSetContainsExactOrderedItems(emptySet.difference(setLike), []);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+
+ // Input has more elements than the this-value.
+ sizeValue = 1;
+
+ log.length = 0;
+ assertSetContainsExactOrderedItems(emptySet.difference(setLike), []);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+
+ // Input has fewer elements than the this-value.
+ sizeValue = 0;
+
+ log.length = 0;
+ assertSetContainsExactOrderedItems(new Set([1]).difference(setLike), [1]);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ "keys()",
+ "[[get]]", "next",
+ "next()",
+ ]);
+}
+
+// Check input validation.
+{
+ let log = [];
+
+ const nonCallable = {};
+ let sizeValue = 0;
+
+ let {proxy: keysIter} = LoggingProxy({
+ next: nonCallable,
+ }, log);
+
+ let {proxy: setLike, obj: setLikeObj} = LoggingProxy({
+ size: {
+ valueOf() {
+ log.push("valueOf()");
+ return sizeValue;
+ }
+ },
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ keys() {
+ log.push("keys()");
+ return keysIter;
+ },
+ }, log);
+
+ log.length = 0;
+ assertSetContainsExactOrderedItems(emptySet.difference(setLike), []);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => new Set([1]).difference(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ "keys()",
+ "[[get]]", "next",
+ ]);
+
+ // Change |keys| to return a non-object value.
+ setLikeObj.keys = () => 123;
+
+ log.length = 0;
+ assertSetContainsExactOrderedItems(emptySet.difference(setLike), []);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => new Set([1]).difference(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+
+ // Change |keys| to a non-callable value.
+ setLikeObj.keys = nonCallable;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.difference(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+
+ // Change |has| to a non-callable value.
+ setLikeObj.has = nonCallable;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.difference(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ ]);
+
+ // Change |size| to NaN.
+ sizeValue = NaN;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.difference(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ ]);
+
+ // Change |size| to undefined.
+ sizeValue = undefined;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.difference(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ ]);
+}
+
+// Doesn't accept Array as an input.
+assertThrowsInstanceOf(() => emptySet.difference([]), TypeError);
+
+// Works with Set subclasses.
+{
+ class MySet extends Set {}
+
+ let myEmptySet = new MySet;
+
+ assertSetContainsExactOrderedItems(emptySet.difference(myEmptySet), []);
+ assertSetContainsExactOrderedItems(myEmptySet.difference(myEmptySet), []);
+ assertSetContainsExactOrderedItems(myEmptySet.difference(emptySet), []);
+}
+
+// Doesn't access any properties on the this-value.
+{
+ let log = [];
+
+ let {proxy: setProto} = LoggingProxy(Set.prototype, log);
+
+ let set = new Set([1, 2, 3]);
+ Object.setPrototypeOf(set, setProto);
+
+ assertSetContainsExactOrderedItems(Set.prototype.difference.call(set, emptySet), [1, 2, 3]);
+
+ assertEqArray(log, []);
+}
+
+// Throws a TypeError when the this-value isn't a Set object.
+for (let thisValue of [
+ null, undefined, true, "", {}, new Map, new Proxy(new Set, {}),
+]) {
+ assertThrowsInstanceOf(() => Set.prototype.difference.call(thisValue, emptySet), TypeError);
+}
+
+// Doesn't return the original Set object.
+{
+ let set = new Set([1]);
+ assertEq(set.difference(emptySet) !== set, true);
+ assertEq(set.difference(new Set([2])) !== set, true);
+}
+
+// Test insertion order
+{
+ let set = new Set([1, 2, 3]);
+
+ // Case 1: Input is empty.
+ assertSetContainsExactOrderedItems(set.difference(new Set([])), [1, 2, 3]);
+
+ // Case 2: Input has fewer elements.
+ assertSetContainsExactOrderedItems(set.difference(new Set([1, 2])), [3]);
+ assertSetContainsExactOrderedItems(set.difference(new Set([2, 1])), [3]);
+ assertSetContainsExactOrderedItems(set.difference(new Set([11, 2])), [1, 3]);
+ assertSetContainsExactOrderedItems(set.difference(new Set([2, 11])), [1, 3]);
+
+ // Case 3: Input has same number of elements.
+ assertSetContainsExactOrderedItems(set.difference(new Set([1, 2, 3])), []);
+ assertSetContainsExactOrderedItems(set.difference(new Set([2, 3, 1])), []);
+ assertSetContainsExactOrderedItems(set.difference(new Set([3, 2, 1])), []);
+ assertSetContainsExactOrderedItems(set.difference(new Set([11, 2, 3])), [1]);
+ assertSetContainsExactOrderedItems(set.difference(new Set([2, 3, 11])), [1]);
+ assertSetContainsExactOrderedItems(set.difference(new Set([3, 2, 11])), [1]);
+
+ // Case 4: Input has more elements.
+ assertSetContainsExactOrderedItems(set.difference(new Set([2, 3, 4, 5])), [1]);
+ assertSetContainsExactOrderedItems(set.difference(new Set([4, 5, 2, 3])), [1]);
+ assertSetContainsExactOrderedItems(set.difference(new Set([5, 4, 3, 2])), [1]);
+}
+
+// Calls |has| when the this-value has fewer or the same number of keys.
+{
+ const keys = [1, 2, 3];
+
+ for (let size of [keys.length, 100, Infinity]) {
+ let i = 0;
+
+ let setLike = {
+ size,
+ has(v) {
+ assertEq(this, setLike);
+ assertEq(arguments.length, 1);
+ assertEq(i < keys.length, true);
+ assertEq(v, keys[i++]);
+ return true;
+ },
+ keys() {
+ throw new Error("Unexpected call to |keys| method");
+ },
+ };
+
+ assertSetContainsExactOrderedItems(new Set(keys).difference(setLike), []);
+ }
+}
+
+// Calls |keys| when the this-value has more keys.
+{
+ const keys = [1, 2, 3];
+
+ for (let size of [0, 1, 2]) {
+ let i = 0;
+
+ let setLike = {
+ size,
+ has() {
+ throw new Error("Unexpected call to |keys| method");
+ },
+ keys() {
+ assertEq(this, setLike);
+ assertEq(arguments.length, 0);
+
+ let iterator = {
+ next() {
+ assertEq(this, iterator);
+ assertEq(arguments.length, 0);
+ if (i < keys.length) {
+ return {
+ done: false,
+ value: keys[i++],
+ };
+ }
+ return {
+ done: true,
+ get value() {
+ throw new Error("Unexpected call to |value| getter");
+ },
+ };
+ }
+ };
+
+ return iterator;
+ },
+ };
+
+ assertSetContainsExactOrderedItems(new Set(keys).difference(setLike), []);
+ }
+}
+
+// Test result set order when the this-value was modified.
+{
+ let originalKeys = null;
+
+ let setLike = {
+ size: 100,
+ has(v) {
+ if (!originalKeys) {
+ assertSetContainsExactOrderedItems(set, [1, 2, 3, 4]);
+
+ originalKeys = [...set.keys()];
+
+ // Remove all existing items.
+ set.clear();
+
+ // Add new keys 11 and 22.
+ set.add(11);
+ set.add(22);
+ }
+
+ // |has| is called exactly once for each key.
+ assertEq(originalKeys.includes(v), true);
+
+ originalKeys.splice(originalKeys.indexOf(v), 1);
+
+ return true;
+ },
+ keys() {
+ throw new Error("Unexpected call to |keys| method");
+ },
+ };
+
+ let set = new Set([1, 2, 3, 4]);
+
+ assertSetContainsExactOrderedItems(set.difference(setLike), []);
+ assertSetContainsExactOrderedItems(set, [11, 22]);
+ assertEqArray(originalKeys, []);
+}
+{
+ let setLike = {
+ size: 0,
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ *keys() {
+ assertSetContainsExactOrderedItems(set, [1, 2, 3, 4]);
+
+ let originalKeys = [...set.keys()];
+
+ // Remove all existing items.
+ set.clear();
+
+ // Add new keys 11 and 22.
+ set.add(11);
+ set.add(22);
+
+ // Yield the original keys of |set|.
+ yield* originalKeys;
+ },
+ };
+
+ let set = new Set([1, 2, 3, 4]);
+
+ assertSetContainsExactOrderedItems(set.difference(setLike), []);
+ assertSetContainsExactOrderedItems(set, [11, 22]);
+}
+
+// Tests which modify any built-ins should appear last, because modifications may disable
+// optimised code paths.
+
+// Doesn't call the built-in |Set.prototype.{has, keys, size}| functions.
+const SetPrototypeHas = Object.getOwnPropertyDescriptor(Set.prototype, "has");
+const SetPrototypeKeys = Object.getOwnPropertyDescriptor(Set.prototype, "keys");
+const SetPrototypeSize = Object.getOwnPropertyDescriptor(Set.prototype, "size");
+
+delete Set.prototype.has;
+delete Set.prototype.keys;
+delete Set.prototype.size;
+
+try {
+ let set = new Set([1, 2, 3]);
+ let other = new SetLike([1, 2, 3]);
+ assertSetContainsExactOrderedItems(set.difference(other), []);
+} finally {
+ Object.defineProperty(Set.prototype, "has", SetPrototypeHas);
+ Object.defineProperty(Set.prototype, "keys", SetPrototypeKeys);
+ Object.defineProperty(Set.prototype, "size", SetPrototypeSize);
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Set/intersection.js b/js/src/tests/non262/Set/intersection.js
new file mode 100644
index 0000000000..dd0411c5a4
--- /dev/null
+++ b/js/src/tests/non262/Set/intersection.js
@@ -0,0 +1,478 @@
+// |reftest| shell-option(--enable-new-set-methods) skip-if(!Set.prototype.intersection)
+
+assertEq(typeof Set.prototype.intersection, "function");
+assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.intersection, "length"), {
+ value: 1, writable: false, enumerable: false, configurable: true,
+});
+assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.intersection, "name"), {
+ value: "intersection", writable: false, enumerable: false, configurable: true,
+});
+
+const emptySet = new Set();
+const emptySetLike = new SetLike();
+const emptyMap = new Map();
+
+function asMap(values) {
+ return new Map(values.map(v => [v, v]));
+}
+
+// Intersection of two empty sets is an empty set.
+assertSetContainsExactOrderedItems(emptySet.intersection(emptySet), []);
+assertSetContainsExactOrderedItems(emptySet.intersection(emptySetLike), []);
+assertSetContainsExactOrderedItems(emptySet.intersection(emptyMap), []);
+
+// Test native Set, Set-like, and Map objects.
+for (let values of [
+ [], [1], [1, 2], [1, true, null, {}],
+]) {
+ // Intersection with an empty set.
+ assertSetContainsExactOrderedItems(new Set(values).intersection(emptySet), []);
+ assertSetContainsExactOrderedItems(new Set(values).intersection(emptySetLike), []);
+ assertSetContainsExactOrderedItems(new Set(values).intersection(emptyMap), []);
+ assertSetContainsExactOrderedItems(emptySet.intersection(new Set(values)), []);
+ assertSetContainsExactOrderedItems(emptySet.intersection(new SetLike(values)), []);
+ assertSetContainsExactOrderedItems(emptySet.intersection(asMap(values)), []);
+
+ // Two sets with the exact same values.
+ assertSetContainsExactOrderedItems(new Set(values).intersection(new Set(values)), values);
+ assertSetContainsExactOrderedItems(new Set(values).intersection(new SetLike(values)), values);
+ assertSetContainsExactOrderedItems(new Set(values).intersection(asMap(values)), values);
+
+ // Intersection of the same set object.
+ let set = new Set(values);
+ assertSetContainsExactOrderedItems(set.intersection(set), values);
+}
+
+// Check property accesses are in the correct order.
+{
+ let log = [];
+
+ let sizeValue = 0;
+
+ let {proxy: keysIter} = LoggingProxy({
+ next() {
+ log.push("next()");
+ return {done: true};
+ }
+ }, log);
+
+ let {proxy: setLike} = LoggingProxy({
+ size: {
+ valueOf() {
+ log.push("valueOf()");
+ return sizeValue;
+ }
+ },
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ keys() {
+ log.push("keys()");
+ return keysIter;
+ },
+ }, log);
+
+ log.length = 0;
+ assertSetContainsExactOrderedItems(emptySet.intersection(setLike), []);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+
+ // Input has more elements than the this-value.
+ sizeValue = 1;
+
+ log.length = 0;
+ assertSetContainsExactOrderedItems(emptySet.intersection(setLike), []);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+
+ // Input has fewer elements than the this-value.
+ sizeValue = 0;
+
+ log.length = 0;
+ assertSetContainsExactOrderedItems(new Set([1]).intersection(setLike), []);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ "keys()",
+ "[[get]]", "next",
+ "next()",
+ ]);
+}
+
+// Check input validation.
+{
+ let log = [];
+
+ const nonCallable = {};
+ let sizeValue = 0;
+
+ let {proxy: keysIter} = LoggingProxy({
+ next: nonCallable,
+ }, log);
+
+ let {proxy: setLike, obj: setLikeObj} = LoggingProxy({
+ size: {
+ valueOf() {
+ log.push("valueOf()");
+ return sizeValue;
+ }
+ },
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ keys() {
+ log.push("keys()");
+ return keysIter;
+ },
+ }, log);
+
+ log.length = 0;
+ assertSetContainsExactOrderedItems(emptySet.intersection(setLike), []);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => new Set([1]).intersection(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ "keys()",
+ "[[get]]", "next",
+ ]);
+
+ // Change |keys| to return a non-object value.
+ setLikeObj.keys = () => 123;
+
+ log.length = 0;
+ assertSetContainsExactOrderedItems(emptySet.intersection(setLike), []);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => new Set([1]).intersection(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+
+ // Change |keys| to a non-callable value.
+ setLikeObj.keys = nonCallable;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.intersection(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+
+ // Change |has| to a non-callable value.
+ setLikeObj.has = nonCallable;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.intersection(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ ]);
+
+ // Change |size| to NaN.
+ sizeValue = NaN;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.intersection(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ ]);
+
+ // Change |size| to undefined.
+ sizeValue = undefined;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.intersection(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ ]);
+}
+
+// Doesn't accept Array as an input.
+assertThrowsInstanceOf(() => emptySet.intersection([]), TypeError);
+
+// Works with Set subclasses.
+{
+ class MySet extends Set {}
+
+ let myEmptySet = new MySet;
+
+ assertSetContainsExactOrderedItems(emptySet.intersection(myEmptySet), []);
+ assertSetContainsExactOrderedItems(myEmptySet.intersection(myEmptySet), []);
+ assertSetContainsExactOrderedItems(myEmptySet.intersection(emptySet), []);
+}
+
+// Doesn't access any properties on the this-value.
+{
+ let log = [];
+
+ let {proxy: setProto} = LoggingProxy(Set.prototype, log);
+
+ let set = new Set([1, 2, 3]);
+ Object.setPrototypeOf(set, setProto);
+
+ assertSetContainsExactOrderedItems(Set.prototype.intersection.call(set, emptySet), []);
+
+ assertEqArray(log, []);
+}
+
+// Throws a TypeError when the this-value isn't a Set object.
+for (let thisValue of [
+ null, undefined, true, "", {}, new Map, new Proxy(new Set, {}),
+]) {
+ assertThrowsInstanceOf(() => Set.prototype.intersection.call(thisValue, emptySet), TypeError);
+}
+
+// Doesn't return the original Set object.
+{
+ let set = new Set([1]);
+ assertEq(set.intersection(emptySet) !== set, true);
+ assertEq(set.intersection(new Set([2])) !== set, true);
+}
+
+// Test insertion order
+{
+ let set = new Set([1, 2, 3]);
+
+ // Case 1: Input is empty.
+ assertSetContainsExactOrderedItems(set.intersection(new Set([])), []);
+
+ // Case 2: Input has fewer elements.
+ assertSetContainsExactOrderedItems(set.intersection(new Set([1, 2])), [1, 2]);
+ assertSetContainsExactOrderedItems(set.intersection(new Set([2, 1])), [2, 1]);
+ assertSetContainsExactOrderedItems(set.intersection(new Set([11, 2])), [2]);
+ assertSetContainsExactOrderedItems(set.intersection(new Set([2, 11])), [2]);
+
+ // Case 3: Input has same number of elements.
+ assertSetContainsExactOrderedItems(set.intersection(new Set([1, 2, 3])), [1, 2, 3]);
+ assertSetContainsExactOrderedItems(set.intersection(new Set([2, 3, 1])), [1, 2, 3]);
+ assertSetContainsExactOrderedItems(set.intersection(new Set([3, 2, 1])), [1, 2, 3]);
+ assertSetContainsExactOrderedItems(set.intersection(new Set([11, 2, 3])), [2, 3]);
+ assertSetContainsExactOrderedItems(set.intersection(new Set([2, 3, 11])), [2, 3]);
+ assertSetContainsExactOrderedItems(set.intersection(new Set([3, 2, 11])), [2, 3]);
+
+ // Case 4: Input has more elements.
+ assertSetContainsExactOrderedItems(set.intersection(new Set([2, 3, 4, 5])), [2, 3]);
+ assertSetContainsExactOrderedItems(set.intersection(new Set([4, 5, 2, 3])), [2, 3]);
+ assertSetContainsExactOrderedItems(set.intersection(new Set([5, 4, 3, 2])), [2, 3]);
+}
+
+// Calls |has| when the this-value has fewer or the same number of keys.
+{
+ const keys = [1, 2, 3];
+
+ for (let size of [keys.length, 100, Infinity]) {
+ let i = 0;
+
+ let setLike = {
+ size,
+ has(v) {
+ assertEq(this, setLike);
+ assertEq(arguments.length, 1);
+ assertEq(i < keys.length, true);
+ assertEq(v, keys[i++]);
+ return true;
+ },
+ keys() {
+ throw new Error("Unexpected call to |keys| method");
+ },
+ };
+
+ assertSetContainsExactOrderedItems(new Set(keys).intersection(setLike), keys);
+ }
+}
+
+// Calls |keys| when the this-value has more keys.
+{
+ const keys = [1, 2, 3];
+
+ for (let size of [0, 1, 2]) {
+ let i = 0;
+
+ let setLike = {
+ size,
+ has() {
+ throw new Error("Unexpected call to |keys| method");
+ },
+ keys() {
+ assertEq(this, setLike);
+ assertEq(arguments.length, 0);
+
+ let iterator = {
+ next() {
+ assertEq(this, iterator);
+ assertEq(arguments.length, 0);
+ if (i < keys.length) {
+ return {
+ done: false,
+ value: keys[i++],
+ };
+ }
+ return {
+ done: true,
+ get value() {
+ throw new Error("Unexpected call to |value| getter");
+ },
+ };
+ }
+ };
+
+ return iterator;
+ },
+ };
+
+ assertSetContainsExactOrderedItems(new Set(keys).intersection(setLike), keys);
+ }
+}
+
+// Test result set order when the this-value was modified.
+{
+ let setLike = {
+ size: 0,
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ *keys() {
+ // Yield the same keys as in |set|.
+ yield* set.keys();
+
+ // Remove all existing items.
+ set.clear();
+
+ // Re-add keys 2 and 3, but in reversed order.
+ set.add(3);
+ set.add(2);
+
+ // Additionally add 99.
+ set.add(99);
+ },
+ };
+
+ let set = new Set([1, 2, 3, 4]);
+
+ assertSetContainsExactOrderedItems(set.intersection(setLike), [1, 2, 3, 4]);
+ assertSetContainsExactOrderedItems(set, [3, 2, 99]);
+}
+{
+ let setLike = {
+ size: 0,
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ *keys() {
+ // Yield the same keys as in |set|.
+ yield* set.keys();
+
+ // Remove only keys 2 and 3.
+ set.delete(2);
+ set.delete(3);
+
+ // Re-add keys 2 and 3, but in reversed order.
+ set.add(3);
+ set.add(2);
+ },
+ };
+
+ let set = new Set([1, 2, 3, 4]);
+
+ assertSetContainsExactOrderedItems(set.intersection(setLike), [1, 2, 3, 4]);
+ assertSetContainsExactOrderedItems(set, [1, 4, 3, 2]);
+}
+
+// Test the same item can't be added multiple times.
+{
+ let seen = [];
+
+ let setLike = {
+ size: 100,
+ has(v) {
+ // Remove and then re-add 2.
+ if (v === 2 && !seen.includes(v)) {
+ set.delete(v);
+ set.add(v);
+ }
+
+ // Remember all visited keys.
+ seen.push(v);
+
+ return true;
+ },
+ keys() {
+ throw new Error("Unexpected call to |keys| method");
+ },
+ };
+
+ let set = new Set([1, 2, 3]);
+
+ assertSetContainsExactOrderedItems(set.intersection(setLike), [1, 2, 3]);
+ assertEqArray(seen, [1, 2, 3, 2]);
+}
+
+// Tests which modify any built-ins should appear last, because modifications may disable
+// optimised code paths.
+
+// Doesn't call the built-in |Set.prototype.{has, keys, size}| functions.
+const SetPrototypeHas = Object.getOwnPropertyDescriptor(Set.prototype, "has");
+const SetPrototypeKeys = Object.getOwnPropertyDescriptor(Set.prototype, "keys");
+const SetPrototypeSize = Object.getOwnPropertyDescriptor(Set.prototype, "size");
+
+delete Set.prototype.has;
+delete Set.prototype.keys;
+delete Set.prototype.size;
+
+try {
+ let set = new Set([1, 2, 3]);
+ let other = new SetLike([1, 2, 3]);
+ assertSetContainsExactOrderedItems(set.intersection(other), [1, 2, 3]);
+} finally {
+ Object.defineProperty(Set.prototype, "has", SetPrototypeHas);
+ Object.defineProperty(Set.prototype, "keys", SetPrototypeKeys);
+ Object.defineProperty(Set.prototype, "size", SetPrototypeSize);
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Set/is-disjoint-from.js b/js/src/tests/non262/Set/is-disjoint-from.js
new file mode 100644
index 0000000000..e518accda4
--- /dev/null
+++ b/js/src/tests/non262/Set/is-disjoint-from.js
@@ -0,0 +1,448 @@
+// |reftest| shell-option(--enable-new-set-methods) skip-if(!Set.prototype.isDisjointFrom)
+
+assertEq(typeof Set.prototype.isDisjointFrom, "function");
+assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.isDisjointFrom, "length"), {
+ value: 1, writable: false, enumerable: false, configurable: true,
+});
+assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.isDisjointFrom, "name"), {
+ value: "isDisjointFrom", writable: false, enumerable: false, configurable: true,
+});
+
+const emptySet = new Set();
+const emptySetLike = new SetLike();
+const emptyMap = new Map();
+
+function asMap(values) {
+ return new Map(values.map(v => [v, v]));
+}
+
+// Empty set is disjoint from the empty set.
+assertEq(emptySet.isDisjointFrom(emptySet), true);
+assertEq(emptySet.isDisjointFrom(emptySetLike), true);
+assertEq(emptySet.isDisjointFrom(emptyMap), true);
+
+// Test native Set, Set-like, and Map objects.
+for (let values of [
+ [], [1], [1, 2], [1, true, null, {}],
+]) {
+ // Disjoint operation with an empty set.
+ assertEq(new Set(values).isDisjointFrom(emptySet), true);
+ assertEq(new Set(values).isDisjointFrom(emptySetLike), true);
+ assertEq(new Set(values).isDisjointFrom(emptyMap), true);
+ assertEq(emptySet.isDisjointFrom(new Set(values)), true);
+ assertEq(emptySet.isDisjointFrom(new SetLike(values)), true);
+ assertEq(emptySet.isDisjointFrom(asMap(values)), true);
+
+ // Two sets with the exact same values.
+ assertEq(new Set(values).isDisjointFrom(new Set(values)), values.length === 0);
+ assertEq(new Set(values).isDisjointFrom(new SetLike(values)), values.length === 0);
+ assertEq(new Set(values).isDisjointFrom(asMap(values)), values.length === 0);
+
+ // Disjoint operation of the same set object.
+ let set = new Set(values);
+ assertEq(set.isDisjointFrom(set), values.length === 0);
+}
+
+// Check property accesses are in the correct order.
+{
+ let log = [];
+
+ let sizeValue = 0;
+
+ let {proxy: keysIter} = LoggingProxy({
+ next() {
+ log.push("next()");
+ return {done: true};
+ }
+ }, log);
+
+ let {proxy: setLike} = LoggingProxy({
+ size: {
+ valueOf() {
+ log.push("valueOf()");
+ return sizeValue;
+ }
+ },
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ keys() {
+ log.push("keys()");
+ return keysIter;
+ },
+ }, log);
+
+ assertEq(emptySet.isDisjointFrom(setLike), true);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+
+ // |keys| is called when the this-value has more elements.
+
+ log.length = 0;
+ assertEq(new Set([1]).isDisjointFrom(setLike), true);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ "keys()",
+ "[[get]]", "next",
+ "next()",
+ ]);
+}
+
+// Check input validation.
+{
+ let log = [];
+
+ const nonCallable = {};
+ let sizeValue = 0;
+
+ let {proxy: keysIter} = LoggingProxy({
+ next: nonCallable,
+ }, log);
+
+ let {proxy: setLike, obj: setLikeObj} = LoggingProxy({
+ size: {
+ valueOf() {
+ log.push("valueOf()");
+ return sizeValue;
+ }
+ },
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ keys() {
+ log.push("keys()");
+ return keysIter;
+ },
+ }, log);
+
+ log.length = 0;
+ assertEq(emptySet.isDisjointFrom(setLike), true);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => new Set([1]).isDisjointFrom(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ "keys()",
+ "[[get]]", "next",
+ ]);
+
+ // Change |keys| to return a non-object value.
+ setLikeObj.keys = () => 123;
+
+ log.length = 0;
+ assertEq(emptySet.isDisjointFrom(setLike), true);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => new Set([1]).isDisjointFrom(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+
+ // Change |keys| to a non-callable value.
+ setLikeObj.keys = nonCallable;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.isDisjointFrom(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+
+ // Change |has| to a non-callable value.
+ setLikeObj.has = nonCallable;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.isDisjointFrom(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ ]);
+
+ // Change |size| to NaN.
+ sizeValue = NaN;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.isDisjointFrom(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ ]);
+
+ // Change |size| to undefined.
+ sizeValue = undefined;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.isDisjointFrom(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ ]);
+}
+
+// Doesn't accept Array as an input.
+assertThrowsInstanceOf(() => emptySet.isDisjointFrom([]), TypeError);
+
+// Works with Set subclasses.
+{
+ class MySet extends Set {}
+
+ let myEmptySet = new MySet;
+
+ assertEq(emptySet.isDisjointFrom(myEmptySet), true);
+ assertEq(myEmptySet.isDisjointFrom(myEmptySet), true);
+ assertEq(myEmptySet.isDisjointFrom(emptySet), true);
+}
+
+// Doesn't access any properties on the this-value.
+{
+ let log = [];
+
+ let {proxy: setProto} = LoggingProxy(Set.prototype, log);
+
+ let set = new Set([]);
+ Object.setPrototypeOf(set, setProto);
+
+ assertEq(Set.prototype.isDisjointFrom.call(set, emptySet), true);
+
+ assertEqArray(log, []);
+}
+
+// Throws a TypeError when the this-value isn't a Set object.
+for (let thisValue of [
+ null, undefined, true, "", {}, new Map, new Proxy(new Set, {}),
+]) {
+ assertThrowsInstanceOf(() => Set.prototype.isDisjointFrom.call(thisValue, emptySet), TypeError);
+}
+
+// Calls |has| when the this-value has fewer keys.
+{
+ const keys = [1, 2, 3];
+
+ for (let size of [keys.length, 100, Infinity]) {
+ let i = 0;
+
+ let setLike = {
+ size,
+ has(v) {
+ assertEq(this, setLike);
+ assertEq(arguments.length, 1);
+ assertEq(i < keys.length, true);
+ assertEq(v, keys[i++]);
+ return false;
+ },
+ keys() {
+ throw new Error("Unexpected call to |keys| method");
+ },
+ };
+
+ assertEq(new Set([1, 2, 3]).isDisjointFrom(setLike), true);
+ }
+}
+
+// Calls |keys| when the this-value has more keys.
+{
+ const keys = [1, 2, 3];
+
+ for (let size of [0, 1, 2]) {
+ let setLike = {
+ size,
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ *keys() {
+ yield* [4, 5, 6];
+ },
+ };
+
+ assertEq(new Set(keys).isDisjointFrom(setLike), true);
+ }
+
+ // Also test early return after first match.
+ for (let size of [0, 1, 2]) {
+ let setLike = {
+ size,
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ *keys() {
+ yield keys[0];
+
+ throw new Error("keys iterator called too many times");
+ },
+ };
+
+ assertEq(new Set(keys).isDisjointFrom(setLike), false);
+ }
+}
+
+// Test when this-value is modified during iteration.
+{
+ let set = new Set([]);
+
+ // |setLike| has more entries than |set|.
+ let setLike = {
+ size: 100,
+ has(v) {
+ assertEq(set.has(v), true);
+ set.delete(v);
+ return false;
+ },
+ keys() {
+ throw new Error("Unexpected call to |keys| method");
+ },
+ };
+
+ assertEq(set.isDisjointFrom(setLike), true);
+ assertSetContainsExactOrderedItems(set, []);
+}
+{
+ let set = new Set([0]);
+
+ let keys = [1, 2, 3];
+
+ let lastValue;
+ let keysIter = {
+ next() {
+ if (lastValue !== undefined) {
+ assertEq(set.has(lastValue), false);
+ set.add(lastValue);
+
+ lastValue = undefined;
+ }
+
+ if (keys.length) {
+ let value = keys.shift();
+ lastValue = value;
+ return {
+ done: false,
+ get value() {
+ return value;
+ }
+ };
+ }
+ return {
+ done: true,
+ get value() {
+ throw new Error("Unexpected call to |value| getter");
+ }
+ };
+ }
+ };
+
+ // |setLike| has fewer entries than |set|.
+ let setLike = {
+ size: 0,
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ keys() {
+ return keysIter;
+ },
+ };
+
+ assertEq(set.isDisjointFrom(setLike), true);
+ assertSetContainsExactOrderedItems(set, [0, 1, 2, 3]);
+}
+
+// IteratorClose is called for early returns.
+{
+ let log = [];
+
+ let keysIter = {
+ next() {
+ log.push("next");
+ return {done: false, value: 1};
+ },
+ return() {
+ log.push("return");
+ return {
+ get value() { throw new Error("Unexpected call to |value| getter"); },
+ get done() { throw new Error("Unexpected call to |done| getter"); },
+ };
+ }
+ };
+
+ let setLike = {
+ size: 0,
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ keys() {
+ return keysIter;
+ },
+ };
+
+ assertEq(new Set([1, 2, 3]).isDisjointFrom(setLike), false);
+
+ assertEqArray(log, ["next", "return"]);
+}
+
+// IteratorClose isn't called for non-early returns.
+{
+ let setLike = new SetLike([4, 5, 6]);
+
+ assertEq(new Set([1, 2, 3]).isDisjointFrom(setLike), true);
+}
+
+// Tests which modify any built-ins should appear last, because modifications may disable
+// optimised code paths.
+
+// Doesn't call the built-in |Set.prototype.{has, keys, size}| functions.
+const SetPrototypeHas = Object.getOwnPropertyDescriptor(Set.prototype, "has");
+const SetPrototypeKeys = Object.getOwnPropertyDescriptor(Set.prototype, "keys");
+const SetPrototypeSize = Object.getOwnPropertyDescriptor(Set.prototype, "size");
+
+delete Set.prototype.has;
+delete Set.prototype.keys;
+delete Set.prototype.size;
+
+try {
+ let set = new Set([1, 2, 3]);
+ let other = new SetLike([1, 2, 3]);
+ assertEq(set.isDisjointFrom(other), false);
+} finally {
+ Object.defineProperty(Set.prototype, "has", SetPrototypeHas);
+ Object.defineProperty(Set.prototype, "keys", SetPrototypeKeys);
+ Object.defineProperty(Set.prototype, "size", SetPrototypeSize);
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Set/is-subset-of.js b/js/src/tests/non262/Set/is-subset-of.js
new file mode 100644
index 0000000000..2847bb4944
--- /dev/null
+++ b/js/src/tests/non262/Set/is-subset-of.js
@@ -0,0 +1,333 @@
+// |reftest| shell-option(--enable-new-set-methods) skip-if(!Set.prototype.isSubsetOf)
+
+assertEq(typeof Set.prototype.isSubsetOf, "function");
+assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.isSubsetOf, "length"), {
+ value: 1, writable: false, enumerable: false, configurable: true,
+});
+assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.isSubsetOf, "name"), {
+ value: "isSubsetOf", writable: false, enumerable: false, configurable: true,
+});
+
+const emptySet = new Set();
+const emptySetLike = new SetLike();
+const emptyMap = new Map();
+
+function asMap(values) {
+ return new Map(values.map(v => [v, v]));
+}
+
+// Empty set is a subset of the empty set.
+assertEq(emptySet.isSubsetOf(emptySet), true);
+assertEq(emptySet.isSubsetOf(emptySetLike), true);
+assertEq(emptySet.isSubsetOf(emptyMap), true);
+
+// Test native Set, Set-like, and Map objects.
+for (let values of [
+ [], [1], [1, 2], [1, true, null, {}],
+]) {
+ // Subset operation with an empty set.
+ assertEq(new Set(values).isSubsetOf(emptySet), values.length === 0);
+ assertEq(new Set(values).isSubsetOf(emptySetLike), values.length === 0);
+ assertEq(new Set(values).isSubsetOf(emptyMap), values.length === 0);
+ assertEq(emptySet.isSubsetOf(new Set(values)), true);
+ assertEq(emptySet.isSubsetOf(new SetLike(values)), true);
+ assertEq(emptySet.isSubsetOf(asMap(values)), true);
+
+ // Two sets with the exact same values.
+ assertEq(new Set(values).isSubsetOf(new Set(values)), true);
+ assertEq(new Set(values).isSubsetOf(new SetLike(values)), true);
+ assertEq(new Set(values).isSubsetOf(asMap(values)), true);
+
+ // Subset operation of the same set object.
+ let set = new Set(values);
+ assertEq(set.isSubsetOf(set), true);
+}
+
+// Check property accesses are in the correct order.
+{
+ let log = [];
+
+ let sizeValue = 0;
+
+ let {proxy: keysIter} = LoggingProxy({
+ next() {
+ log.push("next()");
+ return {done: true};
+ }
+ }, log);
+
+ let {proxy: setLike} = LoggingProxy({
+ size: {
+ valueOf() {
+ log.push("valueOf()");
+ return sizeValue;
+ }
+ },
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ keys() {
+ log.push("keys()");
+ return keysIter;
+ },
+ }, log);
+
+ assertEq(emptySet.isSubsetOf(setLike), true);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+}
+
+// Check input validation.
+{
+ let log = [];
+
+ const nonCallable = {};
+ let sizeValue = 0;
+
+ let {proxy: setLike, obj: setLikeObj} = LoggingProxy({
+ size: {
+ valueOf() {
+ log.push("valueOf()");
+ return sizeValue;
+ }
+ },
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ keys() {
+ throw new Error("Unexpected call to |keys| method");
+ },
+ }, log);
+
+ // Change |keys| to a non-callable value.
+ setLikeObj.keys = nonCallable;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.isSubsetOf(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+
+ // Change |has| to a non-callable value.
+ setLikeObj.has = nonCallable;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.isSubsetOf(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ ]);
+
+ // Change |size| to NaN.
+ sizeValue = NaN;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.isSubsetOf(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ ]);
+
+ // Change |size| to undefined.
+ sizeValue = undefined;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.isSubsetOf(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ ]);
+}
+
+// Doesn't accept Array as an input.
+assertThrowsInstanceOf(() => emptySet.isSubsetOf([]), TypeError);
+
+// Works with Set subclasses.
+{
+ class MySet extends Set {}
+
+ let myEmptySet = new MySet;
+
+ assertEq(emptySet.isSubsetOf(myEmptySet), true);
+ assertEq(myEmptySet.isSubsetOf(myEmptySet), true);
+ assertEq(myEmptySet.isSubsetOf(emptySet), true);
+}
+
+// Doesn't access any properties on the this-value.
+{
+ let log = [];
+
+ let {proxy: setProto} = LoggingProxy(Set.prototype, log);
+
+ let set = new Set([]);
+ Object.setPrototypeOf(set, setProto);
+
+ assertEq(Set.prototype.isSubsetOf.call(set, emptySet), true);
+
+ assertEqArray(log, []);
+}
+
+// Throws a TypeError when the this-value isn't a Set object.
+for (let thisValue of [
+ null, undefined, true, "", {}, new Map, new Proxy(new Set, {}),
+]) {
+ assertThrowsInstanceOf(() => Set.prototype.isSubsetOf.call(thisValue, emptySet), TypeError);
+}
+
+// Doesn't call |has| when this-value has more elements.
+{
+ let set = new Set([1, 2, 3]);
+
+ for (let size of [0, 1, 2]) {
+ let setLike = {
+ size,
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ keys() {
+ throw new Error("Unexpected call to |keys| method");
+ },
+ };
+
+ assertEq(set.isSubsetOf(setLike), false);
+ }
+}
+
+// Test when this-value is modified during iteration.
+{
+ let set = new Set([1, 2, 3]);
+
+ let seen = new Set();
+
+ let setLike = {
+ size: 100,
+ has(v) {
+ assertEq(arguments.length, 1);
+ assertEq(this, setLike);
+ assertEq(set.has(v), true);
+
+ assertEq(seen.has(v), false);
+ seen.add(v);
+
+ // Delete the current element.
+ set.delete(v);
+
+ return true;
+ },
+ keys() {
+ throw new Error("Unexpected call to |keys| method");
+ },
+ };
+
+ assertEq(set.isSubsetOf(setLike), true);
+ assertSetContainsExactOrderedItems(set, []);
+ assertSetContainsExactOrderedItems(seen, [1, 2, 3]);
+}
+{
+ let set = new Set([1]);
+
+ let seen = new Set();
+ let newKeys = [2, 3, 4, 5];
+
+ let setLike = {
+ size: 100,
+ has(v) {
+ assertEq(arguments.length, 1);
+ assertEq(this, setLike);
+ assertEq(set.has(v), true);
+
+ assertEq(seen.has(v), false);
+ seen.add(v);
+
+ // Delete the current element.
+ set.delete(v);
+
+ // Add new elements.
+ if (newKeys.length) {
+ set.add(newKeys.shift());
+ }
+
+ return true;
+ },
+ keys() {
+ throw new Error("Unexpected call to |keys| method");
+ },
+ };
+
+ assertEq(set.isSubsetOf(setLike), true);
+ assertSetContainsExactOrderedItems(set, []);
+ assertSetContainsExactOrderedItems(seen, [1, 2, 3, 4, 5]);
+ assertEq(newKeys.length, 0);
+}
+{
+ let set = new Set([1, 2, 3]);
+
+ let seen = new Set();
+ let deleted = false;
+
+ let setLike = {
+ size: 100,
+ has(v) {
+ assertEq(arguments.length, 1);
+ assertEq(this, setLike);
+ assertEq(set.has(v), true);
+
+ assertEq(seen.has(v), false);
+ seen.add(v);
+
+ if (!deleted) {
+ assertEq(v, 1);
+ set.delete(2);
+ deleted = true;
+ }
+
+ return true;
+ },
+ keys() {
+ throw new Error("Unexpected call to |keys| method");
+ },
+ };
+
+ assertEq(set.isSubsetOf(setLike), true);
+ assertSetContainsExactOrderedItems(set, [1, 3]);
+ assertSetContainsExactOrderedItems(seen, [1, 3]);
+ assertEq(deleted, true);
+}
+
+// Tests which modify any built-ins should appear last, because modifications may disable
+// optimised code paths.
+
+// Doesn't call the built-in |Set.prototype.{has, keys, size}| functions.
+const SetPrototypeHas = Object.getOwnPropertyDescriptor(Set.prototype, "has");
+const SetPrototypeKeys = Object.getOwnPropertyDescriptor(Set.prototype, "keys");
+const SetPrototypeSize = Object.getOwnPropertyDescriptor(Set.prototype, "size");
+
+delete Set.prototype.has;
+delete Set.prototype.keys;
+delete Set.prototype.size;
+
+try {
+ let set = new Set([1, 2, 3]);
+ let other = new SetLike([1, 2, 3]);
+ assertEq(set.isSubsetOf(other), true);
+} finally {
+ Object.defineProperty(Set.prototype, "has", SetPrototypeHas);
+ Object.defineProperty(Set.prototype, "keys", SetPrototypeKeys);
+ Object.defineProperty(Set.prototype, "size", SetPrototypeSize);
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Set/is-superset-of.js b/js/src/tests/non262/Set/is-superset-of.js
new file mode 100644
index 0000000000..c0728697fe
--- /dev/null
+++ b/js/src/tests/non262/Set/is-superset-of.js
@@ -0,0 +1,360 @@
+// |reftest| shell-option(--enable-new-set-methods) skip-if(!Set.prototype.isSupersetOf)
+
+assertEq(typeof Set.prototype.isSupersetOf, "function");
+assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.isSupersetOf, "length"), {
+ value: 1, writable: false, enumerable: false, configurable: true,
+});
+assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.isSupersetOf, "name"), {
+ value: "isSupersetOf", writable: false, enumerable: false, configurable: true,
+});
+
+const emptySet = new Set();
+const emptySetLike = new SetLike();
+const emptyMap = new Map();
+
+function asMap(values) {
+ return new Map(values.map(v => [v, v]));
+}
+
+// Empty set is a superset of the empty set.
+assertEq(emptySet.isSupersetOf(emptySet), true);
+assertEq(emptySet.isSupersetOf(emptySetLike), true);
+assertEq(emptySet.isSupersetOf(emptyMap), true);
+
+// Test native Set, Set-like, and Map objects.
+for (let values of [
+ [], [1], [1, 2], [1, true, null, {}],
+]) {
+ // Superset operation with an empty set.
+ assertEq(new Set(values).isSupersetOf(emptySet), true);
+ assertEq(new Set(values).isSupersetOf(emptySetLike), true);
+ assertEq(new Set(values).isSupersetOf(emptyMap), true);
+ assertEq(emptySet.isSupersetOf(new Set(values)), values.length === 0);
+ assertEq(emptySet.isSupersetOf(new SetLike(values)), values.length === 0);
+ assertEq(emptySet.isSupersetOf(asMap(values)), values.length === 0);
+
+ // Two sets with the exact same values.
+ assertEq(new Set(values).isSupersetOf(new Set(values)), true);
+ assertEq(new Set(values).isSupersetOf(new SetLike(values)), true);
+ assertEq(new Set(values).isSupersetOf(asMap(values)), true);
+
+ // Superset operation of the same set object.
+ let set = new Set(values);
+ assertEq(set.isSupersetOf(set), true);
+}
+
+// Check property accesses are in the correct order.
+{
+ let log = [];
+
+ let sizeValue = 0;
+
+ let {proxy: keysIter} = LoggingProxy({
+ next() {
+ log.push("next()");
+ return {done: true};
+ }
+ }, log);
+
+ let {proxy: setLike} = LoggingProxy({
+ size: {
+ valueOf() {
+ log.push("valueOf()");
+ return sizeValue;
+ }
+ },
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ keys() {
+ log.push("keys()");
+ return keysIter;
+ },
+ }, log);
+
+ assertEq(emptySet.isSupersetOf(setLike), true);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ "keys()",
+ "[[get]]", "next",
+ "next()",
+ ]);
+
+ // |keys| isn't called when the this-value has fewer elements.
+ sizeValue = 1;
+
+ log.length = 0;
+ assertEq(emptySet.isSupersetOf(setLike), false);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+}
+
+// Check input validation.
+{
+ let log = [];
+
+ const nonCallable = {};
+ let sizeValue = 0;
+
+ let {proxy: keysIter} = LoggingProxy({
+ next: nonCallable,
+ }, log);
+
+ let {proxy: setLike, obj: setLikeObj} = LoggingProxy({
+ size: {
+ valueOf() {
+ log.push("valueOf()");
+ return sizeValue;
+ }
+ },
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ keys() {
+ log.push("keys()");
+ return keysIter;
+ },
+ }, log);
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.isSupersetOf(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ "keys()",
+ "[[get]]", "next",
+ ]);
+
+ // Change |keys| to return a non-object value.
+ setLikeObj.keys = () => 123;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.isSupersetOf(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+
+ // Change |keys| to a non-callable value.
+ setLikeObj.keys = nonCallable;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.isSupersetOf(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+
+ // Change |has| to a non-callable value.
+ setLikeObj.has = nonCallable;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.isSupersetOf(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ ]);
+
+ // Change |size| to NaN.
+ sizeValue = NaN;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.isSupersetOf(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ ]);
+
+ // Change |size| to undefined.
+ sizeValue = undefined;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.isSupersetOf(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ ]);
+}
+
+// Doesn't accept Array as an input.
+assertThrowsInstanceOf(() => emptySet.isSupersetOf([]), TypeError);
+
+// Works with Set subclasses.
+{
+ class MySet extends Set {}
+
+ let myEmptySet = new MySet;
+
+ assertEq(emptySet.isSupersetOf(myEmptySet), true);
+ assertEq(myEmptySet.isSupersetOf(myEmptySet), true);
+ assertEq(myEmptySet.isSupersetOf(emptySet), true);
+}
+
+// Doesn't access any properties on the this-value.
+{
+ let log = [];
+
+ let {proxy: setProto} = LoggingProxy(Set.prototype, log);
+
+ let set = new Set([]);
+ Object.setPrototypeOf(set, setProto);
+
+ assertEq(Set.prototype.isSupersetOf.call(set, emptySet), true);
+
+ assertEqArray(log, []);
+}
+
+// Throws a TypeError when the this-value isn't a Set object.
+for (let thisValue of [
+ null, undefined, true, "", {}, new Map, new Proxy(new Set, {}),
+]) {
+ assertThrowsInstanceOf(() => Set.prototype.isSupersetOf.call(thisValue, emptySet), TypeError);
+}
+
+// Doesn't call |has| nor |keys| when this-value has fewer elements.
+{
+ let set = new Set([1, 2, 3]);
+
+ for (let size of [100, Infinity]) {
+ let setLike = {
+ size,
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ keys() {
+ throw new Error("Unexpected call to |keys| method");
+ },
+ };
+
+ assertEq(set.isSupersetOf(setLike), false);
+ }
+}
+
+// Test when this-value is modified during iteration.
+{
+ let set = new Set([]);
+
+ let keys = [1, 2, 3];
+
+ let keysIter = {
+ next() {
+ if (keys.length) {
+ let value = keys.shift();
+ return {
+ done: false,
+ get value() {
+ assertEq(set.has(value), false);
+ set.add(value);
+ return value;
+ }
+ };
+ }
+ return {
+ done: true,
+ get value() {
+ throw new Error("Unexpected call to |value| getter");
+ }
+ };
+ }
+ };
+
+ let setLike = {
+ size: 0,
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ keys() {
+ return keysIter;
+ },
+ };
+
+ assertEq(set.isSupersetOf(setLike), true);
+ assertSetContainsExactOrderedItems(set, [1, 2, 3]);
+}
+
+// IteratorClose is called for early returns.
+{
+ let log = [];
+
+ let keysIter = {
+ next() {
+ log.push("next");
+ return {done: false, value: 1};
+ },
+ return() {
+ log.push("return");
+ return {
+ get value() { throw new Error("Unexpected call to |value| getter"); },
+ get done() { throw new Error("Unexpected call to |done| getter"); },
+ };
+ }
+ };
+
+ let setLike = {
+ size: 0,
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ keys() {
+ return keysIter;
+ },
+ };
+
+ assertEq(new Set([2, 3, 4]).isSupersetOf(setLike), false);
+
+ assertEqArray(log, ["next", "return"]);
+}
+
+// IteratorClose isn't called for non-early returns.
+{
+ let setLike = new SetLike([1, 2, 3]);
+
+ assertEq(new Set([1, 2, 3]).isSupersetOf(setLike), true);
+}
+
+// Tests which modify any built-ins should appear last, because modifications may disable
+// optimised code paths.
+
+// Doesn't call the built-in |Set.prototype.{has, keys, size}| functions.
+const SetPrototypeHas = Object.getOwnPropertyDescriptor(Set.prototype, "has");
+const SetPrototypeKeys = Object.getOwnPropertyDescriptor(Set.prototype, "keys");
+const SetPrototypeSize = Object.getOwnPropertyDescriptor(Set.prototype, "size");
+
+delete Set.prototype.has;
+delete Set.prototype.keys;
+delete Set.prototype.size;
+
+try {
+ let set = new Set([1, 2, 3]);
+ let other = new SetLike([1, 2, 3]);
+ assertEq(set.isSupersetOf(other), true);
+} finally {
+ Object.defineProperty(Set.prototype, "has", SetPrototypeHas);
+ Object.defineProperty(Set.prototype, "keys", SetPrototypeKeys);
+ Object.defineProperty(Set.prototype, "size", SetPrototypeSize);
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Set/shell.js b/js/src/tests/non262/Set/shell.js
new file mode 100644
index 0000000000..d6bd2e2083
--- /dev/null
+++ b/js/src/tests/non262/Set/shell.js
@@ -0,0 +1,102 @@
+(function(global) {
+ // Save the primordial values.
+ const {Array, Error, Object, Proxy, Reflect, Set} = global;
+
+ const ArrayIsArray = Array.isArray;
+ const ReflectApply = Reflect.apply;
+ const ReflectDefineProperty = Reflect.defineProperty;
+ const ReflectGet = Reflect.get;
+ const ReflectGetPrototypeOf = Reflect.getPrototypeOf;
+ const SetPrototype = Set.prototype;
+ const SetPrototypeHas = SetPrototype.has;
+ const SetPrototypeSize = Object.getOwnPropertyDescriptor(SetPrototype, "size").get;
+ const SetPrototypeKeys = SetPrototype.keys;
+ const SetIteratorPrototypeNext = new Set().values().next;
+
+ function assertSetContainsExactOrderedItems(actual, expected) {
+ assertEq(ReflectGetPrototypeOf(actual), SetPrototype, "actual is a native Set object");
+ assertEq(ArrayIsArray(expected), true, "expected is an Array object");
+
+ assertEq(ReflectApply(SetPrototypeSize, actual, []), expected.length);
+
+ let index = 0;
+ let keys = ReflectApply(SetPrototypeKeys, actual, []);
+
+ while (true) {
+ let {done, value: item} = ReflectApply(SetIteratorPrototypeNext, keys, []);
+ if (done) {
+ break;
+ }
+ assertEq(item, expected[index], `Element at index ${index}:`);
+ index++;
+ }
+ }
+ global.assertSetContainsExactOrderedItems = assertSetContainsExactOrderedItems;
+
+ class SetLike {
+ #set;
+
+ constructor(values) {
+ this.#set = new Set(values);
+ }
+
+ get size() {
+ return ReflectApply(SetPrototypeSize, this.#set, []);
+ }
+
+ has(value) {
+ return ReflectApply(SetPrototypeHas, this.#set, [value]);
+ }
+
+ keys() {
+ let keys = ReflectApply(SetPrototypeKeys, this.#set, []);
+ return new SetIteratorLike(keys);
+ }
+ }
+ global.SetLike = SetLike;
+
+ class SetIteratorLike {
+ #keys;
+
+ constructor(keys) {
+ this.#keys = keys;
+ }
+
+ next() {
+ return ReflectApply(SetIteratorPrototypeNext, this.#keys, []);
+ }
+
+ // The |return| method of the iterator protocol is never called.
+ return() {
+ throw new Error("Unexpected call to |return| method");
+ }
+
+ // The |throw| method of the iterator protocol is never called.
+ throw() {
+ throw new Error("Unexpected call to |throw| method");
+ }
+ }
+
+ function LoggingProxy(obj, log) {
+ assertEq(ArrayIsArray(log), true);
+
+ let handler = new Proxy({
+ get(t, pk, r) {
+ ReflectDefineProperty(log, log.length, {
+ value: pk, writable: true, enumerable: true, configurable: true,
+ });
+ return ReflectGet(t, pk, r);
+ }
+ }, {
+ get(t, pk, r) {
+ ReflectDefineProperty(log, log.length, {
+ value: `[[${pk}]]`, writable: true, enumerable: true, configurable: true,
+ });
+ return ReflectGet(t, pk, r);
+ }
+ });
+
+ return {obj, proxy: new Proxy(obj, handler)};
+ }
+ global.LoggingProxy = LoggingProxy;
+})(this);
diff --git a/js/src/tests/non262/Set/symmetric-difference.js b/js/src/tests/non262/Set/symmetric-difference.js
new file mode 100644
index 0000000000..a4182cbed3
--- /dev/null
+++ b/js/src/tests/non262/Set/symmetric-difference.js
@@ -0,0 +1,303 @@
+// |reftest| shell-option(--enable-new-set-methods) skip-if(!Set.prototype.symmetricDifference)
+
+assertEq(typeof Set.prototype.symmetricDifference, "function");
+assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.symmetricDifference, "length"), {
+ value: 1, writable: false, enumerable: false, configurable: true,
+});
+assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.symmetricDifference, "name"), {
+ value: "symmetricDifference", writable: false, enumerable: false, configurable: true,
+});
+
+const emptySet = new Set();
+const emptySetLike = new SetLike();
+const emptyMap = new Map();
+
+function asMap(values) {
+ return new Map(values.map(v => [v, v]));
+}
+
+// Symmetric difference of two empty sets is an empty set.
+assertSetContainsExactOrderedItems(emptySet.symmetricDifference(emptySet), []);
+assertSetContainsExactOrderedItems(emptySet.symmetricDifference(emptySetLike), []);
+assertSetContainsExactOrderedItems(emptySet.symmetricDifference(emptyMap), []);
+
+// Test native Set, Set-like, and Map objects.
+for (let values of [
+ [], [1], [1, 2], [1, true, null, {}],
+]) {
+ // Symmetric difference with an empty set.
+ assertSetContainsExactOrderedItems(new Set(values).symmetricDifference(emptySet), values);
+ assertSetContainsExactOrderedItems(new Set(values).symmetricDifference(emptySetLike), values);
+ assertSetContainsExactOrderedItems(new Set(values).symmetricDifference(emptyMap), values);
+ assertSetContainsExactOrderedItems(emptySet.symmetricDifference(new Set(values)), values);
+ assertSetContainsExactOrderedItems(emptySet.symmetricDifference(new SetLike(values)), values);
+ assertSetContainsExactOrderedItems(emptySet.symmetricDifference(asMap(values)), values);
+
+ // Two sets with the exact same values.
+ assertSetContainsExactOrderedItems(new Set(values).symmetricDifference(new Set(values)), []);
+ assertSetContainsExactOrderedItems(new Set(values).symmetricDifference(new SetLike(values)), []);
+ assertSetContainsExactOrderedItems(new Set(values).symmetricDifference(asMap(values)), []);
+
+ // Symmetric difference of the same set object.
+ let set = new Set(values);
+ assertSetContainsExactOrderedItems(set.symmetricDifference(set), []);
+}
+
+// Check property accesses are in the correct order.
+{
+ let log = [];
+
+ let sizeValue = 0;
+
+ let {proxy: keysIter} = LoggingProxy({
+ next() {
+ log.push("next()");
+ return {done: true};
+ }
+ }, log);
+
+ let {proxy: setLike} = LoggingProxy({
+ size: {
+ valueOf() {
+ log.push("valueOf()");
+ return sizeValue;
+ }
+ },
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ keys() {
+ log.push("keys()");
+ return keysIter;
+ },
+ }, log);
+
+ assertSetContainsExactOrderedItems(emptySet.symmetricDifference(setLike), []);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ "keys()",
+ "[[get]]", "next",
+ "next()",
+ ]);
+}
+
+// Check input validation.
+{
+ let log = [];
+
+ const nonCallable = {};
+ let sizeValue = 0;
+
+ let {proxy: keysIter} = LoggingProxy({
+ next: nonCallable,
+ }, log);
+
+ let {proxy: setLike, obj: setLikeObj} = LoggingProxy({
+ size: {
+ valueOf() {
+ log.push("valueOf()");
+ return sizeValue;
+ }
+ },
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ keys() {
+ log.push("keys()");
+ return keysIter;
+ },
+ }, log);
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.symmetricDifference(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ "keys()",
+ "[[get]]", "next",
+ ]);
+
+ // Change |keys| to return a non-object value.
+ setLikeObj.keys = () => 123;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.symmetricDifference(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+
+ // Change |keys| to a non-callable value.
+ setLikeObj.keys = nonCallable;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.symmetricDifference(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+
+ // Change |has| to a non-callable value.
+ setLikeObj.has = nonCallable;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.symmetricDifference(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ ]);
+
+ // Change |size| to NaN.
+ sizeValue = NaN;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.symmetricDifference(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ ]);
+
+ // Change |size| to undefined.
+ sizeValue = undefined;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.symmetricDifference(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ ]);
+}
+
+// Doesn't accept Array as an input.
+assertThrowsInstanceOf(() => emptySet.symmetricDifference([]), TypeError);
+
+// Works with Set subclasses.
+{
+ class MySet extends Set {}
+
+ let myEmptySet = new MySet;
+
+ assertSetContainsExactOrderedItems(emptySet.symmetricDifference(myEmptySet), []);
+ assertSetContainsExactOrderedItems(myEmptySet.symmetricDifference(myEmptySet), []);
+ assertSetContainsExactOrderedItems(myEmptySet.symmetricDifference(emptySet), []);
+}
+
+// Doesn't access any properties on the this-value.
+{
+ let log = [];
+
+ let {proxy: setProto} = LoggingProxy(Set.prototype, log);
+
+ let set = new Set([1, 2, 3]);
+ Object.setPrototypeOf(set, setProto);
+
+ assertSetContainsExactOrderedItems(Set.prototype.symmetricDifference.call(set, emptySet), [1, 2, 3]);
+
+ assertEqArray(log, []);
+}
+
+// Throws a TypeError when the this-value isn't a Set object.
+for (let thisValue of [
+ null, undefined, true, "", {}, new Map, new Proxy(new Set, {}),
+]) {
+ assertThrowsInstanceOf(() => Set.prototype.symmetricDifference.call(thisValue, emptySet), TypeError);
+}
+
+// Doesn't return the original Set object.
+{
+ let set = new Set([1]);
+ assertEq(set.symmetricDifference(emptySet) !== set, true);
+ assertEq(set.symmetricDifference(new Set([2])) !== set, true);
+}
+
+// Test insertion order
+{
+ let set = new Set([1, 2]);
+
+ // Case 1: Input is empty.
+ assertSetContainsExactOrderedItems(set.symmetricDifference(new Set([])), [1, 2]);
+
+ // Case 2: Input has fewer elements.
+ assertSetContainsExactOrderedItems(set.symmetricDifference(new Set([3])), [1, 2, 3]);
+ assertSetContainsExactOrderedItems(set.symmetricDifference(new Set([2])), [1]);
+
+ // Case 3: Input has same number of elements.
+ assertSetContainsExactOrderedItems(set.symmetricDifference(new Set([3, 4])), [1, 2, 3, 4]);
+ assertSetContainsExactOrderedItems(set.symmetricDifference(new Set([2, 3])), [1, 3]);
+
+ // Case 4: Input has more elements.
+ assertSetContainsExactOrderedItems(set.symmetricDifference(new Set([3, 4, 5])), [1, 2, 3, 4, 5]);
+ assertSetContainsExactOrderedItems(set.symmetricDifference(new Set([2, 4, 5])), [1, 4, 5]);
+}
+
+// Test that the input set is copied after accessing the |next| property of the keys iterator.
+{
+ let set = new Set([1, 2, 3]);
+
+ let setLike = {
+ size: 0,
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ keys() {
+ return {
+ get next() {
+ // Clear the set when getting the |next| method.
+ set.clear();
+
+ // And then add a single new key.
+ set.add(4);
+
+ return function() {
+ return {done: true};
+ };
+ }
+ };
+ },
+ };
+
+ // The result should consist of the single, newly added key.
+ assertSetContainsExactOrderedItems(set.symmetricDifference(setLike), [4]);
+}
+
+// Tests which modify any built-ins should appear last, because modifications may disable
+// optimised code paths.
+
+// Doesn't call the built-in |Set.prototype.{has, keys, size}| functions.
+const SetPrototypeHas = Object.getOwnPropertyDescriptor(Set.prototype, "has");
+const SetPrototypeKeys = Object.getOwnPropertyDescriptor(Set.prototype, "keys");
+const SetPrototypeSize = Object.getOwnPropertyDescriptor(Set.prototype, "size");
+
+delete Set.prototype.has;
+delete Set.prototype.keys;
+delete Set.prototype.size;
+
+try {
+ let set = new Set([1, 2, 3]);
+ let other = new SetLike([1, 2, 3]);
+ assertSetContainsExactOrderedItems(set.symmetricDifference(other), []);
+} finally {
+ Object.defineProperty(Set.prototype, "has", SetPrototypeHas);
+ Object.defineProperty(Set.prototype, "keys", SetPrototypeKeys);
+ Object.defineProperty(Set.prototype, "size", SetPrototypeSize);
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Set/union.js b/js/src/tests/non262/Set/union.js
new file mode 100644
index 0000000000..0aef79822d
--- /dev/null
+++ b/js/src/tests/non262/Set/union.js
@@ -0,0 +1,300 @@
+// |reftest| shell-option(--enable-new-set-methods) skip-if(!Set.prototype.union)
+
+assertEq(typeof Set.prototype.union, "function");
+assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.union, "length"), {
+ value: 1, writable: false, enumerable: false, configurable: true,
+});
+assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.union, "name"), {
+ value: "union", writable: false, enumerable: false, configurable: true,
+});
+
+const emptySet = new Set();
+const emptySetLike = new SetLike();
+const emptyMap = new Map();
+
+function asMap(values) {
+ return new Map(values.map(v => [v, v]));
+}
+
+// Union of two empty sets is an empty set.
+assertSetContainsExactOrderedItems(emptySet.union(emptySet), []);
+assertSetContainsExactOrderedItems(emptySet.union(emptySetLike), []);
+assertSetContainsExactOrderedItems(emptySet.union(emptyMap), []);
+
+// Test native Set, Set-like, and Map objects.
+for (let values of [
+ [], [1], [1, 2], [1, true, null, {}],
+]) {
+ // Union with an empty set.
+ assertSetContainsExactOrderedItems(new Set(values).union(emptySet), values);
+ assertSetContainsExactOrderedItems(new Set(values).union(emptySetLike), values);
+ assertSetContainsExactOrderedItems(new Set(values).union(emptyMap), values);
+ assertSetContainsExactOrderedItems(emptySet.union(new Set(values)), values);
+ assertSetContainsExactOrderedItems(emptySet.union(new SetLike(values)), values);
+ assertSetContainsExactOrderedItems(emptySet.union(asMap(values)), values);
+
+ // Two sets with the exact same values.
+ assertSetContainsExactOrderedItems(new Set(values).union(new Set(values)), values);
+ assertSetContainsExactOrderedItems(new Set(values).union(new SetLike(values)), values);
+ assertSetContainsExactOrderedItems(new Set(values).union(asMap(values)), values);
+
+ // Union of the same set object.
+ let set = new Set(values);
+ assertSetContainsExactOrderedItems(set.union(set), values);
+}
+
+// Check property accesses are in the correct order.
+{
+ let log = [];
+
+ let sizeValue = 0;
+
+ let {proxy: keysIter} = LoggingProxy({
+ next() {
+ log.push("next()");
+ return {done: true};
+ }
+ }, log);
+
+ let {proxy: setLike} = LoggingProxy({
+ size: {
+ valueOf() {
+ log.push("valueOf()");
+ return sizeValue;
+ }
+ },
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ keys() {
+ log.push("keys()");
+ return keysIter;
+ },
+ }, log);
+
+ assertSetContainsExactOrderedItems(emptySet.union(setLike), []);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ "keys()",
+ "[[get]]", "next",
+ "next()",
+ ]);
+}
+
+// Check input validation.
+{
+ let log = [];
+
+ const nonCallable = {};
+ let sizeValue = 0;
+
+ let {proxy: keysIter} = LoggingProxy({
+ next: nonCallable,
+ }, log);
+
+ let {proxy: setLike, obj: setLikeObj} = LoggingProxy({
+ size: {
+ valueOf() {
+ log.push("valueOf()");
+ return sizeValue;
+ }
+ },
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ keys() {
+ log.push("keys()");
+ return keysIter;
+ },
+ }, log);
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.union(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ "keys()",
+ "[[get]]", "next",
+ ]);
+
+ // Change |keys| to return a non-object value.
+ setLikeObj.keys = () => 123;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.union(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+
+ // Change |keys| to a non-callable value.
+ setLikeObj.keys = nonCallable;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.union(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ "[[get]]", "keys",
+ ]);
+
+ // Change |has| to a non-callable value.
+ setLikeObj.has = nonCallable;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.union(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ "[[get]]", "has",
+ ]);
+
+ // Change |size| to NaN.
+ sizeValue = NaN;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.union(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ ]);
+
+ // Change |size| to undefined.
+ sizeValue = undefined;
+
+ log.length = 0;
+ assertThrowsInstanceOf(() => emptySet.union(setLike), TypeError);
+
+ assertEqArray(log, [
+ "[[get]]", "size",
+ "valueOf()",
+ ]);
+}
+
+// Doesn't accept Array as an input.
+assertThrowsInstanceOf(() => emptySet.union([]), TypeError);
+
+// Works with Set subclasses.
+{
+ class MySet extends Set {}
+
+ let myEmptySet = new MySet;
+
+ assertSetContainsExactOrderedItems(emptySet.union(myEmptySet), []);
+ assertSetContainsExactOrderedItems(myEmptySet.union(myEmptySet), []);
+ assertSetContainsExactOrderedItems(myEmptySet.union(emptySet), []);
+}
+
+// Doesn't access any properties on the this-value.
+{
+ let log = [];
+
+ let {proxy: setProto} = LoggingProxy(Set.prototype, log);
+
+ let set = new Set([1, 2, 3]);
+ Object.setPrototypeOf(set, setProto);
+
+ assertSetContainsExactOrderedItems(Set.prototype.union.call(set, emptySet), [1, 2, 3]);
+
+ assertEqArray(log, []);
+}
+
+// Throws a TypeError when the this-value isn't a Set object.
+for (let thisValue of [
+ null, undefined, true, "", {}, new Map, new Proxy(new Set, {}),
+]) {
+ assertThrowsInstanceOf(() => Set.prototype.union.call(thisValue, emptySet), TypeError);
+}
+
+// Doesn't return the original Set object.
+{
+ let set = new Set([1]);
+ assertEq(set.union(emptySet) !== set, true);
+ assertEq(set.union(new Set([2])) !== set, true);
+}
+
+// Test insertion order
+{
+ let set = new Set([1, 2]);
+
+ // Case 1: Input is empty.
+ assertSetContainsExactOrderedItems(set.union(new Set([])), [1, 2]);
+
+ // Case 2: Input has fewer elements.
+ assertSetContainsExactOrderedItems(set.union(new Set([3])), [1, 2, 3]);
+
+ // Case 3: Input has same number of elements.
+ assertSetContainsExactOrderedItems(set.union(new Set([3, 4])), [1, 2, 3, 4]);
+
+ // Case 4: Input has more elements.
+ assertSetContainsExactOrderedItems(set.union(new Set([3, 4, 5])), [1, 2, 3, 4, 5]);
+}
+
+// Test that the input set is copied after accessing the |next| property of the keys iterator.
+{
+ let set = new Set([1, 2, 3]);
+
+ let setLike = {
+ size: 0,
+ has() {
+ throw new Error("Unexpected call to |has| method");
+ },
+ keys() {
+ return {
+ get next() {
+ // Clear the set when getting the |next| method.
+ set.clear();
+
+ // And then add a single new key.
+ set.add(4);
+
+ return function() {
+ return {done: true};
+ };
+ }
+ };
+ },
+ };
+
+ // The result should consist of the single, newly added key.
+ assertSetContainsExactOrderedItems(set.union(setLike), [4]);
+}
+
+// Tests which modify any built-ins should appear last, because modifications may disable
+// optimised code paths.
+
+// Doesn't call the built-in |Set.prototype.{has, keys, size}| functions.
+const SetPrototypeHas = Object.getOwnPropertyDescriptor(Set.prototype, "has");
+const SetPrototypeKeys = Object.getOwnPropertyDescriptor(Set.prototype, "keys");
+const SetPrototypeSize = Object.getOwnPropertyDescriptor(Set.prototype, "size");
+
+delete Set.prototype.has;
+delete Set.prototype.keys;
+delete Set.prototype.size;
+
+try {
+ let set = new Set([1, 2, 3]);
+ let other = new SetLike([1, 2, 3]);
+ assertSetContainsExactOrderedItems(set.union(other), [1, 2, 3]);
+} finally {
+ Object.defineProperty(Set.prototype, "has", SetPrototypeHas);
+ Object.defineProperty(Set.prototype, "keys", SetPrototypeKeys);
+ Object.defineProperty(Set.prototype, "size", SetPrototypeSize);
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);