diff options
Diffstat (limited to 'js/src/jit-test/tests/collections')
148 files changed, 3165 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/collections/Array-of-1.js b/js/src/jit-test/tests/collections/Array-of-1.js new file mode 100644 index 0000000000..8dc5b7337a --- /dev/null +++ b/js/src/jit-test/tests/collections/Array-of-1.js @@ -0,0 +1,15 @@ +// Array.of makes real arrays. + +function check(a) { + assertEq(Object.getPrototypeOf(a), Array.prototype); + assertEq(Array.isArray(a), true); + a[9] = 9; + assertEq(a.length, 10); +} + +check(Array.of()); +check(Array.of(0)); +check(Array.of(0, 1, 2)); + +var f = Array.of; +check(f()); diff --git a/js/src/jit-test/tests/collections/Array-of-2.js b/js/src/jit-test/tests/collections/Array-of-2.js new file mode 100644 index 0000000000..26836eaaf3 --- /dev/null +++ b/js/src/jit-test/tests/collections/Array-of-2.js @@ -0,0 +1,14 @@ +// Array.of basics + +load(libdir + "asserts.js"); + +var a = Array.of(); +assertEq(a.length, 0); + +a = Array.of(undefined, null, 3.14, []); +assertDeepEq(a, [undefined, null, 3.14, []]); + +a = []; +for (var i = 0; i < 1000; i++) + a[i] = i; +assertDeepEq(Array.of.apply({}, a), a); diff --git a/js/src/jit-test/tests/collections/Array-of-3.js b/js/src/jit-test/tests/collections/Array-of-3.js new file mode 100644 index 0000000000..e2bcbff7a1 --- /dev/null +++ b/js/src/jit-test/tests/collections/Array-of-3.js @@ -0,0 +1,8 @@ +// Array.of does not leave holes + +load(libdir + "asserts.js"); + +assertDeepEq(Array.of(undefined), [undefined]); +assertDeepEq(Array.of(undefined, undefined), [undefined, undefined]); +assertDeepEq(Array.of.apply(this, [,,undefined]), [undefined, undefined, undefined]); +assertDeepEq(Array.of.apply(this, Array(4)), [undefined, undefined, undefined, undefined]); diff --git a/js/src/jit-test/tests/collections/Array-of-4.js b/js/src/jit-test/tests/collections/Array-of-4.js new file mode 100644 index 0000000000..0bb1c8b8f4 --- /dev/null +++ b/js/src/jit-test/tests/collections/Array-of-4.js @@ -0,0 +1,13 @@ +// Array.of does not trigger prototype setters. +// (It defines elements rather than assigning to them.) + +var status = "pass"; +Object.defineProperty(Array.prototype, "0", {set: v => status = "FAIL 1"}); +assertEq(Array.of(1)[0], 1); +assertEq(status, "pass"); + +function Bag() {} +Bag.of = Array.of; +Object.defineProperty(Bag.prototype, "0", {set: v => status = "FAIL 2"}); +assertEq(Bag.of(1)[0], 1); +assertEq(status, "pass"); diff --git a/js/src/jit-test/tests/collections/Array-of-cross-compartment.js b/js/src/jit-test/tests/collections/Array-of-cross-compartment.js new file mode 100644 index 0000000000..5872a98fd5 --- /dev/null +++ b/js/src/jit-test/tests/collections/Array-of-cross-compartment.js @@ -0,0 +1,10 @@ +// Array.of returns an object in the target compartment, not the caller's compartment. +// This rules out implementations along the lines of (...args) => args. + +var g = newGlobal(); +var ga = g.Array.of(1, 2, 3); +assertEq(ga instanceof g.Array, true); + +g.Array.of = Array.of; +var a = g.Array.of(1, 2, 3); // this-value is a wrapper of g.Array, which IsConstructor, so we call it +assertEq(ga instanceof g.Array, true); // it produces a g.Array instance diff --git a/js/src/jit-test/tests/collections/Array-of-generic-1.js b/js/src/jit-test/tests/collections/Array-of-generic-1.js new file mode 100644 index 0000000000..090a8f97c2 --- /dev/null +++ b/js/src/jit-test/tests/collections/Array-of-generic-1.js @@ -0,0 +1,25 @@ +// Array.of can be transplanted to other classes. + +load(libdir + "asserts.js"); + +var hits = 0; +function Bag() { + hits++; +} +Bag.of = Array.of; + +hits = 0; +var actual = Bag.of("zero", "one"); +assertEq(hits, 1); + +var expected = new Bag; +expected[0] = "zero"; +expected[1] = "one"; +expected.length = 2; +assertDeepEq(actual, expected); + +hits = 0; +actual = Array.of.call(Bag, "zero", "one"); +assertEq(hits, 1); +assertDeepEq(actual, expected); + diff --git a/js/src/jit-test/tests/collections/Array-of-generic-2.js b/js/src/jit-test/tests/collections/Array-of-generic-2.js new file mode 100644 index 0000000000..203e58b008 --- /dev/null +++ b/js/src/jit-test/tests/collections/Array-of-generic-2.js @@ -0,0 +1,11 @@ +// Array.of passes the number of arguments to the constructor it calls. + +var hits = 0; +function Herd(n) { + assertEq(arguments.length, 1); + assertEq(n, 5); + hits++; +} +Herd.of = Array.of; +Herd.of("sheep", "cattle", "elephants", "whales", "seals"); +assertEq(hits, 1); diff --git a/js/src/jit-test/tests/collections/Array-of-generic-3.js b/js/src/jit-test/tests/collections/Array-of-generic-3.js new file mode 100644 index 0000000000..3f5b2c3c56 --- /dev/null +++ b/js/src/jit-test/tests/collections/Array-of-generic-3.js @@ -0,0 +1,7 @@ +// |jit-test| error:TypeError +// Array.of can be transplanted to builtin constructors. + +load(libdir + "asserts.js"); + +Uint8Array.of = Array.of; +assertDeepEq(new Uint8Array.of(0x12, 0x34, 0x5678, 0x9abcdef), new Uint8Array([0x12, 0x34, 0x78, 0xef])); diff --git a/js/src/jit-test/tests/collections/Array-of-length-setter-2.js b/js/src/jit-test/tests/collections/Array-of-length-setter-2.js new file mode 100644 index 0000000000..4dc95549ef --- /dev/null +++ b/js/src/jit-test/tests/collections/Array-of-length-setter-2.js @@ -0,0 +1,13 @@ +// Array.of does a strict assignment to the new object's .length. +// The assignment is strict even if the code we're calling from is not strict. + +load(libdir + "asserts.js"); + +function Empty() {} +Empty.of = Array.of; +Object.defineProperty(Empty.prototype, "length", {get: () => 0}); + +var nothing = new Empty; +nothing.length = 2; // no exception; this is not a strict mode assignment + +assertThrowsInstanceOf(() => Empty.of(), TypeError); diff --git a/js/src/jit-test/tests/collections/Array-of-length-setter.js b/js/src/jit-test/tests/collections/Array-of-length-setter.js new file mode 100644 index 0000000000..e3d97f412f --- /dev/null +++ b/js/src/jit-test/tests/collections/Array-of-length-setter.js @@ -0,0 +1,26 @@ +// Array.of calls a "length" setter if one is present. + +var hits = 0; +var lastObj = null, lastVal = undefined; +function setter(v) { + hits++; + lastObj = this; + lastVal = v; +} + +// when the setter is on the new object +function Pack() { + Object.defineProperty(this, "length", {set: setter}); +} +Pack.of = Array.of; +var pack = Pack.of("wolves", "cards", "cigarettes", "lies"); +assertEq(lastObj, pack); +assertEq(lastVal, 4); + +// when the setter is on the new object's prototype +function Bevy() {} +Object.defineProperty(Bevy.prototype, "length", {set: setter}); +Bevy.of = Array.of; +var bevy = Bevy.of("quail"); +assertEq(lastObj, bevy); +assertEq(lastVal, 1); diff --git a/js/src/jit-test/tests/collections/Array-of-nonconfigurable-1.js b/js/src/jit-test/tests/collections/Array-of-nonconfigurable-1.js new file mode 100644 index 0000000000..d516a1a40c --- /dev/null +++ b/js/src/jit-test/tests/collections/Array-of-nonconfigurable-1.js @@ -0,0 +1,8 @@ +// If Array.of tries to overwrite a non-configurable property, it throws a TypeError. + +load(libdir + "asserts.js"); + +function C() { + Object.defineProperty(this, 0, {value: "v", configurable: false}); +} +assertThrowsInstanceOf(() => Array.of.call(C, 1, 2, 3), TypeError); diff --git a/js/src/jit-test/tests/collections/Array-of-nonconfigurable-2.js b/js/src/jit-test/tests/collections/Array-of-nonconfigurable-2.js new file mode 100644 index 0000000000..f5c783714a --- /dev/null +++ b/js/src/jit-test/tests/collections/Array-of-nonconfigurable-2.js @@ -0,0 +1,16 @@ +// Array.of does not overwrite non-configurable properties. + +load(libdir + "asserts.js"); + +var obj; +function C() { + obj = this; + Object.defineProperty(this, 0, {value: "v", configurable: false}); +} +try { Array.of.call(C, 1); } catch (e) {} +assertDeepEq(Object.getOwnPropertyDescriptor(obj, 0), { + value: "v", + writable: false, + enumerable: false, + configurable: false +}); diff --git a/js/src/jit-test/tests/collections/Array-of-ordering.js b/js/src/jit-test/tests/collections/Array-of-ordering.js new file mode 100644 index 0000000000..5906e1b498 --- /dev/null +++ b/js/src/jit-test/tests/collections/Array-of-ordering.js @@ -0,0 +1,32 @@ +// Order of Array.of operations. + +load(libdir + "asserts.js"); + +var log; + +var dstdata = []; +var dst = new Proxy(dstdata, { + defineProperty: function (t, name, desc) { + log.push(["def", name, desc.value]); + return true; + }, + set: function (t, name, value) { + log.push(["set", name, value]); + return true; + } +}); + +function Troop() { + return dst; +} +Troop.of = Array.of; + +log = []; +assertEq(Troop.of("monkeys", "baboons", "kangaroos"), dst); +assertDeepEq(log, [ + ["def", "0", "monkeys"], + ["def", "1", "baboons"], + ["def", "2", "kangaroos"], + ["set", "length", 3] +]); + diff --git a/js/src/jit-test/tests/collections/Array-of-surfaces.js b/js/src/jit-test/tests/collections/Array-of-surfaces.js new file mode 100644 index 0000000000..88edfacdb2 --- /dev/null +++ b/js/src/jit-test/tests/collections/Array-of-surfaces.js @@ -0,0 +1,14 @@ +// Check superficial features of Array.of. + +load(libdir + "asserts.js"); + +var desc = Object.getOwnPropertyDescriptor(Array, "of"); +assertEq(desc.configurable, true); +assertEq(desc.enumerable, false); +assertEq(desc.writable, true); +assertEq(Array.of.length, 0); +assertThrowsInstanceOf(() => new Array.of(), TypeError); // not a constructor + +// When the this-value passed in is not a constructor, the result is an array. +for (let v of [undefined, null, false, "cow"]) + assertEq(Array.isArray(Array.of.call(v)), true); diff --git a/js/src/jit-test/tests/collections/Map-Set-moving-gc.js b/js/src/jit-test/tests/collections/Map-Set-moving-gc.js new file mode 100644 index 0000000000..e603756dc3 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-Set-moving-gc.js @@ -0,0 +1,16 @@ +var m = new Map; +var s = new Set; + +var A = []; +for (var i = 0; i < 1024; ++i) { + var key = {i:i}; + m.set(key, i); + s.add(key); + A.push(key); +} +gc(); +for (var i in A) { + var key = A[i]; + assertEq(m.has(key), true); + assertEq(s.has(key), true); +} diff --git a/js/src/jit-test/tests/collections/Map-clear-1.js b/js/src/jit-test/tests/collections/Map-clear-1.js new file mode 100644 index 0000000000..68b08cdb22 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-clear-1.js @@ -0,0 +1,8 @@ +// Clearing an empty Map has no effect. + +var m = new Map(); +for (var i = 0; i < 2; i++) { + m.clear(); + assertEq(m.size, 0); + assertEq(m.has(undefined), false); +} diff --git a/js/src/jit-test/tests/collections/Map-clear-2.js b/js/src/jit-test/tests/collections/Map-clear-2.js new file mode 100644 index 0000000000..f9358b6f0c --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-clear-2.js @@ -0,0 +1,17 @@ +// Clearing a Map removes its entries; the Map remains usable afterwards. + +var m = new Map([["a", "b"], ["b", "c"]]); +assertEq(m.size, 2); +m.clear(); +assertEq(m.size, 0); +assertEq(m.has("a"), false); +assertEq(m.get("a"), undefined); +assertEq(m.delete("a"), false); +assertEq(m.has("b"), false); +for (var pair of m) + throw "FAIL"; // shouldn't be any pairs + +m.set("c", "d"); +assertEq(m.size, 1); +assertEq(m.has("a"), false); +assertEq(m.has("b"), false); diff --git a/js/src/jit-test/tests/collections/Map-clear-3.js b/js/src/jit-test/tests/collections/Map-clear-3.js new file mode 100644 index 0000000000..902a4cf292 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-clear-3.js @@ -0,0 +1,10 @@ +// Clearing a Map with a nontrivial number of elements works. + +var m = new Map(); +for (var i = 0; i < 100; i++) + m.set(i, i); +assertEq(m.size, i); +m.clear(); +assertEq(m.size, 0); +m.set("a", 1); +assertEq(m.get("a"), 1); diff --git a/js/src/jit-test/tests/collections/Map-clear-4.js b/js/src/jit-test/tests/collections/Map-clear-4.js new file mode 100644 index 0000000000..2925c801f8 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-clear-4.js @@ -0,0 +1,10 @@ +// Clearing a Map after deleting some entries works. + +var m = new Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]); +for (var [k, v] of m) + if (k !== "c") + m.delete(k); +m.clear(); +assertEq(m.size, 0); +assertEq(m.has("c"), false); +assertEq(m.has("d"), false); diff --git a/js/src/jit-test/tests/collections/Map-clear-5.js b/js/src/jit-test/tests/collections/Map-clear-5.js new file mode 100644 index 0000000000..74fcdd4ebe --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-clear-5.js @@ -0,0 +1,14 @@ +// Map.clear is unaffected by deleting/monkeypatching Map.prototype.{delete,iterator}. + +var data = [["a", 1], ["b", 2]]; +var m1 = new Map(data), m2 = new Map(data); + +delete Map.prototype.delete; +delete Map.prototype.iterator; +m1.clear(); +assertEq(m1.size, 0); + +Map.prototype.delete = function () { throw "FAIL"; }; +Map.prototype.iterator = function () { throw "FAIL"; }; +m2.clear(); +assertEq(m2.size, 0); diff --git a/js/src/jit-test/tests/collections/Map-clear-6.js b/js/src/jit-test/tests/collections/Map-clear-6.js new file mode 100644 index 0000000000..4df7388f59 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-clear-6.js @@ -0,0 +1,6 @@ +// Clearing a Map doesn't affect expando properties. + +var m = new Map(); +m.x = 3; +m.clear(); +assertEq(m.x, 3); diff --git a/js/src/jit-test/tests/collections/Map-clear-iterators-1.js b/js/src/jit-test/tests/collections/Map-clear-iterators-1.js new file mode 100644 index 0000000000..0372929fb9 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-clear-iterators-1.js @@ -0,0 +1,23 @@ +// A Map iterator does not visit entries removed by clear(). + +load(libdir + "iteration.js"); + +var m = new Map(); +var it = m[Symbol.iterator](); +m.clear(); +assertIteratorDone(it, undefined); + +m = new Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]); +it = m[Symbol.iterator](); +assertIteratorNext(it, ["a", 1]); +m.clear(); +assertIteratorDone(it, undefined); + +var log = ""; +m = new Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]); +for (var [k, v] of m) { + log += k + v; + if (k == "b") + m.clear(); +} +assertEq(log, "a1b2"); diff --git a/js/src/jit-test/tests/collections/Map-clear-iterators-2.js b/js/src/jit-test/tests/collections/Map-clear-iterators-2.js new file mode 100644 index 0000000000..a64b5f11cb --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-clear-iterators-2.js @@ -0,0 +1,12 @@ +// A Map iterator continues to visit entries added after a clear(). + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +var m = new Map([["a", 1]]); +var it = m[Symbol.iterator](); +assertIteratorNext(it, ["a", 1]); +m.clear(); +m.set("b", 2); +assertIteratorNext(it, ["b", 2]); +assertIteratorDone(it, undefined); diff --git a/js/src/jit-test/tests/collections/Map-constructor-1.js b/js/src/jit-test/tests/collections/Map-constructor-1.js new file mode 100644 index 0000000000..b1a55967dc --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-constructor-1.js @@ -0,0 +1,14 @@ +// The Map constructor creates an empty Map by default. + +load(libdir + "asserts.js"); + +var m = new Map(); +assertEq(m.size, 0); +m = new Map(undefined); +assertEq(m.size, 0); +m = new Map(null); +assertEq(m.size, 0); + +assertThrowsInstanceOf(() => Map(), TypeError); +assertThrowsInstanceOf(() => Map(undefined), TypeError); +assertThrowsInstanceOf(() => Map(null), TypeError); diff --git a/js/src/jit-test/tests/collections/Map-constructor-2.js b/js/src/jit-test/tests/collections/Map-constructor-2.js new file mode 100644 index 0000000000..b80a171d84 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-constructor-2.js @@ -0,0 +1,6 @@ +// The Map constructor can take an argument that is an array of pairs. + +var arr = [["zero", 0], ["one", 1], ["two", 2]]; +var m = new Map(arr); +for (var [k, v] of arr) + assertEq(m.get(k), v); diff --git a/js/src/jit-test/tests/collections/Map-constructor-3.js b/js/src/jit-test/tests/collections/Map-constructor-3.js new file mode 100644 index 0000000000..601a76188e --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-constructor-3.js @@ -0,0 +1,9 @@ +// Map can take an argument that is an array of singleton arrays. + +var arr = [["a"], ["b"], ["c"]]; +var m = new Map(arr); +assertEq(m.size, 3); +for (var [k, _] of arr) { + assertEq(m.has(k), true); + assertEq(m.get(k), undefined); +} diff --git a/js/src/jit-test/tests/collections/Map-constructor-4.js b/js/src/jit-test/tests/collections/Map-constructor-4.js new file mode 100644 index 0000000000..22c2c416be --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-constructor-4.js @@ -0,0 +1,6 @@ +// new Map(x) throws if x is not iterable (unless x is undefined). + +load(libdir + "asserts.js"); +var nonIterables = [true, 1, -0, 3.14, NaN, {}, Math, this]; +for (let k of nonIterables) + assertThrowsInstanceOf(function () { new Map(k); }, TypeError); diff --git a/js/src/jit-test/tests/collections/Map-constructor-5.js b/js/src/jit-test/tests/collections/Map-constructor-5.js new file mode 100644 index 0000000000..98d1a03ba4 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-constructor-5.js @@ -0,0 +1,15 @@ +// new Map(arr) throws if arr contains holes (or undefined values). + +load(libdir + "asserts.js"); +assertThrowsInstanceOf(function () { new Map([undefined]); }, TypeError); +assertThrowsInstanceOf(function () { new Map([null]); }, TypeError); +assertThrowsInstanceOf(function () { new Map([[0, 0], [1, 1], , [3, 3]]); }, TypeError); +assertThrowsInstanceOf(function () { new Map([[0, 0], [1, 1], ,]); }, TypeError); + +// new Map(iterable) throws if iterable doesn't have array-like objects + +assertThrowsInstanceOf(function () { new Map([1, 2, 3]); }, TypeError); +assertThrowsInstanceOf(function () { + let s = new Set([1, 2, "abc"]); + new Map(s); +}, TypeError); diff --git a/js/src/jit-test/tests/collections/Map-constructor-duplicates.js b/js/src/jit-test/tests/collections/Map-constructor-duplicates.js new file mode 100644 index 0000000000..125081bf87 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-constructor-duplicates.js @@ -0,0 +1,8 @@ +// When the argument to Map contains a key multiple times, the last value is retained. + +var arg = [["zero", 7], ["one", 1], ["two", 4], ["zero", 8], ["two", 2], ["zero", 0]]; +var m = new Map(arg); +assertEq(m.get("zero"), 0); +assertEq(m.get("one"), 1); +assertEq(m.get("two"), 2); +assertEq(m.size, 3); diff --git a/js/src/jit-test/tests/collections/Map-constructor-generator-1.js b/js/src/jit-test/tests/collections/Map-constructor-generator-1.js new file mode 100644 index 0000000000..7bda266328 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-constructor-generator-1.js @@ -0,0 +1,19 @@ +// The argument to Map can be a generator. + +var done = false; +function* data(n) { + var s = ''; + for (var i = 0; i < n; i++) { + yield [s, i]; + s += '.'; + } + done = true; +} + +var m = new Map(data(50)); +assertEq(done, true); // the constructor consumes the argument +assertEq(m.size, 50); +assertEq(m.get(""), 0); +assertEq(m.get("....."), 5); +assertEq(m.get(Array(49+1).join(".")), 49); +assertEq(m.has(undefined), false); diff --git a/js/src/jit-test/tests/collections/Map-constructor-generator-3.js b/js/src/jit-test/tests/collections/Map-constructor-generator-3.js new file mode 100644 index 0000000000..3d555a0747 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-constructor-generator-3.js @@ -0,0 +1,7 @@ +// The argument to Map may be a generator-iterator that produces no values. + +function* none() { + if (0) yield 0; +} +var m = new Map(none()); +assertEq(m.size, 0); diff --git a/js/src/jit-test/tests/collections/Map-constructor-generator-exception.js b/js/src/jit-test/tests/collections/Map-constructor-generator-exception.js new file mode 100644 index 0000000000..4c9089bdf2 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-constructor-generator-exception.js @@ -0,0 +1,12 @@ +// Iterating over the argument to Map can throw. The exception is propagated. + +load(libdir + "asserts.js"); + +function* data2() { + yield [{}, "XR22/Z"]; + yield [{}, "23D-BN"]; + throw "oops"; +} + +var it = data2(); +assertThrowsValue(function () { new Map(it); }, "oops"); diff --git a/js/src/jit-test/tests/collections/Map-constructor-set.js b/js/src/jit-test/tests/collections/Map-constructor-set.js new file mode 100644 index 0000000000..2b6e241525 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-constructor-set.js @@ -0,0 +1,204 @@ +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +var k1 = {}; +var v1 = 42; +var k2 = {}; +var v2 = 42; +var k3 = {}; +var v3 = 43; +var k4 = {}; +var v4 = 44; + +function test_patched() { + let orig = Map.prototype.set; + + // If adder is modified, constructor should call it. + var called = false; + + Map.prototype.set = function(k, v) { + assertEq(k, k1); + assertEq(v, v1); + orig.call(this, k2, v2); + called = true; + }; + + var arr = [[k1, v1]]; + + var m = new Map(arr); + + assertEq(called, true); + assertEq(m.size, 1); + assertEq(m.has(k1), false); + assertEq(m.has(k2), true); + assertEq(m.get(k1), undefined); + assertEq(m.get(k2), v2); + + Map.prototype.set = orig; +} + +function test_proxy1() { + let orig = Map.prototype.set; + + // If adder is modified, constructor should call it. + var called = false; + + Map.prototype.set = new Proxy(function(k, v) { + assertEq(k, k1); + assertEq(v, v1); + orig.call(this, k2, v2); + called = true; + }, {}); + + var arr = [[k1, v1]]; + + var m = new Map(arr); + + assertEq(called, true); + assertEq(m.size, 1); + assertEq(m.has(k1), false); + assertEq(m.has(k2), true); + assertEq(m.get(k1), undefined); + assertEq(m.get(k2), v2); + + Map.prototype.set = orig; +} + +function test_proxy2() { + let orig = Map.prototype.set; + + // If adder is modified, constructor should call it. + var called = false; + + Map.prototype.set = new Proxy(function() { + }, { + apply: function(target, that, args) { + var [k, v] = args; + assertEq(k, k1); + assertEq(v, v1); + orig.call(that, k2, v2); + called = true; + } + }); + + var arr = [[k1, v1]]; + + var m = new Map(arr); + + assertEq(called, true); + assertEq(m.size, 1); + assertEq(m.has(k1), false); + assertEq(m.has(k2), true); + assertEq(m.get(k1), undefined); + assertEq(m.get(k2), v2); + + Map.prototype.set = orig; +} + +function test_change1() { + let orig = Map.prototype.set; + + // Change to adder in GetIterator(..) call should be ignored. + var called = false; + var modified = false; + + var arr = [[k1, v1]]; + + var proxy_arr = new Proxy(arr, { + get: function(target, name) { + if (name == Symbol.iterator) { + modified = true; + Map.prototype.set = function() { + called = true; + }; + } + return target[name]; + } + }); + + var m = new Map(proxy_arr); + + assertEq(modified, true); + assertEq(called, false); + assertEq(m.size, 1); + assertEq(m.has(k1), true); + assertEq(m.has(k2), false); + assertEq(m.get(k1), v1); + assertEq(m.get(k2), undefined); + + Map.prototype.set = orig; +} + +function test_change2() { + let orig = Map.prototype.set; + + // Change to adder in adder(...) call should be ignored. + var called = false; + var count = 0; + + Map.prototype.set = function(k, v) { + if (count == 0) { + assertEq(k, k1); + assertEq(v, v1); + orig.call(this, k3, v3); + Map.prototype.set = function() { + called = true; + }; + count = 1; + } else { + assertEq(k, k2); + assertEq(v, v2); + orig.call(this, k4, v4); + count = 2; + } + }; + + var arr = [[k1, v1], [k2, v2]]; + + var m = new Map(arr); + + assertEq(called, false); + assertEq(count, 2); + assertEq(m.size, 2); + assertEq(m.has(k1), false); + assertEq(m.has(k2), false); + assertEq(m.has(k3), true); + assertEq(m.has(k4), true); + assertEq(m.get(k1), undefined); + assertEq(m.get(k2), undefined); + assertEq(m.get(k3), v3); + assertEq(m.get(k4), v4); + + Map.prototype.set = orig; +} + +function test_error() { + let orig = Map.prototype.set; + + var arr = [[k1, v1]]; + + // Map should throw TypeError if adder is not callable. + Map.prototype.set = null; + assertThrowsInstanceOf(() => new Map(arr), TypeError); + Map.prototype.set = {}; + assertThrowsInstanceOf(() => new Map(arr), TypeError); + + // Map should propagate error thrown by adder. + Map.prototype.set = function() { + throw SyntaxError(); + }; + assertThrowsInstanceOf(() => new Map(arr), SyntaxError); + + Map.prototype.set = orig; +} + +function test() { + test_patched(); + test_proxy1(); + test_proxy2(); + test_change1(); + test_change2(); + test_error(); +} + +test(); diff --git a/js/src/jit-test/tests/collections/Map-delete-size.js b/js/src/jit-test/tests/collections/Map-delete-size.js new file mode 100644 index 0000000000..c2537e0dc1 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-delete-size.js @@ -0,0 +1,14 @@ +// map.delete(k) decrements the map size iff an entry was actually removed. + +var m = new Map(); +m.delete(3); +assertEq(m.size, 0); +m.set({}, 'ok'); +m.set(Math, 'ok'); +assertEq(m.size, 2); +m.delete({}); +assertEq(m.size, 2); +m.delete(Math); +assertEq(m.size, 1); +m.delete(Math); +assertEq(m.size, 1); diff --git a/js/src/jit-test/tests/collections/Map-delete.js b/js/src/jit-test/tests/collections/Map-delete.js new file mode 100644 index 0000000000..d200eb873f --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-delete.js @@ -0,0 +1,18 @@ +// Map.prototype.delete works whether the key is present or not. + +var m = new Map; +var key = {}; + +// when the map is new +assertEq(m.delete(key), false); +assertEq(m.has(key), false); + +// when the key is present +assertEq(m.set(key, 'x'), m); +assertEq(m.delete(key), true); +assertEq(m.has(key), false); +assertEq(m.get(key), undefined); + +// when the key has already been deleted +assertEq(m.delete(key), false); +assertEq(m.has(key), false); diff --git a/js/src/jit-test/tests/collections/Map-forEach.js b/js/src/jit-test/tests/collections/Map-forEach.js new file mode 100644 index 0000000000..6db15f4c31 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-forEach.js @@ -0,0 +1,58 @@ +/* test Map.prototype.forEach */ + +load(libdir + 'asserts.js'); +load(libdir + 'iteration.js'); + +// testing success conditions of Map.prototype.forEach + +var testMap = new Map(); + +function callback(value, key, map) { + testMap.set(key, value); + assertEq(map.has(key), true); + assertEq(map.get(key), value); +} + +var initialMap = new Map([['a', 1], ['b', 2.3], [false, undefined]]); +initialMap.forEach(callback); + +// test that both the Maps are equal and are in same order +var iterator = initialMap[Symbol.iterator](); +var count = 0; +for (var [k, v] of testMap) { + assertEq(initialMap.has(k), true); + assertEq(initialMap.get(k), testMap.get(k)); + assertIteratorNext(iterator, [k, testMap.get(k)]); + count++; +} + +//check both the Maps we have are equal in size +assertEq(initialMap.size, testMap.size); +assertEq(initialMap.size, count); + +var x = { abc: 'test'}; +function callback2(value, key, map) { + assertEq(x, this); +} +initialMap = new Map([['a', 1]]); +initialMap.forEach(callback2, x); + +// testing failure conditions of Map.prototype.forEach + +var s = new Set([1, 2, 3]); +assertThrowsInstanceOf(function() { + Map.prototype.forEach.call(s, callback); +}, TypeError, "Map.prototype.forEach should raise TypeError if not used on a Map"); + +var fn = 2; +assertThrowsInstanceOf(function() { + initialMap.forEach(fn); +}, TypeError, "Map.prototype.forEach should raise TypeError if callback is not a function"); + +// testing that Map#forEach uses internal next() function. + +var m = new Map([["one", 1]]); +Object.getPrototypeOf(m[Symbol.iterator]()).next = function () { throw "FAIL"; }; +assertThrowsValue(function () { + m.forEach(function () { throw Math; }); +}, Math, "Map.prototype.forEach should use intrinsic next method."); diff --git a/js/src/jit-test/tests/collections/Map-gc-4.js b/js/src/jit-test/tests/collections/Map-gc-4.js new file mode 100644 index 0000000000..5597ec1bbf --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-gc-4.js @@ -0,0 +1,15 @@ +// Bug 770954. + +gczeal(4); +var s = new Set; +s.add(-0); +s.add(0); +assertEq(s.delete(-0), true); +assertEq(s.has(0), (false )); +assertEq(s.has(-0), false); +var m = new Map; +m.set(-0, 'x'); +assertEq(m.has(0), true); +assertEq(m.get(0), 'x'); +assertEq(m.has(-0), true); +assertEq(m.delete(-0), true); diff --git a/js/src/jit-test/tests/collections/Map-get.js b/js/src/jit-test/tests/collections/Map-get.js new file mode 100644 index 0000000000..62bfbcabe3 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-get.js @@ -0,0 +1,41 @@ +// Map.prototype.get and .has basically work +var m = new Map; + +function rope() { + var s = "s"; + for (var i = 0; i < 16; i++) + s += s; + return s; +} + +var keys = [undefined, null, true, false, + 0, 1, 65535, 65536, 2147483647, 2147483648, 4294967295, 4294967296, + -1, -65536, -2147483648, + 0.5, Math.sqrt(81), Math.PI, + Number.MAX_VALUE, -Number.MAX_VALUE, Number.MIN_VALUE, -Number.MIN_VALUE, + NaN, Infinity, -Infinity, + "", "\0", "a", "ab", "abcdefg", rope(), + {}, [], /a*b/, Object.prototype, Object]; + +for (var i = 0; i < keys.length; i++) { + // without being set + var key = keys[i]; + assertEq(m.has(key), false); + assertEq(m.get(key), undefined); + + // after being set + var v = {}; + assertEq(m.set(key, v), m); + assertEq(m.has(key), true); + assertEq(m.get(key), v); + + // after being deleted + assertEq(m.delete(key), true); + assertEq(m.has(key), false); + assertEq(m.get(key), undefined); + + m.set(key, v); +} + +for (var i = 0; i < keys.length; i++) + assertEq(m.has(keys[i]), true); diff --git a/js/src/jit-test/tests/collections/Map-iterator-1.js b/js/src/jit-test/tests/collections/Map-iterator-1.js new file mode 100644 index 0000000000..5e9d685737 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-iterator-1.js @@ -0,0 +1,11 @@ +// for-of can be used to iterate over a Map twice. + +var map = new Map([['a', 0], ['b', 1], ['c', 2]]); +var log = ''; + +for (let i = 0; i < 2; i++) { + for (let [k, v] of map) + log += k + v; + log += ';' +} +assertEq(log, 'a0b1c2;a0b1c2;'); diff --git a/js/src/jit-test/tests/collections/Map-iterator-2.js b/js/src/jit-test/tests/collections/Map-iterator-2.js new file mode 100644 index 0000000000..8a648a273a --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-iterator-2.js @@ -0,0 +1,11 @@ +// Nested for-of loops can iterate over a Map. + +var map = new Map([['a', 0], ['b', 1]]); +var log = ''; +for (let [k0, v0] of map) { + log += k0 + v0 + ':' + for (let [k1, v1] of map) + log += k1 + v1; + log += ';' +}; +assertEq(log, 'a0:a0b1;b1:a0b1;'); diff --git a/js/src/jit-test/tests/collections/Map-iterator-add-1.js b/js/src/jit-test/tests/collections/Map-iterator-add-1.js new file mode 100644 index 0000000000..495ad62882 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-iterator-add-1.js @@ -0,0 +1,15 @@ +// map.iterator() is live: entries added during iteration are visited. + +var map = new Map(); +function force(k) { + if (!map.has(k) && k >= 0) + map.set(k, k - 1); +} +force(5); +var log = ''; +for (let [k, v] of map) { + log += k + ';'; + force(v); +} +assertEq(log, '5;4;3;2;1;0;'); +assertEq(map.size, 6); diff --git a/js/src/jit-test/tests/collections/Map-iterator-add-2.js b/js/src/jit-test/tests/collections/Map-iterator-add-2.js new file mode 100644 index 0000000000..fce3dac6ed --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-iterator-add-2.js @@ -0,0 +1,10 @@ +// A Map iterator does not iterate over new entries added after it throws StopIteration. + +load(libdir + "iteration.js"); + +var map = new Map(); +var iter0 = map[Symbol.iterator](), iter1 = map[Symbol.iterator](); +assertIteratorDone(iter0, undefined); // closes iter0 +map.set(1, 2); +assertIteratorDone(iter0, undefined); // already closed +assertIteratorNext(iter1, [1, 2]); // was not yet closed diff --git a/js/src/jit-test/tests/collections/Map-iterator-add-remove.js b/js/src/jit-test/tests/collections/Map-iterator-add-remove.js new file mode 100644 index 0000000000..b71bfd18cf --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-iterator-add-remove.js @@ -0,0 +1,14 @@ +// Removing and re-adding entries while an iterator is live causes the iterator to visit them again. + +var map = new Map([['a', 1]]); +var n = 5; +for (let [k, v] of map) { + assertEq(k, 'a'); + assertEq(v, 1); + if (n === 0) + break; + map.delete('a'); + map.set('a', 1); + n--; +} +assertEq(n, 0); diff --git a/js/src/jit-test/tests/collections/Map-iterator-already-done.js b/js/src/jit-test/tests/collections/Map-iterator-already-done.js new file mode 100644 index 0000000000..3e3db75436 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-iterator-already-done.js @@ -0,0 +1,12 @@ +let a = new Map(); +for (let i = 0; i < 1000; i++) + a.set(i, i); + +function f() { + let iter = a.entries(); + while (!iter.next().done) {} + iter.next(); +} + +for (let i = 0; i < 10; i++) + f(); diff --git a/js/src/jit-test/tests/collections/Map-iterator-order.js b/js/src/jit-test/tests/collections/Map-iterator-order.js new file mode 100644 index 0000000000..55eb30360d --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-iterator-order.js @@ -0,0 +1,15 @@ +// Map iterators produces entries in the order they were inserted. + +load(libdir + "eqArrayHelper.js"); + +var map = new Map(); +for (var i = 7; i !== 1; i = i * 7 % 1117) + map.set("" + i, i); +assertEq(map.size, 557); + +i = 7; +for (var pair of map) { + assertEqArray(pair, ["" + i, i]); + i = i * 7 % 1117; +} +assertEq(i, 1); diff --git a/js/src/jit-test/tests/collections/Map-iterator-pairs-1.js b/js/src/jit-test/tests/collections/Map-iterator-pairs-1.js new file mode 100644 index 0000000000..2a8e49d89b --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-iterator-pairs-1.js @@ -0,0 +1,17 @@ +// mapiter.next() returns an actual array. + +load(libdir + "iteration.js"); + +var key = {}; +var map = new Map([[key, 'value']]); +var entry = map[Symbol.iterator]().next().value; +assertEq(Array.isArray(entry), true); +assertEq(Object.getPrototypeOf(entry), Array.prototype); +assertEq(Object.isExtensible(entry), true); + +assertEq(entry.length, 2); +var names = Object.getOwnPropertyNames(entry).sort(); +assertEq(names.length, 3); +assertEq(names.join(","), "0,1,length"); +assertEq(entry[0], key); +assertEq(entry[1], 'value'); diff --git a/js/src/jit-test/tests/collections/Map-iterator-pairs-2.js b/js/src/jit-test/tests/collections/Map-iterator-pairs-2.js new file mode 100644 index 0000000000..4d4e66f00c --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-iterator-pairs-2.js @@ -0,0 +1,13 @@ +// mapiter.next() returns a fresh array each time. + +load(libdir + "iteration.js"); + +var map = new Map([['a', 1], ['b', 2]]); +var iter = map[Symbol.iterator](); +var a = iter.next(), b = iter.next(); +assertIteratorResult(a, ['a', 1], false); +assertIteratorResult(b, ['b', 2], false); +assertEq(a.value !== b.value, true); +var a1 = map[Symbol.iterator](); +assertIteratorNext(a1, ['a', 1]); +assertEq(a.value !== a1.value, true); diff --git a/js/src/jit-test/tests/collections/Map-iterator-pairs-3.js b/js/src/jit-test/tests/collections/Map-iterator-pairs-3.js new file mode 100644 index 0000000000..738215b791 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-iterator-pairs-3.js @@ -0,0 +1,13 @@ +// Modifying an array returned by mapiter.next() does not modify the Map. + +load(libdir + "iteration.js"); + +var map = new Map([['a', 1]]); +var res = map[Symbol.iterator]().next(); +assertIteratorResult(res, ['a', 1], false); +res.value[0] = 'b'; +res.value[1] = 2; +assertIteratorResult(res, ['b', 2], false); +assertEq(map.get('a'), 1); +assertEq(map.has('b'), false); +assertEq(map.size, 1); diff --git a/js/src/jit-test/tests/collections/Map-iterator-proxies-1.js b/js/src/jit-test/tests/collections/Map-iterator-proxies-1.js new file mode 100644 index 0000000000..545d80b6ac --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-iterator-proxies-1.js @@ -0,0 +1,8 @@ +// for-of works on a cross-compartment wrapper of a Map. + +var g = newGlobal(); +var mw = g.eval("new Map([['a', 1], ['b', 2]])"); +var log = ''; +for (let [k, v] of mw) + log += k + v; +assertEq(log, "a1b2"); diff --git a/js/src/jit-test/tests/collections/Map-iterator-proxies-2.js b/js/src/jit-test/tests/collections/Map-iterator-proxies-2.js new file mode 100644 index 0000000000..9b1c8bb985 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-iterator-proxies-2.js @@ -0,0 +1,21 @@ +// map.iterator() and iter.next() are non-generic but work on cross-compartment wrappers. + +load(libdir + "asserts.js"); +load(libdir + "eqArrayHelper.js"); +load(libdir + "iteration.js"); + +var g = newGlobal(); + +var iterator_fn = Map.prototype[Symbol.iterator]; +assertThrowsInstanceOf(function () { iterator_fn.call({}); }, TypeError); +assertThrowsInstanceOf(function () { iterator_fn.call(new Set()); }, TypeError); +var mapw = g.eval("new Map([['x', 1], ['y', 2]])"); +assertEqArray(iterator_fn.call(mapw).next().value, ["x", 1]); + +var next_fn = (new Map())[Symbol.iterator]().next; +assertThrowsInstanceOf(function () { next_fn.call({}); }, TypeError); +assertThrowsInstanceOf(function () { next_fn.call((new Set())[Symbol.iterator]()); }, TypeError); +var iterw = mapw[Symbol.iterator](); +assertEqArray(next_fn.call(iterw).value, ["x", 1]); +assertEqArray(next_fn.call(iterw).value, ["y", 2]); +assertEq(next_fn.call(iterw).done, true); diff --git a/js/src/jit-test/tests/collections/Map-iterator-remove-1.js b/js/src/jit-test/tests/collections/Map-iterator-remove-1.js new file mode 100644 index 0000000000..d138a4423d --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-iterator-remove-1.js @@ -0,0 +1,40 @@ +// A map iterator can cope with removing the current entry. + +function test(pairs) { + print(JSON.stringify(pairs)); + var map = new Map(pairs); + + var all_keys = ''; + var false_keys = ''; + for (let [k, v] of map) { + all_keys += k; + if (!v) + false_keys += k; + } + + var log = ''; + for (let [k, remove] of map) { + log += k; + if (remove) + map.delete(k); + } + assertEq(log, all_keys); + + var remaining_keys = [...map].map(([k]) => k).join(''); + assertEq(remaining_keys, false_keys); +} + +// removing the only entry +test([['a', true]]); + +// removing the first entry +test([['a', true], ['b', false], ['c', false]]); + +// removing a middle entry +test([['a', false], ['b', true], ['c', false]]); + +// removing the last entry +test([['a', false], ['b', false], ['c', true]]); + +// removing all entries +test([['a', true], ['b', true], ['c', true]]); diff --git a/js/src/jit-test/tests/collections/Map-iterator-remove-2.js b/js/src/jit-test/tests/collections/Map-iterator-remove-2.js new file mode 100644 index 0000000000..d49efa1c88 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-iterator-remove-2.js @@ -0,0 +1,13 @@ +// A map iterator can cope with removing the next entry. + +load(libdir + "iteration.js"); + +var map = new Map([['a', 0], ['b', 1], ['c', 2], ['d', 3]]); +var iter = map[Symbol.iterator](); +var log = ''; +for (let [k, v] of iter) { + log += k + v; + if (k === 'b') + map.delete('c'); +} +assertEq(log, 'a0b1d3'); diff --git a/js/src/jit-test/tests/collections/Map-iterator-remove-3.js b/js/src/jit-test/tests/collections/Map-iterator-remove-3.js new file mode 100644 index 0000000000..0b17dcfec8 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-iterator-remove-3.js @@ -0,0 +1,13 @@ +// A map iterator can cope with removing the next entry, then the current entry. + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +var map = new Map([['a', 0], ['b', 1], ['c', 2], ['d', 3]]); +var iter = map[Symbol.iterator](); +assertIteratorNext(iter, ['a', 0]); +assertIteratorNext(iter, ['b', 1]); +map.delete('c'); +map.delete('b'); +assertIteratorNext(iter, ['d', 3]); +assertIteratorDone(iter, undefined); diff --git a/js/src/jit-test/tests/collections/Map-iterator-remove-4.js b/js/src/jit-test/tests/collections/Map-iterator-remove-4.js new file mode 100644 index 0000000000..7739000137 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-iterator-remove-4.js @@ -0,0 +1,31 @@ +// Multiple live iterators on the same Map can cope with removing entries. + +load(libdir + "iteration.js"); + +// Make a map. +var map = new Map(); +var SIZE = 7; +for (var j = 0; j < SIZE; j++) + map.set(j, j); + +// Make lots of iterators pointing to entry 2 of the map. +var NITERS = 5; +var iters = []; +for (var i = 0; i < NITERS; i++) { + var iter = map[Symbol.iterator](); + assertIteratorNext(iter, [0, 0]); + assertIteratorNext(iter, [1, 1]); + iters[i] = iter; +} + +// Remove half of the map entries. +for (var j = 0; j < SIZE; j += 2) + map.delete(j); + +// Make sure all the iterators still work. +for (var i = 0; i < NITERS; i++) { + var iter = iters[i]; + for (var j = 3; j < SIZE; j += 2) + assertIteratorNext(iter, [j, j]); + assertIteratorDone(iter, undefined); +} diff --git a/js/src/jit-test/tests/collections/Map-iterator-remove-5.js b/js/src/jit-test/tests/collections/Map-iterator-remove-5.js new file mode 100644 index 0000000000..d5e3c26b2e --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-iterator-remove-5.js @@ -0,0 +1,22 @@ +// Removing a Map entry already visited by an iterator does not cause any +// entries to be skipped. + +var map = new Map(); +for (var i = 0; i < 20; i++) + map.set(String.fromCharCode('A'.charCodeAt(0) + i), i); + +var log = ''; +for (var [k, v] of map) { + log += k; + if (v % 5 === 4) { + // Delete all entries preceding this one. + for (let [k1, v1] of map) { + if (k1 === k) + break; + map.delete(k1); + } + } +} +assertEq(log, 'ABCDEFGHIJKLMNOPQRST'); +assertEq(map.size, 1); // Only the last entry remains. +assertEq(map.get('T'), 19); diff --git a/js/src/jit-test/tests/collections/Map-iterator-remove-6.js b/js/src/jit-test/tests/collections/Map-iterator-remove-6.js new file mode 100644 index 0000000000..9fc6f1e56e --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-iterator-remove-6.js @@ -0,0 +1,21 @@ +// Removing many Map entries does not cause a live iterator to skip any of the +// entries that were not removed. (Compacting a Map must not be observable to +// script.) + +load(libdir + "iteration.js"); + +var map = new Map(); +for (var i = 0; i < 32; i++) + map.set(i, i); +var iter = map[Symbol.iterator](); +assertIteratorNext(iter, [0, 0]); +for (var i = 0; i < 30; i++) + map.delete(i); +assertEq(map.size, 2); +for (var i = 32; i < 100; i++) + map.set(i, i); // eventually triggers compaction + +for (var i = 30; i < 100; i++) + assertIteratorNext(iter, [i, i]); + +assertIteratorDone(iter, undefined); diff --git a/js/src/jit-test/tests/collections/Map-iterators-3.js b/js/src/jit-test/tests/collections/Map-iterators-3.js new file mode 100644 index 0000000000..d1aac03f84 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-iterators-3.js @@ -0,0 +1,10 @@ +// A closed Map iterator does not visit new entries added after a clear(). + +load(libdir + "iteration.js"); + +var m = new Map(); +var it = m[Symbol.iterator](); +assertIteratorDone(it, undefined); // close the iterator +m.clear(); +m.set("a", 1); +assertIteratorDone(it, undefined); // iterator still closed diff --git a/js/src/jit-test/tests/collections/Map-scale.js b/js/src/jit-test/tests/collections/Map-scale.js new file mode 100644 index 0000000000..a456d1d34d --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-scale.js @@ -0,0 +1,8 @@ +// Maps can hold at least 64K values. + +var N = 1 << 16; +var m = new Map; +for (var i = 0; i < N; i++) + assertEq(m.set(i, i), m); +for (var i = 0; i < N; i++) + assertEq(m.get(i), i); diff --git a/js/src/jit-test/tests/collections/Map-set-returns-this.js b/js/src/jit-test/tests/collections/Map-set-returns-this.js new file mode 100644 index 0000000000..2120b09a35 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-set-returns-this.js @@ -0,0 +1,7 @@ +// Bug 1031632 - Map.prototype.set, WeakMap.prototype.set and +// Set.prototype.add should be chainable + +var m = new Map(); +assertEq(m.set('oof', 'RAB'), m); +var a = m.set('foo', 'BAR').get('foo'); +assertEq(a, 'BAR'); diff --git a/js/src/jit-test/tests/collections/Map-set-size.js b/js/src/jit-test/tests/collections/Map-set-size.js new file mode 100644 index 0000000000..3c4a13c85e --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-set-size.js @@ -0,0 +1,14 @@ +// map.set(k, v) increments the map size iff map didn't already have an entry for k. + +var m = new Map(); +m.set('a', 0); +assertEq(m.size, 1); +m.set('a', 0); +assertEq(m.size, 1); +m.set('a', undefined); +assertEq(m.size, 1); + +m.set('b', 2); +assertEq(m.size, 2); +m.set('a', 1); +assertEq(m.size, 2); diff --git a/js/src/jit-test/tests/collections/Map-set-undefined.js b/js/src/jit-test/tests/collections/Map-set-undefined.js new file mode 100644 index 0000000000..44ad134a99 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-set-undefined.js @@ -0,0 +1,15 @@ +// Setting a Map key to undefined, or a missing argument, isn't the same as deleting it. + +var m = new Map; +m.set(42, undefined); +assertEq(m.has(42), true); +assertEq(m.get(42), undefined); + +m.set(42, "wrong"); +m.set(42); +assertEq(m.has(42), true); +assertEq(m.get(42), undefined); + +m.set(); +assertEq(m.has(undefined), true); +assertEq(m.get(undefined), undefined); diff --git a/js/src/jit-test/tests/collections/Map-size.js b/js/src/jit-test/tests/collections/Map-size.js new file mode 100644 index 0000000000..12cb6e7358 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-size.js @@ -0,0 +1,6 @@ +// Each Map has its own size. + +var m1 = new Map(), m2 = new Map(); +m1.set("x", 3); +assertEq(m1.size, 1); +assertEq(m2.size, 0); diff --git a/js/src/jit-test/tests/collections/Map-surfaces-1.js b/js/src/jit-test/tests/collections/Map-surfaces-1.js new file mode 100644 index 0000000000..827dd7b1ef --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-surfaces-1.js @@ -0,0 +1,48 @@ +// Map surfaces + +load(libdir + "iteration.js"); + +var desc = Object.getOwnPropertyDescriptor(this, "Map"); +assertEq(desc.enumerable, false); +assertEq(desc.configurable, true); +assertEq(desc.writable, true); + +assertEq(typeof Map, 'function'); +assertEq(Object.keys(Map).length, 0); +assertEq(Map.length, 0); +assertEq(Map.name, "Map"); + +assertEq(Object.getPrototypeOf(Map.prototype), Object.prototype); +assertEq(Object.prototype.toString.call(Map.prototype), "[object Map]"); +assertEq(Object.prototype.toString.call(new Map()), "[object Map]"); +assertEq(Object.keys(Map.prototype).join(), ""); +assertEq(Map.prototype.constructor, Map); + +function checkMethod(name, arity) { + var desc = Object.getOwnPropertyDescriptor(Map.prototype, name); + assertEq(desc.enumerable, false); + assertEq(desc.configurable, true); + assertEq(desc.writable, true); + assertEq(typeof desc.value, 'function'); + assertEq(desc.value.name, name); + assertEq(desc.value.length, arity); +} + +checkMethod("get", 1); +checkMethod("has", 1); +checkMethod("set", 2); +checkMethod("delete", 1); +checkMethod("keys", 0); +checkMethod("values", 0); +checkMethod("entries", 0); + +var desc = Object.getOwnPropertyDescriptor(Map.prototype, "size"); +assertEq(desc.enumerable, false); +assertEq(desc.configurable, true); +assertEq(typeof desc.get, 'function'); +assertEq(desc.get.length, 0); +assertEq(desc.set, undefined); +checkMethod("clear", 0); + +// Map.prototype[@@iterator] and .entries are the same function object. +assertEq(Map.prototype[Symbol.iterator], Map.prototype.entries); diff --git a/js/src/jit-test/tests/collections/Map-surfaces-2.js b/js/src/jit-test/tests/collections/Map-surfaces-2.js new file mode 100644 index 0000000000..7fb638b623 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-surfaces-2.js @@ -0,0 +1,29 @@ +// Map methods throw when passed a this-value that isn't a Map. + +load(libdir + "asserts.js"); + +function testcase(obj, fn, ...args) { + assertEq(typeof fn, "function"); + assertThrowsInstanceOf(function () { fn.apply(obj, args); }, TypeError); +} + +var Map_size_getter = Object.getOwnPropertyDescriptor(Map.prototype, "size").get; + +function test(obj) { + testcase(obj, Map.prototype.get, "x"); + testcase(obj, Map.prototype.has, "x"); + testcase(obj, Map.prototype.set, "x", 1); + testcase(obj, Map.prototype.delete, "x"); + testcase(obj, Map.prototype.clear); + testcase(obj, Map.prototype.keys); + testcase(obj, Map.prototype.values); + testcase(obj, Map.prototype.entries); + testcase(obj, Map_size_getter); +} + +test(Map.prototype); +test(Object.create(new Map)); +test(new Set()); +test({}); +test(null); +test(undefined); diff --git a/js/src/jit-test/tests/collections/Map-surfaces-3.js b/js/src/jit-test/tests/collections/Map-surfaces-3.js new file mode 100644 index 0000000000..41e072c91b --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-surfaces-3.js @@ -0,0 +1,14 @@ +// Map methods work when arguments are omitted. + +var m = new Map; +assertEq(m.has(), false); +assertEq(m.get(), undefined); +assertEq(m.delete(), false); +assertEq(m.has(), false); +assertEq(m.get(), undefined); +assertEq(m.set(), m); +assertEq(m.has(), true); +assertEq(m.get(), undefined); +assertEq(m.delete(), true); +assertEq(m.has(), false); +assertEq(m.get(), undefined); diff --git a/js/src/jit-test/tests/collections/Map-values-1.js b/js/src/jit-test/tests/collections/Map-values-1.js new file mode 100644 index 0000000000..8a31b59076 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-values-1.js @@ -0,0 +1,14 @@ +// map.keys(), .values(), and .entries() on an empty map produce empty iterators. + +var m = new Map(); +var ki = m.keys(), vi = m.values(), ei = m.entries(); +var p = Object.getPrototypeOf(ki) +assertEq(Object.getPrototypeOf(vi), p); +assertEq(Object.getPrototypeOf(ei), p); + +for (let k of ki) + throw "FAIL"; +for (let v of vi) + throw "FAIL"; +for (let [k, v] of ei) + throw "FAIL"; diff --git a/js/src/jit-test/tests/collections/Map-values-2.js b/js/src/jit-test/tests/collections/Map-values-2.js new file mode 100644 index 0000000000..d3bd192e39 --- /dev/null +++ b/js/src/jit-test/tests/collections/Map-values-2.js @@ -0,0 +1,18 @@ +// map.keys() and map.values() return iterators over the key or the value, +// respectively, of each key-value pair in the map. + +load(libdir + "iteration.js"); + +var data = [["one", 1], ["two", 2], ["three", 3], ["four", 4]]; +var m = new Map(data); + +var ki = m.keys(); +assertIteratorNext(ki, "one"); +assertIteratorNext(ki, "two"); +assertIteratorNext(ki, "three"); +assertIteratorNext(ki, "four"); +assertIteratorDone(ki, undefined); + +assertDeepEq([...m.keys()], ["one", "two", "three", "four"]); +assertDeepEq([...m.values()], [1, 2, 3, 4]); +assertDeepEq([...m.entries()], data); diff --git a/js/src/jit-test/tests/collections/Set-add-returns-this.js b/js/src/jit-test/tests/collections/Set-add-returns-this.js new file mode 100644 index 0000000000..88806b8d87 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-add-returns-this.js @@ -0,0 +1,7 @@ +// Bug 1031632 - Map.prototype.set, WeakMap.prototype.set and +// Set.prototype.add should be chainable + +var s = new Set(); +assertEq(s.add('BAR'), s); +var b = s.add('foo').has('foo'); +assertEq(b, true); diff --git a/js/src/jit-test/tests/collections/Set-add-size.js b/js/src/jit-test/tests/collections/Set-add-size.js new file mode 100644 index 0000000000..dfd131db67 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-add-size.js @@ -0,0 +1,11 @@ +// set.add(v) increments set.size iff the set did not already contain v. + +var s = new Set(); +for (var i = 0; i < 10; i++) { + assertEq(s.size, i); + s.add(i); +} +for (var i = 0; i < 10; i++) { + assertEq(s.size, 10); + s.add(i); +} diff --git a/js/src/jit-test/tests/collections/Set-clear-1.js b/js/src/jit-test/tests/collections/Set-clear-1.js new file mode 100644 index 0000000000..ce6bc19526 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-clear-1.js @@ -0,0 +1,8 @@ +// Clearing an empty Set has no effect. + +var s = new Set(); +for (var i = 0; i < 2; i++) { + s.clear(); + assertEq(s.size, 0); + assertEq(s.has(undefined), false); +} diff --git a/js/src/jit-test/tests/collections/Set-clear-2.js b/js/src/jit-test/tests/collections/Set-clear-2.js new file mode 100644 index 0000000000..f3bbd77da0 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-clear-2.js @@ -0,0 +1,16 @@ +// Clearing a Set removes its elements; the Set remains usable afterwards. + +var s = new Set(["x", "y", "z", "z", "y"]); +assertEq(s.size, 3); +s.clear(); +assertEq(s.size, 0); +assertEq(s.has("x"), false); +assertEq(s.delete("x"), false); +assertEq(s.has("z"), false); +for (var v of s) + throw "FAIL"; // shouldn't be any elements + +s.add("y"); +assertEq(s.size, 1); +assertEq(s.has("x"), false); +assertEq(s.has("z"), false); diff --git a/js/src/jit-test/tests/collections/Set-clear-3.js b/js/src/jit-test/tests/collections/Set-clear-3.js new file mode 100644 index 0000000000..0fe138212e --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-clear-3.js @@ -0,0 +1,10 @@ +// Clearing a Set with a nontrivial number of elements works. + +var s = new Set(); +for (var i = 0; i < 100; i++) + s.add(i); +assertEq(s.size, i); +s.clear(); +assertEq(s.size, 0); +s.add(12); +assertEq(s.has(12), true); diff --git a/js/src/jit-test/tests/collections/Set-clear-4.js b/js/src/jit-test/tests/collections/Set-clear-4.js new file mode 100644 index 0000000000..5551a07abe --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-clear-4.js @@ -0,0 +1,10 @@ +// Clearing a Set after deleting some entries works. + +var s = new Set(["a", "b", "c", "d"]); +for (var v of s) + if (v !== "c") + s.delete(v); +s.clear(); +assertEq(s.size, 0); +assertEq(s.has("c"), false); +assertEq(s.has("d"), false); diff --git a/js/src/jit-test/tests/collections/Set-clear-5.js b/js/src/jit-test/tests/collections/Set-clear-5.js new file mode 100644 index 0000000000..48c5bf8688 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-clear-5.js @@ -0,0 +1,14 @@ +// Set.clear is unaffected by deleting/monkeypatching Set.prototype.{delete,iterator}. + +var data = ["a", 1, {}]; +var s1 = new Set(data), s2 = new Set(data); + +delete Set.prototype.delete; +delete Set.prototype.iterator; +s1.clear(); +assertEq(s1.size, 0); + +Set.prototype.delete = function () { throw "FAIL"; }; +Set.prototype.iterator = function () { throw "FAIL"; }; +s2.clear(); +assertEq(s2.size, 0); diff --git a/js/src/jit-test/tests/collections/Set-clear-6.js b/js/src/jit-test/tests/collections/Set-clear-6.js new file mode 100644 index 0000000000..5c8cfce006 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-clear-6.js @@ -0,0 +1,6 @@ +// Clearing a Set doesn't affect expando properties. + +var s = new Set(); +s.x = 3; +s.clear(); +assertEq(s.x, 3); diff --git a/js/src/jit-test/tests/collections/Set-clear-iterators-1.js b/js/src/jit-test/tests/collections/Set-clear-iterators-1.js new file mode 100644 index 0000000000..b5198ed64a --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-clear-iterators-1.js @@ -0,0 +1,23 @@ +// A Set iterator does not visit entries removed by clear(). + +load(libdir + "iteration.js"); + +var s = new Set(); +var it = s[Symbol.iterator](); +s.clear(); +assertIteratorDone(it, undefined); + +s = new Set(["a", "b", "c", "d"]); +it = s[Symbol.iterator](); +assertIteratorNext(it, "a"); +s.clear(); +assertIteratorDone(it, undefined); + +var log = ""; +s = new Set(["a", "b", "c", "d"]); +for (var v of s) { + log += v; + if (v == "b") + s.clear(); +} +assertEq(log, "ab"); diff --git a/js/src/jit-test/tests/collections/Set-clear-iterators-2.js b/js/src/jit-test/tests/collections/Set-clear-iterators-2.js new file mode 100644 index 0000000000..3b80d42c33 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-clear-iterators-2.js @@ -0,0 +1,11 @@ +// A Set iterator continues to visit entries added after a clear(). + +load(libdir + "iteration.js"); + +var s = new Set(["a"]); +var it = s[Symbol.iterator](); +assertIteratorNext(it, "a"); +s.clear(); +s.add("b"); +assertIteratorNext(it, "b"); +assertIteratorDone(it, undefined); diff --git a/js/src/jit-test/tests/collections/Set-clear-iterators-3.js b/js/src/jit-test/tests/collections/Set-clear-iterators-3.js new file mode 100644 index 0000000000..c814d4ca9a --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-clear-iterators-3.js @@ -0,0 +1,10 @@ +// A closed Set iterator does not visit new entries added after a clear(). + +load(libdir + "iteration.js"); + +var s = new Set(); +var it = s[Symbol.iterator](); +assertIteratorDone(it, undefined); // close the iterator +s.clear(); +s.add("a"); +assertIteratorDone(it, undefined); diff --git a/js/src/jit-test/tests/collections/Set-constructor-1.js b/js/src/jit-test/tests/collections/Set-constructor-1.js new file mode 100644 index 0000000000..eab36af909 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-constructor-1.js @@ -0,0 +1,14 @@ +// The Set constructor creates an empty Set by default. + +load(libdir + "asserts.js"); + +var s = new Set(); +assertEq(s.size, 0); +s = new Set(undefined); +assertEq(s.size, 0); +s = new Set(null); +assertEq(s.size, 0); + +assertThrowsInstanceOf(() => Set(), TypeError); +assertThrowsInstanceOf(() => Set(undefined), TypeError); +assertThrowsInstanceOf(() => Set(null), TypeError); diff --git a/js/src/jit-test/tests/collections/Set-constructor-2.js b/js/src/jit-test/tests/collections/Set-constructor-2.js new file mode 100644 index 0000000000..43fffba8e8 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-constructor-2.js @@ -0,0 +1,18 @@ +// The Set constructor can take an argument that is an array. + +var s = new Set([]); +assertEq(s.size, 0); +assertEq(s.has(undefined), false); + +s = new Set(["one", "two", "three"]); +assertEq(s.size, 3); +assertEq(s.has("one"), true); +assertEq(s.has("eleventeen"), false); + +var a = [{}, {}, {}]; +s = new Set(a); +assertEq(s.size, 3); +for (let obj of a) + assertEq(s.has(obj), true); +assertEq(s.has({}), false); +assertEq(s.has("three"), false); diff --git a/js/src/jit-test/tests/collections/Set-constructor-3.js b/js/src/jit-test/tests/collections/Set-constructor-3.js new file mode 100644 index 0000000000..07bcd1a1dd --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-constructor-3.js @@ -0,0 +1,12 @@ +// The argument to Set may contain a value multiple times. Duplicates are discarded. + +var s = new Set(["testing", "testing", 123]); +assertEq(s.size, 2); + +var values = [undefined, null, false, NaN, 0, -0, 6.022e23, -Infinity, "", "xyzzy", {}, Math.sin]; +for (let v of values) { + var a = [v, {}, {}, {}, v, {}, v, v]; + s = new Set(a); + assertEq(s.size, 5); + assertEq(s.has(v), true); +} diff --git a/js/src/jit-test/tests/collections/Set-constructor-add.js b/js/src/jit-test/tests/collections/Set-constructor-add.js new file mode 100644 index 0000000000..8604c1386a --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-constructor-add.js @@ -0,0 +1,183 @@ +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +var k1 = {}; +var k2 = {}; +var k3 = {}; +var k4 = {}; + +function test_patched() { + let orig = Set.prototype.add; + + // If adder is modified, constructor should call it. + var called = false; + + Set.prototype.add = function(k, v) { + assertEq(k, k1); + orig.call(this, k2); + called = true; + }; + + var arr = [k1]; + + var m = new Set(arr); + + assertEq(called, true); + assertEq(m.size, 1); + assertEq(m.has(k1), false); + assertEq(m.has(k2), true); + + Set.prototype.add = orig; +} + +function test_proxy1() { + let orig = Set.prototype.add; + + // If adder is modified, constructor should call it. + var called = false; + + Set.prototype.add = new Proxy(function(k, v) { + assertEq(k, k1); + orig.call(this, k2); + called = true; + }, {}); + + var arr = [k1]; + + var m = new Set(arr); + + assertEq(called, true); + assertEq(m.size, 1); + assertEq(m.has(k1), false); + assertEq(m.has(k2), true); + + Set.prototype.add = orig; +} + +function test_proxy2() { + let orig = Set.prototype.add; + + // If adder is modified, constructor should call it. + var called = false; + + Set.prototype.add = new Proxy(function() { + }, { + apply: function(target, that, args) { + var [k, v] = args; + assertEq(k, k1); + orig.call(that, k2); + called = true; + } + }); + + var arr = [k1]; + + var m = new Set(arr); + + assertEq(called, true); + assertEq(m.size, 1); + assertEq(m.has(k1), false); + assertEq(m.has(k2), true); + + Set.prototype.add = orig; +} + +function test_change1() { + let orig = Set.prototype.add; + + // Change to adder in GetIterator(..) call should be ignored. + var called = false; + var modified = false; + + var arr = [k1]; + + var proxy_arr = new Proxy(arr, { + get: function(target, name) { + if (name == Symbol.iterator) { + modified = true; + Set.prototype.add = function() { + called = true; + }; + } + return target[name]; + } + }); + + var m = new Set(proxy_arr); + + assertEq(modified, true); + assertEq(called, false); + assertEq(m.size, 1); + assertEq(m.has(k1), true); + assertEq(m.has(k2), false); + + Set.prototype.add = orig; +} + +function test_change2() { + let orig = Set.prototype.add; + + // Change to adder in adder(...) call should be ignored. + var called = false; + var count = 0; + + Set.prototype.add = function(k, v) { + if (count == 0) { + assertEq(k, k1); + orig.call(this, k3); + Set.prototype.add = function() { + called = true; + }; + count = 1; + } else { + assertEq(k, k2); + orig.call(this, k4); + count = 2; + } + }; + + var arr = [k1, k2]; + + var m = new Set(arr); + + assertEq(called, false); + assertEq(count, 2); + assertEq(m.size, 2); + assertEq(m.has(k1), false); + assertEq(m.has(k2), false); + assertEq(m.has(k3), true); + assertEq(m.has(k4), true); + + Set.prototype.add = orig; +} + +function test_error() { + let orig = Set.prototype.add; + + var arr = [k1]; + + // Set should throw TypeError if adder is not callable. + Set.prototype.add = null; + assertThrowsInstanceOf(() => new Set(arr), TypeError); + Set.prototype.add = {}; + assertThrowsInstanceOf(() => new Set(arr), TypeError); + + // Set should propagate error thrown by adder. + Set.prototype.add = function() { + throw SyntaxError(); + }; + assertThrowsInstanceOf(() => new Set(arr), SyntaxError); + + Set.prototype.add = orig; +} + +function test() { + test_patched(); + test_proxy1(); + test_proxy2(); + test_change1(); + test_change2(); + test_error(); +} + +test(); diff --git a/js/src/jit-test/tests/collections/Set-constructor-generator-1.js b/js/src/jit-test/tests/collections/Set-constructor-generator-1.js new file mode 100644 index 0000000000..c9c760f7ef --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-constructor-generator-1.js @@ -0,0 +1,12 @@ +// The argument to Set can be a generator. + +function* hexData(n) { + for (var i = 0; i < n; i++) + yield i.toString(16); +} + +var s = new Set(hexData(256)); +assertEq(s.size, 256); +assertEq(s.has("0"), true); +assertEq(s.has(0), false); +assertEq(s.has("ff"), true); diff --git a/js/src/jit-test/tests/collections/Set-delete-size.js b/js/src/jit-test/tests/collections/Set-delete-size.js new file mode 100644 index 0000000000..524978b817 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-delete-size.js @@ -0,0 +1,15 @@ +// set.delete(v) decrements set.size iff the set contained v. + +var s = new Set(); +for (var i = 0; i < 10; i++) + s.add(i); + +for (var i = 10; i > 0; i--) { + assertEq(s.size, i); + assertEq(s.delete(i), false); + assertEq(s.size, i); + assertEq(s.delete(i - 1), true); + assertEq(s.size, i - 1); + assertEq(s.delete(i - 1), false); + assertEq(s.size, i - 1); +} diff --git a/js/src/jit-test/tests/collections/Set-forEach.js b/js/src/jit-test/tests/collections/Set-forEach.js new file mode 100644 index 0000000000..35ba036bcf --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-forEach.js @@ -0,0 +1,49 @@ +/* test Set.prototype.forEach */ + +load(libdir + 'asserts.js'); +load(libdir + 'iteration.js'); + +// testing success conditions of Set.prototype.forEach + +var testSet = new Set(); + +function callback(value, key, set) { + assertEq(value, key); + testSet.add(value); + assertEq(set.has(key), true); +} + +var initialSet = new Set(['a', 1, undefined]); +initialSet.forEach(callback); + +// test that both the Sets are equal and are in same order +var iterator = initialSet[Symbol.iterator](); +var count = 0; +for (var v of testSet) { + assertEq(initialSet.has(v), true); + assertIteratorNext(iterator, v); + count++; +} + +//check both the Sets we have are equal in size +assertEq(initialSet.size, testSet.size); +assertEq(initialSet.size, count); + +var x = { abc: 'test'}; +function callback2(value, key, set) { + assertEq(x, this); +} +initialSet = new Set(['a']); +initialSet.forEach(callback2, x); + +// testing failure conditions of Set.prototype.forEach + +var m = new Map([['a', 1], ['b', 2.3], ['c', undefined]]); +assertThrowsInstanceOf(function() { + Set.prototype.forEach.call(m, callback); +}, TypeError, "Set.prototype.forEach should raise TypeError if not a used on a Set"); + +var fn = 2; +assertThrowsInstanceOf(function() { + initialSet.forEach(fn); +}, TypeError, "Set.prototype.forEach should raise TypeError if callback is not a function"); diff --git a/js/src/jit-test/tests/collections/Set-iterator-1.js b/js/src/jit-test/tests/collections/Set-iterator-1.js new file mode 100644 index 0000000000..c0bd8fc5a3 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-iterator-1.js @@ -0,0 +1,11 @@ +// for-of can be used to iterate over a Set twice. + +var set = new Set(['a', 'b', 'c']); +var log = ''; + +for (let i = 0; i < 2; i++) { + for (let x of set) + log += x; + log += ';' +} +assertEq(log, 'abc;abc;'); diff --git a/js/src/jit-test/tests/collections/Set-iterator-2.js b/js/src/jit-test/tests/collections/Set-iterator-2.js new file mode 100644 index 0000000000..32737a285e --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-iterator-2.js @@ -0,0 +1,11 @@ +// Nested for-of loops can iterate over a Set. + +var map = new Set(['a', 'b']); +var log = ''; +for (let x of map) { + log += x + ':' + for (let y of map) + log += y; + log += ';' +}; +assertEq(log, 'a:ab;b:ab;'); diff --git a/js/src/jit-test/tests/collections/Set-iterator-3.js b/js/src/jit-test/tests/collections/Set-iterator-3.js new file mode 100644 index 0000000000..c337acb2f6 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-iterator-3.js @@ -0,0 +1,11 @@ +// Iterating over a set of objects yields those exact objects. + +var arr = [{}, {}, {}, [], /xyz/, new Date]; +var set = new Set(arr); +assertEq(set.size, arr.length); + +var i = 0; +for (var x of set) + assertEq(x, arr[i++]); +assertEq(i, arr.length); + diff --git a/js/src/jit-test/tests/collections/Set-iterator-add-1.js b/js/src/jit-test/tests/collections/Set-iterator-add-1.js new file mode 100644 index 0000000000..9f07929239 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-iterator-add-1.js @@ -0,0 +1,11 @@ +// set.iterator() is live: entries added during iteration are visited. + +var set = new Set([5]); +var log = ''; +for (let x of set) { + log += x + ';'; + if (x > 0) + set.add(x - 1); +} +assertEq(log, '5;4;3;2;1;0;'); +assertEq(set.size, 6); diff --git a/js/src/jit-test/tests/collections/Set-iterator-add-2.js b/js/src/jit-test/tests/collections/Set-iterator-add-2.js new file mode 100644 index 0000000000..211614a4cf --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-iterator-add-2.js @@ -0,0 +1,10 @@ +// A Set iterator does not iterate over new entries added after it throws StopIteration. + +load(libdir + "iteration.js"); + +var set = new Set(); +var iter0 = set[Symbol.iterator](), iter1 = set[Symbol.iterator](); +assertIteratorDone(iter0, undefined); // closes iter0 +set.add("x"); +assertIteratorDone(iter0, undefined); // already closed +assertIteratorNext(iter1, "x"); // was not yet closed diff --git a/js/src/jit-test/tests/collections/Set-iterator-add-remove.js b/js/src/jit-test/tests/collections/Set-iterator-add-remove.js new file mode 100644 index 0000000000..b0d3518daf --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-iterator-add-remove.js @@ -0,0 +1,13 @@ +// Removing and re-adding entries while an iterator is live causes the iterator to visit them again. + +var set = new Set(['a']); +var n = 5; +for (let v of set) { + assertEq(v, 'a'); + if (n === 0) + break; + set.delete('a'); + set.add('a'); + n--; +} +assertEq(n, 0); diff --git a/js/src/jit-test/tests/collections/Set-iterator-gc-2.js b/js/src/jit-test/tests/collections/Set-iterator-gc-2.js new file mode 100644 index 0000000000..41979b86a8 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-iterator-gc-2.js @@ -0,0 +1,8 @@ +// GC-ing during a for-of loop doesn't crash. + +var i = 0; +for (var x of new Set(Object.getOwnPropertyNames(this))) { + gc(); + if (++i >= 20) + break; +} diff --git a/js/src/jit-test/tests/collections/Set-iterator-gc-3.js b/js/src/jit-test/tests/collections/Set-iterator-gc-3.js new file mode 100644 index 0000000000..88c428b006 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-iterator-gc-3.js @@ -0,0 +1,20 @@ +// GC in nested for-loops is safe. + +var x; +for (x of new Set([1])) + for (x of new Set([1])) + for (x of new Set([1])) + for (x of new Set([1])) + for (x of new Set([1])) + for (x of new Set([1])) + for (x of new Set([1])) + for (x of new Set([1])) + for (x of new Set([1])) + for (x of new Set([1])) + for (x of new Set([1])) + for (x of new Set([1])) + for (x of new Set([1])) + for (x of new Set([1])) + for (x of new Set([1])) + for (x of new Set([1])) + gc(); diff --git a/js/src/jit-test/tests/collections/Set-iterator-order.js b/js/src/jit-test/tests/collections/Set-iterator-order.js new file mode 100644 index 0000000000..3ac8425076 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-iterator-order.js @@ -0,0 +1,14 @@ +// Set iterators produces entries in the order they were inserted. + +var set = new Set(); +var i; +for (i = 7; i !== 1; i = i * 7 % 1117) + set.add(i); +assertEq(set.size, 557); + +i = 7; +for (var v of set) { + assertEq(v, i); + i = i * 7 % 1117; +} +assertEq(i, 1); diff --git a/js/src/jit-test/tests/collections/Set-iterator-proxies-1.js b/js/src/jit-test/tests/collections/Set-iterator-proxies-1.js new file mode 100644 index 0000000000..c3d041d088 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-iterator-proxies-1.js @@ -0,0 +1,8 @@ +// for-of works on a cross-compartment wrapper of a Set. + +var g = newGlobal(); +var mw = g.eval("new Set(['a', 'b', 1, 2])"); +var log = ''; +for (let x of mw) + log += x; +assertEq(log, "ab12"); diff --git a/js/src/jit-test/tests/collections/Set-iterator-proxies-2.js b/js/src/jit-test/tests/collections/Set-iterator-proxies-2.js new file mode 100644 index 0000000000..75e4565148 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-iterator-proxies-2.js @@ -0,0 +1,20 @@ +// map.iterator() and iter.next() are non-generic but work on cross-compartment wrappers. + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +var g = newGlobal(); + +var iterator_fn = Set.prototype[Symbol.iterator]; +assertThrowsInstanceOf(function () { iterator_fn.call({}); }, TypeError); +assertThrowsInstanceOf(function () { iterator_fn.call(new Map()); }, TypeError); +var setw = g.eval("new Set(['x', 'y'])"); +assertIteratorNext(iterator_fn.call(setw), "x"); + +var next_fn = (new Set())[Symbol.iterator]().next; +assertThrowsInstanceOf(function () { next_fn.call({}); }, TypeError); +assertThrowsInstanceOf(function () { next_fn.call((new Map())[Symbol.iterator]()); }, TypeError); +var iterw = setw[Symbol.iterator](); +assertIteratorResult(next_fn.call(iterw), "x", false); +assertIteratorResult(next_fn.call(iterw), "y", false); +assertIteratorResult(next_fn.call(iterw), undefined, true); diff --git a/js/src/jit-test/tests/collections/Set-iterator-remove-1.js b/js/src/jit-test/tests/collections/Set-iterator-remove-1.js new file mode 100644 index 0000000000..31ac12f1ac --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-iterator-remove-1.js @@ -0,0 +1,26 @@ +// A set iterator can cope with removing the current entry. + +function test(letters, toRemove) { + var set = new Set(letters); + toRemove = new Set(toRemove); + + var leftovers = [...set].filter(x => !toRemove.has(x)).join(""); + + var log = ""; + for (let x of set) { + log += x; + if (toRemove.has(x)) + set.delete(x); + } + assertEq(log, letters); + + var remaining = [...set].join(""); + assertEq(remaining, leftovers); +} + +test('a', 'a'); // removing the only entry +test('abc', 'a'); // removing the first entry +test('abc', 'b'); // removing a middle entry +test('abc', 'c'); // removing the last entry +test('abc', 'abc') // removing all entries + diff --git a/js/src/jit-test/tests/collections/Set-iterator-remove-2.js b/js/src/jit-test/tests/collections/Set-iterator-remove-2.js new file mode 100644 index 0000000000..e78d8a182f --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-iterator-remove-2.js @@ -0,0 +1,13 @@ +// A map iterator can cope with removing the next entry. + +load(libdir + "iteration.js"); + +var set = new Set("abcd"); +var iter = set[Symbol.iterator](); +var log = ""; +for (let x of iter) { + log += x; + if (x === "b") + set.delete("c"); +} +assertEq(log, "abd"); diff --git a/js/src/jit-test/tests/collections/Set-iterator-remove-3.js b/js/src/jit-test/tests/collections/Set-iterator-remove-3.js new file mode 100644 index 0000000000..fd9f498833 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-iterator-remove-3.js @@ -0,0 +1,12 @@ +// A set iterator can cope with removing the next entry, then the current entry. + +load(libdir + "iteration.js"); + +var set = new Set("abcd"); +var iter = set[Symbol.iterator](); +assertIteratorNext(iter, "a"); +assertIteratorNext(iter, "b"); +set.delete("c"); +set.delete("b"); +assertIteratorNext(iter, "d"); +assertIteratorDone(iter, undefined); diff --git a/js/src/jit-test/tests/collections/Set-iterator-remove-4.js b/js/src/jit-test/tests/collections/Set-iterator-remove-4.js new file mode 100644 index 0000000000..78f3b66e64 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-iterator-remove-4.js @@ -0,0 +1,31 @@ +// Multiple live iterators on the same Set can cope with removing entries. + +load(libdir + "iteration.js"); + +// Make a set. +var set = new Set(); +var SIZE = 7; +for (var j = 0; j < SIZE; j++) + set.add(j); + +// Make lots of iterators pointing to entry 2 of the set. +var NITERS = 5; +var iters = []; +for (var i = 0; i < NITERS; i++) { + var iter = set[Symbol.iterator](); + assertIteratorNext(iter, 0); + assertIteratorNext(iter, 1); + iters[i] = iter; +} + +// Remove half of the set entries. +for (var j = 0; j < SIZE; j += 2) + set.delete(j); + +// Make sure all the iterators still work. +for (var i = 0; i < NITERS; i++) { + var iter = iters[i]; + for (var j = 3; j < SIZE; j += 2) + assertIteratorNext(iter, j); + assertIteratorDone(iter, undefined); +} diff --git a/js/src/jit-test/tests/collections/Set-iterator-remove-5.js b/js/src/jit-test/tests/collections/Set-iterator-remove-5.js new file mode 100644 index 0000000000..378accc3ac --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-iterator-remove-5.js @@ -0,0 +1,22 @@ +// Removing a Set entry already visited by an iterator does not cause any +// entries to be skipped. + +var str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; +var set = new Set(str); + +var log = ''; +var i = 0; +for (var x of set) { + log += x; + if (i++ % 5 === 0) { + // Delete all entries preceding this one. + for (let y of set) { + if (y === x) + break; + set.delete(y); + } + } +} +assertEq(log, str); +assertEq(set.size, 1); // Elements 0 to 24 are removed, leaving only 25 (Z). +assertEq(set.has('Z'), true); diff --git a/js/src/jit-test/tests/collections/Set-iterator-remove-6.js b/js/src/jit-test/tests/collections/Set-iterator-remove-6.js new file mode 100644 index 0000000000..923ae631c0 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-iterator-remove-6.js @@ -0,0 +1,20 @@ +// Removing many Set entries does not cause a live iterator to skip any of the +// entries that were not removed. (Compacting a Set must not be observable to +// script.) + +load(libdir + "iteration.js"); + +var set = new Set(); +for (var i = 0; i < 32; i++) + set.add(i); +var iter = set[Symbol.iterator](); +assertIteratorNext(iter, 0); +for (var i = 0; i < 30; i++) + set.delete(i); +assertEq(set.size, 2); +for (var i = 32; i < 100; i++) + set.add(i); // eventually triggers compaction + +for (var i = 30; i < 100; i++) + assertIteratorNext(iter, i); +assertIteratorDone(iter, undefined); diff --git a/js/src/jit-test/tests/collections/Set-scale.js b/js/src/jit-test/tests/collections/Set-scale.js new file mode 100644 index 0000000000..5c5af2a5a8 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-scale.js @@ -0,0 +1,8 @@ +// Sets can hold at least 64K values. + +var N = 1 << 16; +var s = new Set; +for (var i = 0; i < N; i++) + assertEq(s.add(i), s); +for (var i = 0; i < N; i++) + assertEq(s.has(i), true); diff --git a/js/src/jit-test/tests/collections/Set-size.js b/js/src/jit-test/tests/collections/Set-size.js new file mode 100644 index 0000000000..7aca1d3b75 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-size.js @@ -0,0 +1,7 @@ +// Each Set has its own size. + +var s1 = new Set(), s2 = new Set(); +for (var i = 0; i < 30; i++) + s1.add(i); +assertEq(s1.size, 30); +assertEq(s2.size, 0); diff --git a/js/src/jit-test/tests/collections/Set-surfaces-1.js b/js/src/jit-test/tests/collections/Set-surfaces-1.js new file mode 100644 index 0000000000..cd8573868c --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-surfaces-1.js @@ -0,0 +1,47 @@ +// Set surfaces + +load(libdir + "iteration.js"); + +var desc = Object.getOwnPropertyDescriptor(this, "Set"); +assertEq(desc.enumerable, false); +assertEq(desc.configurable, true); +assertEq(desc.writable, true); + +assertEq(typeof Set, 'function'); +assertEq(Object.keys(Set).length, 0); +assertEq(Set.length, 0); +assertEq(Set.name, "Set"); + +assertEq(Object.getPrototypeOf(Set.prototype), Object.prototype); +assertEq(Object.prototype.toString.call(Set.prototype), "[object Set]"); +assertEq(Object.prototype.toString.call(new Set()), "[object Set]"); +assertEq(Object.keys(Set.prototype).join(), ""); +assertEq(Set.prototype.constructor, Set); + +function checkMethod(name, arity) { + var desc = Object.getOwnPropertyDescriptor(Set.prototype, name); + assertEq(desc.enumerable, false); + assertEq(desc.configurable, true); + assertEq(desc.writable, true); + assertEq(typeof desc.value, 'function'); + assertEq(desc.value.name, name); + assertEq(desc.value.length, arity); +} + +checkMethod("has", 1); +checkMethod("add", 1); +checkMethod("delete", 1); +checkMethod("values", 0); +checkMethod("entries", 0); + +var desc = Object.getOwnPropertyDescriptor(Set.prototype, "size"); +assertEq(desc.enumerable, false); +assertEq(desc.configurable, true); +assertEq(typeof desc.get, 'function'); +assertEq(desc.get.length, 0); +assertEq(desc.set, undefined); +checkMethod("clear", 0); + +// Set.prototype.keys, .values, and .iterator are the same function object +assertEq(Set.prototype.keys, Set.prototype.values); +assertEq(Set.prototype[Symbol.iterator], Set.prototype.values); diff --git a/js/src/jit-test/tests/collections/Set-surfaces-2.js b/js/src/jit-test/tests/collections/Set-surfaces-2.js new file mode 100644 index 0000000000..3d43b0da1c --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-surfaces-2.js @@ -0,0 +1,28 @@ +// Set methods throw when passed a this-value that isn't a Set. + +load(libdir + "asserts.js"); + +function testcase(obj, fn, ...args) { + assertEq(typeof fn, "function"); + assertThrowsInstanceOf(function () { fn.apply(obj, args); }, TypeError); +} + +var Set_size_getter = Object.getOwnPropertyDescriptor(Set.prototype, "size").get; + +function test(obj) { + testcase(obj, Set.prototype.has, 12); + testcase(obj, Set.prototype.add, 12); + testcase(obj, Set.prototype.delete, 12); + testcase(obj, Set.prototype.clear); + testcase(obj, Set.prototype.keys); + testcase(obj, Set.prototype.values); + testcase(obj, Set.prototype.entries); + testcase(obj, Set_size_getter); +} + +test(Set.prototype); +test(Object.create(new Set)); +test(new Map()); +test({}); +test(null); +test(undefined); diff --git a/js/src/jit-test/tests/collections/Set-surfaces-3.js b/js/src/jit-test/tests/collections/Set-surfaces-3.js new file mode 100644 index 0000000000..b487b7b22f --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-surfaces-3.js @@ -0,0 +1,10 @@ +// Set methods work when arguments are omitted. + +var s = new Set; +assertEq(s.has(), false); +assertEq(s.delete(), false); +assertEq(s.has(), false); +assertEq(s.add(), s); +assertEq(s.has(), true); +assertEq(s.delete(), true); +assertEq(s.has(), false); diff --git a/js/src/jit-test/tests/collections/Set-values-1.js b/js/src/jit-test/tests/collections/Set-values-1.js new file mode 100644 index 0000000000..31acf42d07 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-values-1.js @@ -0,0 +1,14 @@ +// set.keys(), .values(), and .entries() on an empty set produce empty iterators + +var s = new Set(); +var ki = s.keys(), vi = s.values(), ei = s.entries(); +var p = Object.getPrototypeOf(ki); +assertEq(Object.getPrototypeOf(vi), p); +assertEq(Object.getPrototypeOf(ei), p); + +for (let k of ki) + throw "FAIL"; +for (let v of vi) + throw "FAIL"; +for (let [k, v] of ei) + throw "FAIL"; diff --git a/js/src/jit-test/tests/collections/Set-values-2.js b/js/src/jit-test/tests/collections/Set-values-2.js new file mode 100644 index 0000000000..2367c4df91 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-values-2.js @@ -0,0 +1,18 @@ +// set.keys() and set.values() return iterators over the elements +// and set.entries() returns an iterator that yields pairs [e, e]. + +load(libdir + "iteration.js"); + +var data = [1, 2, 3, 4]; +var s = new Set(data); + +var ki = s.keys(); +assertIteratorNext(ki, 1); +assertIteratorNext(ki, 2); +assertIteratorNext(ki, 3); +assertIteratorNext(ki, 4); +assertIteratorDone(ki, undefined); + +assertDeepEq([...s.keys()], data); +assertDeepEq([...s.values()], data); +assertDeepEq([...s.entries()], [[1, 1], [2, 2], [3, 3], [4, 4]]); diff --git a/js/src/jit-test/tests/collections/WeakMap-constructor-1.js b/js/src/jit-test/tests/collections/WeakMap-constructor-1.js new file mode 100644 index 0000000000..ab73feb51c --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakMap-constructor-1.js @@ -0,0 +1,11 @@ +// The WeakMap constructor creates an empty WeakMap by default. + +load(libdir + "asserts.js"); + +new WeakMap(); +new WeakMap(undefined); +new WeakMap(null); + +assertThrowsInstanceOf(() => WeakMap(), TypeError); +assertThrowsInstanceOf(() => WeakMap(undefined), TypeError); +assertThrowsInstanceOf(() => WeakMap(null), TypeError); diff --git a/js/src/jit-test/tests/collections/WeakMap-constructor-2.js b/js/src/jit-test/tests/collections/WeakMap-constructor-2.js new file mode 100644 index 0000000000..4c64d9e9ff --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakMap-constructor-2.js @@ -0,0 +1,37 @@ +// The WeakMap constructor can take an argument that is an array of pairs. + +var k1 = {}; +var v1 = 42; +var k2 = {}; +var v2 = 43; +var k3 = {}; + +var arr = [[k1, v1], [k2, v2]]; + +var m = new WeakMap(arr); + +assertEq(m.has(k1), true); +assertEq(m.has(k2), true); +assertEq(m.has(k3), false); +assertEq(m.get(k1), v1); +assertEq(m.get(k2), v2); +assertEq(m.get(k3), undefined); + +var arraylike1 = { + 0: k1, + 1: v1 +}; +var arraylike2 = { + 0: k2, + 1: v2 +}; +arr = [arraylike1, arraylike2]; + +m = new WeakMap(arr); + +assertEq(m.has(k1), true); +assertEq(m.has(k2), true); +assertEq(m.has(k3), false); +assertEq(m.get(k1), v1); +assertEq(m.get(k2), v2); +assertEq(m.get(k3), undefined); diff --git a/js/src/jit-test/tests/collections/WeakMap-constructor-3.js b/js/src/jit-test/tests/collections/WeakMap-constructor-3.js new file mode 100644 index 0000000000..ab18a2151e --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakMap-constructor-3.js @@ -0,0 +1,35 @@ +// WeakMap can take an argument that is an array of singleton arrays. + +var k1 = {}; +var k2 = {}; +var k3 = {}; + +var arr = [[k1], [k2]]; + +var m = new WeakMap(arr); + +assertEq(m.has(k1), true); +assertEq(m.has(k2), true); +assertEq(m.has(k3), false); +assertEq(m.get(k1), undefined); +assertEq(m.get(k2), undefined); +assertEq(m.get(k3), undefined); + +var arraylike1 = { + 0: k1, + 1: undefined +}; +var arraylike2 = { + 0: k2, +}; + +arr = [arraylike1, arraylike2]; + +m = new WeakMap(arr); + +assertEq(m.has(k1), true); +assertEq(m.has(k2), true); +assertEq(m.has(k3), false); +assertEq(m.get(k1), undefined); +assertEq(m.get(k2), undefined); +assertEq(m.get(k3), undefined); diff --git a/js/src/jit-test/tests/collections/WeakMap-constructor-4.js b/js/src/jit-test/tests/collections/WeakMap-constructor-4.js new file mode 100644 index 0000000000..6b87d66f59 --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakMap-constructor-4.js @@ -0,0 +1,6 @@ +// new WeakMap(x) throws if x is not iterable (unless x is undefined). + +load(libdir + "asserts.js"); +var nonIterables = [true, 1, -0, 3.14, NaN, {}, Math, this]; +for (let k of nonIterables) + assertThrowsInstanceOf(function () { new WeakMap(k); }, TypeError); diff --git a/js/src/jit-test/tests/collections/WeakMap-constructor-5.js b/js/src/jit-test/tests/collections/WeakMap-constructor-5.js new file mode 100644 index 0000000000..3ef7300bea --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakMap-constructor-5.js @@ -0,0 +1,23 @@ +// new WeakMap(arr) throws if arr contains holes (or undefined values). + +load(libdir + "asserts.js"); + +var k1 = {}; +var v1 = 42; +var k2 = {}; +var v2 = 43; +var k3 = {}; +var v3 = {}; + +assertThrowsInstanceOf(function () { new WeakMap([undefined]); }, TypeError); +assertThrowsInstanceOf(function () { new WeakMap([null]); }, TypeError); +assertThrowsInstanceOf(function () { new WeakMap([[k1, v1], [k2, v2], , [k3, k3]]); }, TypeError); +assertThrowsInstanceOf(function () { new WeakMap([[k1, v1], [k2, v2], ,]); }, TypeError); + +// new WeakMap(iterable) throws if iterable doesn't have array-like objects + +assertThrowsInstanceOf(function () { new WeakMap([1, 2, 3]); }, TypeError); +assertThrowsInstanceOf(function () { + let s = new Set([1, 2, "abc"]); + new WeakMap(s); +}, TypeError); diff --git a/js/src/jit-test/tests/collections/WeakMap-constructor-arraylike-exception.js b/js/src/jit-test/tests/collections/WeakMap-constructor-arraylike-exception.js new file mode 100644 index 0000000000..bbb1b43bdd --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakMap-constructor-arraylike-exception.js @@ -0,0 +1,23 @@ +// WeakMap constructor should propagate exception while getting key and value. + +load(libdir + "asserts.js"); + +var k1 = {}; +var v1 = 42; + +var error_thrower_0 = { + get 0() { + throw new Error; + }, + 1: v1 +}; +assertThrowsInstanceOf(() => new WeakMap([error_thrower_0]), Error); + +var error_thrower_1 = { + 0: k1, + get 1() { + throw new Error; + } +}; +assertThrowsInstanceOf(() => new WeakMap([error_thrower_1]), Error); + diff --git a/js/src/jit-test/tests/collections/WeakMap-constructor-duplicates.js b/js/src/jit-test/tests/collections/WeakMap-constructor-duplicates.js new file mode 100644 index 0000000000..5e5f3a58e9 --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakMap-constructor-duplicates.js @@ -0,0 +1,27 @@ +// When the argument to WeakMap contains a key multiple times, the last value +// is retained. + +var k1 = {}; +var v1 = 42; +var k2 = {}; +var v2 = 43; +var k3 = {}; +var v3 = 44; +var k4 = {}; + +var wrong1 = 8; +var wrong2 = 9; +var wrong3 = 10; + +var arr = [[k1, wrong1], [k2, v2], [k3, wrong2], [k1, wrong3], [k3, v3], [k1, v1]]; + +var m = new WeakMap(arr); + +assertEq(m.has(k1), true); +assertEq(m.has(k2), true); +assertEq(m.has(k3), true); +assertEq(m.has(k4), false); +assertEq(m.get(k1), v1); +assertEq(m.get(k2), v2); +assertEq(m.get(k3), v3); +assertEq(m.get(k4), undefined); diff --git a/js/src/jit-test/tests/collections/WeakMap-constructor-generator-1.js b/js/src/jit-test/tests/collections/WeakMap-constructor-generator-1.js new file mode 100644 index 0000000000..4b03c2ab82 --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakMap-constructor-generator-1.js @@ -0,0 +1,25 @@ +// The argument to WeakMap can be a generator. + +var k1 = {}; +var v1 = 42; +var k2 = {}; +var v2 = 43; +var k3 = {}; + +var done = false; + +function* data() { + yield [k1, v1]; + yield [k2, v2]; + done = true; +}; + +m = new WeakMap(data()); + +assertEq(done, true); // the constructor consumes the argument +assertEq(m.has(k1), true); +assertEq(m.has(k2), true); +assertEq(m.has(k3), false); +assertEq(m.get(k1), v1); +assertEq(m.get(k2), v2); +assertEq(m.get(k3), undefined); diff --git a/js/src/jit-test/tests/collections/WeakMap-constructor-generator-3.js b/js/src/jit-test/tests/collections/WeakMap-constructor-generator-3.js new file mode 100644 index 0000000000..a860a61d65 --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakMap-constructor-generator-3.js @@ -0,0 +1,6 @@ +// The argument to WeakMap may be a generator-iterator that produces no values. + +function* none() { + if (0) yield 0; +} +new WeakMap(none()); diff --git a/js/src/jit-test/tests/collections/WeakMap-constructor-generator-exception.js b/js/src/jit-test/tests/collections/WeakMap-constructor-generator-exception.js new file mode 100644 index 0000000000..996b8e7c73 --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakMap-constructor-generator-exception.js @@ -0,0 +1,13 @@ +// Iterating over the argument to WeakMap can throw. The exception is +// propagated. + +load(libdir + "asserts.js"); + +function* data() { + yield [{}, "XR22/Z"]; + yield [{}, "23D-BN"]; + throw "oops"; +} + +var it2 = data(); +assertThrowsValue(() => new WeakMap(it2), "oops"); diff --git a/js/src/jit-test/tests/collections/WeakMap-constructor-iterable.js b/js/src/jit-test/tests/collections/WeakMap-constructor-iterable.js new file mode 100644 index 0000000000..f5eac8e30d --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakMap-constructor-iterable.js @@ -0,0 +1,28 @@ +// The argument to WeakMap can be a iterable object. + +load(libdir + "iteration.js"); + +var k1 = {}; +var v1 = 42; +var k2 = {}; +var v2 = 43; +var k3 = {}; + +var done = false; + +var iterable = {}; +iterable[Symbol.iterator] = function*() { + yield [k1, v1]; + yield [k2, v2]; + done = true; +}; +var m = new WeakMap(iterable); + +assertEq(done, true); // the constructor consumes the argument +assertEq(m.has(k1), true); +assertEq(m.has(k2), true); +assertEq(m.has(k3), false); +assertEq(m.get(k1), v1); +assertEq(m.get(k2), v2); +assertEq(m.get(k3), undefined); + diff --git a/js/src/jit-test/tests/collections/WeakMap-constructor-non-iterable.js b/js/src/jit-test/tests/collections/WeakMap-constructor-non-iterable.js new file mode 100644 index 0000000000..427818d775 --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakMap-constructor-non-iterable.js @@ -0,0 +1,13 @@ +// WeakMap should throw if argument is not iterable object. + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +var non_iterable1 = {}; +non_iterable1[Symbol.iterator] = {}; +assertThrowsInstanceOf(() => new WeakMap(non_iterable1), TypeError); + +var non_iterable2 = {}; +non_iterable2[Symbol.iterator] = function() { +}; +assertThrowsInstanceOf(() => new WeakMap(non_iterable2), TypeError); diff --git a/js/src/jit-test/tests/collections/WeakMap-constructor-nonnull.js b/js/src/jit-test/tests/collections/WeakMap-constructor-nonnull.js new file mode 100644 index 0000000000..5cae35a38f --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakMap-constructor-nonnull.js @@ -0,0 +1,11 @@ +// WeakMap constructor should throw when key is nonnull. + +load(libdir + "asserts.js"); + +var v1 = 42; + +var primitive = 10; +assertThrowsInstanceOf(() => new WeakMap([[primitive, v1]]), TypeError); + +var empty_array = []; +assertThrowsInstanceOf(() => new WeakMap([empty_array]), TypeError); diff --git a/js/src/jit-test/tests/collections/WeakMap-constructor-set.js b/js/src/jit-test/tests/collections/WeakMap-constructor-set.js new file mode 100644 index 0000000000..4026f824c3 --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakMap-constructor-set.js @@ -0,0 +1,199 @@ +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +var k1 = {}; +var v1 = 42; +var k2 = {}; +var v2 = 42; +var k3 = {}; +var v3 = 43; +var k4 = {}; +var v4 = 44; + +function test_patched() { + let orig = WeakMap.prototype.set; + + // If adder is modified, constructor should call it. + var called = false; + + WeakMap.prototype.set = function(k, v) { + assertEq(k, k1); + assertEq(v, v1); + orig.call(this, k2, v2); + called = true; + }; + + var arr = [[k1, v1]]; + + var m = new WeakMap(arr); + + assertEq(called, true); + assertEq(m.has(k1), false); + assertEq(m.has(k2), true); + assertEq(m.get(k1), undefined); + assertEq(m.get(k2), v2); + + WeakMap.prototype.set = orig; +} + +function test_proxy1() { + let orig = WeakMap.prototype.set; + + // If adder is modified, constructor should call it. + var called = false; + + WeakMap.prototype.set = new Proxy(function(k, v) { + assertEq(k, k1); + assertEq(v, v1); + orig.call(this, k2, v2); + called = true; + }, {}); + + var arr = [[k1, v1]]; + + var m = new WeakMap(arr); + + assertEq(called, true); + assertEq(m.has(k1), false); + assertEq(m.has(k2), true); + assertEq(m.get(k1), undefined); + assertEq(m.get(k2), v2); + + WeakMap.prototype.set = orig; +} + +function test_proxy2() { + let orig = WeakMap.prototype.set; + + // If adder is modified, constructor should call it. + var called = false; + + WeakMap.prototype.set = new Proxy(function() { + }, { + apply: function(target, that, args) { + var [k, v] = args; + assertEq(k, k1); + assertEq(v, v1); + orig.call(that, k2, v2); + called = true; + } + }); + + var arr = [[k1, v1]]; + + var m = new WeakMap(arr); + + assertEq(called, true); + assertEq(m.has(k1), false); + assertEq(m.has(k2), true); + assertEq(m.get(k1), undefined); + assertEq(m.get(k2), v2); + + WeakMap.prototype.set = orig; +} + +function test_change1() { + let orig = WeakMap.prototype.set; + + // Change to adder in GetIterator(..) call should be ignored. + var called = false; + var modified = false; + + var arr = [[k1, v1]]; + + var proxy_arr = new Proxy(arr, { + get: function(target, name) { + if (name == Symbol.iterator) { + modified = true; + WeakMap.prototype.set = function() { + called = true; + }; + } + return target[name]; + } + }); + + var m = new WeakMap(proxy_arr); + + assertEq(modified, true); + assertEq(called, false); + assertEq(m.has(k1), true); + assertEq(m.has(k2), false); + assertEq(m.get(k1), v1); + assertEq(m.get(k2), undefined); + + WeakMap.prototype.set = orig; +} + +function test_change2() { + let orig = WeakMap.prototype.set; + + // Change to adder in adder(...) call should be ignored. + var called = false; + var count = 0; + + WeakMap.prototype.set = function(k, v) { + if (count == 0) { + assertEq(k, k1); + assertEq(v, v1); + orig.call(this, k3, v3); + WeakMap.prototype.set = function() { + called = true; + }; + count = 1; + } else { + assertEq(k, k2); + assertEq(v, v2); + orig.call(this, k4, v4); + count = 2; + } + }; + + var arr = [[k1, v1], [k2, v2]]; + + var m = new WeakMap(arr); + + assertEq(called, false); + assertEq(count, 2); + assertEq(m.has(k1), false); + assertEq(m.has(k2), false); + assertEq(m.has(k3), true); + assertEq(m.has(k4), true); + assertEq(m.get(k1), undefined); + assertEq(m.get(k2), undefined); + assertEq(m.get(k3), v3); + assertEq(m.get(k4), v4); + + WeakMap.prototype.set = orig; +} + +function test_error() { + let orig = WeakMap.prototype.set; + + var arr = [[k1, v1]]; + + // WeakMap should throw TypeError if adder is not callable. + WeakMap.prototype.set = null; + assertThrowsInstanceOf(() => new WeakMap(arr), TypeError); + WeakMap.prototype.set = {}; + assertThrowsInstanceOf(() => new WeakMap(arr), TypeError); + + // WeakMap should propagate error thrown by adder. + WeakMap.prototype.set = function() { + throw SyntaxError(); + }; + assertThrowsInstanceOf(() => new WeakMap(arr), SyntaxError); + + WeakMap.prototype.set = orig; +} + +function test() { + test_patched(); + test_proxy1(); + test_proxy2(); + test_change1(); + test_change2(); + test_error(); +} + +test(); diff --git a/js/src/jit-test/tests/collections/WeakMap-moving-gc.js b/js/src/jit-test/tests/collections/WeakMap-moving-gc.js new file mode 100644 index 0000000000..1bb29428b8 --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakMap-moving-gc.js @@ -0,0 +1,12 @@ +var wm = new WeakMap; +var A = []; +for (var i = 0; i < 1024; ++i) { + var key = {i:i}; + wm.set(key, i); + A.push(key); +} +gc(); +for (var i in A) { + var key = A[i]; + assertEq(wm.has(key), true); +} diff --git a/js/src/jit-test/tests/collections/WeakMap-set-returns-this.js b/js/src/jit-test/tests/collections/WeakMap-set-returns-this.js new file mode 100644 index 0000000000..f62c75fa1a --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakMap-set-returns-this.js @@ -0,0 +1,9 @@ +// Bug 1031632 - Map.prototype.set, WeakMap.prototype.set and +// Set.prototype.add should be chainable + +var wm = new WeakMap(); +var bar = {}; +assertEq(wm.set(bar, 'BAR'), wm); +var foo = {}; +var a = wm.set(foo, 'FOO').get(foo); +assertEq(a, 'FOO'); diff --git a/js/src/jit-test/tests/collections/WeakMap-surfaces.js b/js/src/jit-test/tests/collections/WeakMap-surfaces.js new file mode 100644 index 0000000000..662bb39e33 --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakMap-surfaces.js @@ -0,0 +1,32 @@ +// WeakMap surfaces + +var desc = Object.getOwnPropertyDescriptor(this, "WeakMap"); +assertEq(desc.enumerable, false); +assertEq(desc.configurable, true); +assertEq(desc.writable, true); + +assertEq(typeof WeakMap, 'function'); +assertEq(Object.keys(WeakMap).length, 0); +assertEq(WeakMap.length, 0); +assertEq(WeakMap.name, "WeakMap"); + +assertEq(Object.getPrototypeOf(WeakMap.prototype), Object.prototype); +assertEq(Object.prototype.toString.call(WeakMap.prototype), "[object WeakMap]"); +assertEq(Object.prototype.toString.call(new WeakMap()), "[object WeakMap]"); +assertEq(Object.keys(WeakMap.prototype).join(), ""); +assertEq(WeakMap.prototype.constructor, WeakMap); + +function checkMethod(name, arity) { + var desc = Object.getOwnPropertyDescriptor(WeakMap.prototype, name); + assertEq(desc.enumerable, false); + assertEq(desc.configurable, true); + assertEq(desc.writable, true); + assertEq(typeof desc.value, 'function'); + assertEq(desc.value.name, name); + assertEq(desc.value.length, arity); +} + +checkMethod("get", 1); +checkMethod("has", 1); +checkMethod("set", 2); +checkMethod("delete", 1); diff --git a/js/src/jit-test/tests/collections/WeakSet-add-returns-this.js b/js/src/jit-test/tests/collections/WeakSet-add-returns-this.js new file mode 100644 index 0000000000..eb9c40e407 --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakSet-add-returns-this.js @@ -0,0 +1,9 @@ +var ws = new WeakSet(); +var bar = {}; +assertEq(ws.add(bar), ws); +var foo = {}; +var a = ws.add(foo); +assertEq(a, ws); +assertEq(a.has(bar), true); +assertEq(a.has(foo), true); +assertEq(WeakSet.prototype.add.call(ws, {}), ws); diff --git a/js/src/jit-test/tests/collections/WeakSet-constructor-1.js b/js/src/jit-test/tests/collections/WeakSet-constructor-1.js new file mode 100644 index 0000000000..ce03f22de9 --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakSet-constructor-1.js @@ -0,0 +1,11 @@ +// The WeakSet constructor creates an empty WeakSet by default. + +load(libdir + "asserts.js"); + +new WeakSet(); +new WeakSet(undefined); +new WeakSet(null); + +assertThrowsInstanceOf(() => WeakSet(), TypeError); +assertThrowsInstanceOf(() => WeakSet(undefined), TypeError); +assertThrowsInstanceOf(() => WeakSet(null), TypeError); diff --git a/js/src/jit-test/tests/collections/WeakSet-constructor-add.js b/js/src/jit-test/tests/collections/WeakSet-constructor-add.js new file mode 100644 index 0000000000..b883e074f1 --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakSet-constructor-add.js @@ -0,0 +1,178 @@ +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +var k1 = {}; +var k2 = {}; +var k3 = {}; +var k4 = {}; + +function test_patched() { + let orig = WeakSet.prototype.add; + + // If adder is modified, constructor should call it. + var called = false; + + WeakSet.prototype.add = function(k, v) { + assertEq(k, k1); + orig.call(this, k2); + called = true; + }; + + var arr = [k1]; + + var m = new WeakSet(arr); + + assertEq(called, true); + assertEq(m.has(k1), false); + assertEq(m.has(k2), true); + + WeakSet.prototype.add = orig; +} + +function test_proxy1() { + let orig = WeakSet.prototype.add; + + // If adder is modified, constructor should call it. + var called = false; + + WeakSet.prototype.add = new Proxy(function(k, v) { + assertEq(k, k1); + orig.call(this, k2); + called = true; + }, {}); + + var arr = [k1]; + + var m = new WeakSet(arr); + + assertEq(called, true); + assertEq(m.has(k1), false); + assertEq(m.has(k2), true); + + WeakSet.prototype.add = orig; +} + +function test_proxy2() { + let orig = WeakSet.prototype.add; + + // If adder is modified, constructor should call it. + var called = false; + + WeakSet.prototype.add = new Proxy(function() { + }, { + apply: function(target, that, args) { + var [k, v] = args; + assertEq(k, k1); + orig.call(that, k2); + called = true; + } + }); + + var arr = [k1]; + + var m = new WeakSet(arr); + + assertEq(called, true); + assertEq(m.has(k1), false); + assertEq(m.has(k2), true); + + WeakSet.prototype.add = orig; +} + +function test_change1() { + let orig = WeakSet.prototype.add; + + // Change to adder in GetIterator(..) call should be ignored. + var called = false; + var modified = false; + + var arr = [k1]; + + var proxy_arr = new Proxy(arr, { + get: function(target, name) { + if (name == Symbol.iterator) { + modified = true; + WeakSet.prototype.add = function() { + called = true; + }; + } + return target[name]; + } + }); + + var m = new WeakSet(proxy_arr); + + assertEq(modified, true); + assertEq(called, false); + assertEq(m.has(k1), true); + assertEq(m.has(k2), false); + + WeakSet.prototype.add = orig; +} + +function test_change2() { + let orig = WeakSet.prototype.add; + + // Change to adder in adder(...) call should be ignored. + var called = false; + var count = 0; + + WeakSet.prototype.add = function(k, v) { + if (count == 0) { + assertEq(k, k1); + orig.call(this, k3); + WeakSet.prototype.add = function() { + called = true; + }; + count = 1; + } else { + assertEq(k, k2); + orig.call(this, k4); + count = 2; + } + }; + + var arr = [k1, k2]; + + var m = new WeakSet(arr); + + assertEq(called, false); + assertEq(count, 2); + assertEq(m.has(k1), false); + assertEq(m.has(k2), false); + assertEq(m.has(k3), true); + assertEq(m.has(k4), true); + + WeakSet.prototype.add = orig; +} + +function test_error() { + let orig = WeakSet.prototype.add; + + var arr = [k1]; + + // WeakSet should throw TypeError if adder is not callable. + WeakSet.prototype.add = null; + assertThrowsInstanceOf(() => new WeakSet(arr), TypeError); + WeakSet.prototype.add = {}; + assertThrowsInstanceOf(() => new WeakSet(arr), TypeError); + + // WeakSet should propagate error thrown by adder. + WeakSet.prototype.add = function() { + throw SyntaxError(); + }; + assertThrowsInstanceOf(() => new WeakSet(arr), SyntaxError); + + WeakSet.prototype.add = orig; +} + +function test() { + test_patched(); + test_proxy1(); + test_proxy2(); + test_change1(); + test_change2(); + test_error(); +} + +test(); diff --git a/js/src/jit-test/tests/collections/WeakSet-constructor.js b/js/src/jit-test/tests/collections/WeakSet-constructor.js new file mode 100644 index 0000000000..a89e8fc0c8 --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakSet-constructor.js @@ -0,0 +1,9 @@ +var list = [Number, Function]; + +var ws = new WeakSet(list); +assertEq(ws.has(Number), true); +assertEq(ws.has(Function), true); + +ws = new WeakSet(new Set(list)); +assertEq(ws.has(Number), true); +assertEq(ws.has(Function), true); diff --git a/js/src/jit-test/tests/collections/WeakSet-delete.js b/js/src/jit-test/tests/collections/WeakSet-delete.js new file mode 100644 index 0000000000..0309ad7131 --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakSet-delete.js @@ -0,0 +1,32 @@ +var ws = new WeakSet; + +// Delete on empty +assertEq(ws.delete({}), false); + +// Delete existing +var value = {}; +ws.add(value); +assertEq(ws.has(value), true); +assertEq(ws.delete(value), true); +assertEq(ws.has(value), false); + +// Delete non-empty +for (var i = 0; i < 10; i++) + ws.add({}); +assertEq(ws.add(value), ws); +assertEq(ws.has(value), true); +assertEq(ws.delete(value), true); +assertEq(ws.has(value), false); +assertEq(ws.delete(value), false); +assertEq(ws.has(value), false); + +// Delete primitive +assertEq(ws.delete(15), false); + +// Delete with cross-compartment WeakSet +ws = new (newGlobal().WeakSet); +WeakSet.prototype.add.call(ws, value); +assertEq(WeakSet.prototype.has.call(ws, value), true); +assertEq(WeakSet.prototype.delete.call(ws, value), true); +assertEq(WeakSet.prototype.has.call(ws, value), false); +assertEq(WeakSet.prototype.delete.call(ws, value), false); diff --git a/js/src/jit-test/tests/collections/WeakSet-error.js b/js/src/jit-test/tests/collections/WeakSet-error.js new file mode 100644 index 0000000000..4125aaba79 --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakSet-error.js @@ -0,0 +1,22 @@ +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +function testMethod(name) { + var method = WeakSet.prototype[name]; + + assertThrowsInstanceOf(function() { method.call(1); }, TypeError); + assertThrowsInstanceOf(function() { method.call({}); }, TypeError); + assertThrowsInstanceOf(function() { method.call(new WeakMap); }, TypeError); + assertThrowsInstanceOf(function() { method.call(WeakSet.prototype); }, TypeError); +} + +testMethod("has"); +testMethod("add"); +testMethod("delete"); +testMethod("clear"); + +assertThrowsInstanceOf(function() { var ws = new WeakSet(); ws.add(1); }, TypeError); +assertThrowsInstanceOf(function() { new WeakSet({[Symbol.iterator]: 2}) }, TypeError); +assertEq(typeof [][Symbol.iterator], "function"); + +assertThrowsInstanceOf(function() { WeakSet(); }, TypeError); diff --git a/js/src/jit-test/tests/collections/WeakSet-moving-gc.js b/js/src/jit-test/tests/collections/WeakSet-moving-gc.js new file mode 100644 index 0000000000..e5ae87bc75 --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakSet-moving-gc.js @@ -0,0 +1,12 @@ +var ws = new WeakSet; +var A = []; +for (var i = 0; i < 1024; ++i) { + var value = {}; + ws.add(value); + A.push(value); +} +gc(); +for (i = 0; i < A.length; A++) { + value = A[i]; + assertEq(ws.has(value), true); +} diff --git a/js/src/jit-test/tests/collections/WeakSet-surface.js b/js/src/jit-test/tests/collections/WeakSet-surface.js new file mode 100644 index 0000000000..b72a7d0306 --- /dev/null +++ b/js/src/jit-test/tests/collections/WeakSet-surface.js @@ -0,0 +1,31 @@ +// WeakSet surfaces + +var desc = Object.getOwnPropertyDescriptor(this, "WeakSet"); +assertEq(desc.enumerable, false); +assertEq(desc.configurable, true); +assertEq(desc.writable, true); + +assertEq(typeof WeakSet, 'function'); +assertEq(Object.keys(WeakSet).length, 0); +assertEq(WeakSet.length, 0); +assertEq(WeakSet.name, "WeakSet"); + +assertEq(Object.getPrototypeOf(WeakSet.prototype), Object.prototype); +assertEq(Object.prototype.toString.call(WeakSet.prototype), "[object WeakSet]"); +assertEq(Object.prototype.toString.call(new WeakSet), "[object WeakSet]"); +assertEq(Object.keys(WeakSet.prototype).length, 0); +assertEq(WeakSet.prototype.constructor, WeakSet); + +function checkMethod(name, arity) { + var desc = Object.getOwnPropertyDescriptor(WeakSet.prototype, name); + assertEq(desc.enumerable, false); + assertEq(desc.configurable, true); + assertEq(desc.writable, true); + assertEq(typeof desc.value, 'function'); + assertEq(desc.value.name, name); + assertEq(desc.value.length, arity); +} + +checkMethod("has", 1); +checkMethod("add", 1); +checkMethod("delete", 1); diff --git a/js/src/jit-test/tests/collections/bug-1381423.js b/js/src/jit-test/tests/collections/bug-1381423.js new file mode 100644 index 0000000000..59b29d3f01 --- /dev/null +++ b/js/src/jit-test/tests/collections/bug-1381423.js @@ -0,0 +1,5 @@ +var s = new Set(Array(1000).fill(0).map((v, k) => k + 1)); +do { + var n = [...s].length; + assertEq(n, 1000); +} while (!inIon()); diff --git a/js/src/jit-test/tests/collections/bug-743101.js b/js/src/jit-test/tests/collections/bug-743101.js new file mode 100644 index 0000000000..5328306690 --- /dev/null +++ b/js/src/jit-test/tests/collections/bug-743101.js @@ -0,0 +1,7 @@ +load(libdir + "asserts.js"); + +var g = newGlobal({newCompartment: true}); +for (var cls of [Map, Set]) { + var getter = Object.getOwnPropertyDescriptor(cls.prototype, "size").get; + assertThrowsInstanceOf(function () { getter.apply(g, []); }, g.TypeError); +} diff --git a/js/src/jit-test/tests/collections/constructor-errors.js b/js/src/jit-test/tests/collections/constructor-errors.js new file mode 100644 index 0000000000..6a145180f1 --- /dev/null +++ b/js/src/jit-test/tests/collections/constructor-errors.js @@ -0,0 +1,19 @@ +// The Set constructor throws TypeError when passed a non-iterable argument. + +load(libdir + "asserts.js"); + +var misc = [ + {}, {x: 1}, Math, isNaN, + Object.create(null), + true, 0, 3.1416, + new Boolean(true), new Number(0), + {iterator: function () { return undefined; }}, + {iterator: function () { return null; }}, + {iterator: function () { return true; }}, + {iterator: function () { return 17; }}, +]; + +for (var v of misc) { + assertThrowsInstanceOf(function () { new Set(v); }, TypeError); + assertThrowsInstanceOf(function () { new Map(v); }, TypeError); +} diff --git a/js/src/jit-test/tests/collections/for-in.js b/js/src/jit-test/tests/collections/for-in.js new file mode 100644 index 0000000000..b46de15e21 --- /dev/null +++ b/js/src/jit-test/tests/collections/for-in.js @@ -0,0 +1,25 @@ +// for-in loops on Maps and Sets enumerate properties. + +var test = function test(obj) { + assertEq(Object.keys(obj).length, 0); + + var i = 0, v; + for (v in obj) + i++; + assertEq(i, 0); + + obj.ownProp = 1; + assertEq(Object.keys(obj).join(), "ownProp"); + + for (v in obj) + i++; + assertEq(i, 1); + assertEq(v, "ownProp"); + + delete obj.ownProp; +}; + +test(Map.prototype); +test(new Map); +test(Set.prototype); +test(new Set); diff --git a/js/src/jit-test/tests/collections/iterator-1.js b/js/src/jit-test/tests/collections/iterator-1.js new file mode 100644 index 0000000000..9624bae792 --- /dev/null +++ b/js/src/jit-test/tests/collections/iterator-1.js @@ -0,0 +1,13 @@ +// collection.iterator() returns an iterator object. + +load(libdir + "iteration.js"); + +function test(obj, name) { + var iter = obj[Symbol.iterator](); + assertEq(typeof iter, "object"); + assertEq(iter.toString(), "[object " + obj.constructor.name + " Iterator]"); +} + +test([]); +test(new Map); +test(new Set); diff --git a/js/src/jit-test/tests/collections/iterator-2.js b/js/src/jit-test/tests/collections/iterator-2.js new file mode 100644 index 0000000000..923a9b79b4 --- /dev/null +++ b/js/src/jit-test/tests/collections/iterator-2.js @@ -0,0 +1,12 @@ +// for-of on an empty collection does not execute the loop body or modify the loop variable. + +function test(empty) { + var x = 'unchanged'; + for (x of empty) + throw fit; + assertEq(x, 'unchanged'); +} + +test([]); +test(new Map); +test(new Set);
\ No newline at end of file diff --git a/js/src/jit-test/tests/collections/iterator-noSuchMethod.js b/js/src/jit-test/tests/collections/iterator-noSuchMethod.js new file mode 100644 index 0000000000..f79d3fa317 --- /dev/null +++ b/js/src/jit-test/tests/collections/iterator-noSuchMethod.js @@ -0,0 +1,24 @@ +// __noSuchMethod__ is totally non-standard and evil, but in this one weird case +// below we don't actually use it. So this test is bog-standard ES6, not +// SpiderMonkey-specific. +// +// In ES6: +// Accessing 1[Symbol.iterator]() throws a TypeError calling |undefined|. +// In SpiderMonkey: +// Accessing 1[Symbol.iterator]() does *not* invoke __noSuchMethod__ looked up +// on 1 (or on an implicitly boxed 1), because 1 is a primitive value. +// SpiderMonkey then does exactly the ES6 thing here and throws a TypeError +// calling |undefined|. + +Object.prototype.__noSuchMethod__ = {}; + +try +{ + var [x] = 1; + throw new Error("didn't throw"); +} +catch (e) +{ + assertEq(e instanceof TypeError, true, + "expected TypeError, got " + e); +} diff --git a/js/src/jit-test/tests/collections/iterator-proto-1.js b/js/src/jit-test/tests/collections/iterator-proto-1.js new file mode 100644 index 0000000000..c9827a5746 --- /dev/null +++ b/js/src/jit-test/tests/collections/iterator-proto-1.js @@ -0,0 +1,18 @@ +// All iterators of the same collection type share their immediate prototype. +// Those prototype objects in turn inherit directly from %IteratorPrototype%. + +load(libdir + "iteration.js"); + +// Get %IteratorPrototype%. +var iterProto = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())); + +function test(obj0, obj1) { + var iter0 = obj0[Symbol.iterator](), iter1 = obj1[Symbol.iterator](); + var proto = Object.getPrototypeOf(iter0); + assertEq(Object.getPrototypeOf(iter1), proto); + assertEq(Object.getPrototypeOf(proto), iterProto); +} + +test([], [1]); +test(new Map(), new Map([[1, 1]])); +test(new Set(), new Set([1])); diff --git a/js/src/jit-test/tests/collections/iterator-proto-2.js b/js/src/jit-test/tests/collections/iterator-proto-2.js new file mode 100644 index 0000000000..d6d5ba7d7f --- /dev/null +++ b/js/src/jit-test/tests/collections/iterator-proto-2.js @@ -0,0 +1,13 @@ +// Iterators of different collection types have different prototypes. + +load(libdir + "iteration.js"); + +var aproto = Object.getPrototypeOf(Array()[Symbol.iterator]()); +var mproto = Object.getPrototypeOf((new Map())[Symbol.iterator]()); +var sproto = Object.getPrototypeOf((new Set())[Symbol.iterator]()); +assertEq(aproto !== mproto, true); +assertEq(aproto !== sproto, true); +assertEq(mproto !== sproto, true); +assertEq(aproto.next !== mproto.next, true); +assertEq(aproto.next !== sproto.next, true); +assertEq(mproto.next !== sproto.next, true); diff --git a/js/src/jit-test/tests/collections/iterator-proto-surfaces.js b/js/src/jit-test/tests/collections/iterator-proto-surfaces.js new file mode 100644 index 0000000000..e39ae777fb --- /dev/null +++ b/js/src/jit-test/tests/collections/iterator-proto-surfaces.js @@ -0,0 +1,39 @@ +// Iterator prototype surfaces. + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +var iterProto = null; + +function test(constructor) { + var iter = new constructor()[Symbol.iterator](); + assertDeepEq(Reflect.ownKeys(iter), []); + + // Iterator prototypes only have a .next and @@toStringTag property. + var proto1 = Object.getPrototypeOf(iter); + assertDeepEq(Reflect.ownKeys(proto1), ['next', Symbol.toStringTag]); + + var desc = Object.getOwnPropertyDescriptor(proto1, 'next'); + assertEq(desc.configurable, true); + assertEq(desc.enumerable, false); + assertEq(desc.writable, true); + + // %IteratorPrototype% + var proto2 = Object.getPrototypeOf(proto1); + assertEq(Object.getPrototypeOf(proto2), Object.prototype); + assertEq(Object.prototype.toString.call(proto2), "[object Object]"); + + assertDeepEq(Reflect.ownKeys(proto2), [Symbol.iterator]); + assertEq(proto2[Symbol.iterator](), proto2); + + // Check there's a single %IteratorPrototype% object. + if (iterProto === null) + iterProto = proto2; + else + assertEq(iterProto, proto2); +} + +test(Array); +test(String); +test(Map); +test(Set); diff --git a/js/src/jit-test/tests/collections/key-equality-0.js b/js/src/jit-test/tests/collections/key-equality-0.js new file mode 100644 index 0000000000..7f9da8c2bd --- /dev/null +++ b/js/src/jit-test/tests/collections/key-equality-0.js @@ -0,0 +1,43 @@ +// -0 is treated as the same key as +0. + +var s = new Set; +s.add(-0); +assertEq(s.has(0), true); +assertEq(s.has(-0), true); + +assertEq(s.delete(0), true); +assertEq(s.has(-0), false); +assertEq(s.has(0), false); + +s.add(0); +assertEq(s.has(0), true); +assertEq(s.has(-0), true); +assertEq(s.delete(-0), true); +assertEq(s.has(-0), false); +assertEq(s.has(0), false); + +var m = new Map; +m.set(-0, 'x'); +assertEq(m.has(0), true); +assertEq(m.get(0), 'x'); +assertEq(m.has(-0), true); +assertEq(m.get(-0), 'x'); + +assertEq(m.delete(0), true); +assertEq(m.has(-0), false); +assertEq(m.get(-0), undefined); +assertEq(m.has(0), false); +assertEq(m.get(0), undefined); + +m.set(-0, 'x'); +m.set(0, 'y'); +assertEq(m.has(0), true); +assertEq(m.get(0), 'y'); +assertEq(m.has(-0), true); +assertEq(m.get(-0), 'y'); + +assertEq(m.delete(-0), true); +assertEq(m.has(0), false); +assertEq(m.get(0), undefined); +assertEq(m.has(-0), false); +assertEq(m.get(-0), undefined); diff --git a/js/src/jit-test/tests/collections/key-equality-1.js b/js/src/jit-test/tests/collections/key-equality-1.js new file mode 100644 index 0000000000..501c882ff8 --- /dev/null +++ b/js/src/jit-test/tests/collections/key-equality-1.js @@ -0,0 +1,28 @@ +// Different representations of the same number or string are treated as the same Map key. + +var m = new Map; +var test = function test(a, b) { + m.set(a, 'secret'); + assertEq(m.get(b), 'secret'); + m.set(b, 'password'); + assertEq(m.get(a), 'password'); + + assertEq(a, b); +}; + +// Float and integer representations of the same number are the same key. +test(9, Math.sqrt(81)); + +// Ordinary strings and ropes are the same key. +var a = Array(1001).join('x'); +var b = Array(501).join('x') + Array(501).join('x'); +test(a, b); + +// Two structurally different ropes with the same characters are the same key. +a = ""; +b = ""; +for (var i = 0; i < 10; i++) { + a = Array(501).join('x') + a; + b = b + Array(501).join('x'); +} +test(a, b); diff --git a/js/src/jit-test/tests/collections/key-equality-2.js b/js/src/jit-test/tests/collections/key-equality-2.js new file mode 100644 index 0000000000..25605b29ba --- /dev/null +++ b/js/src/jit-test/tests/collections/key-equality-2.js @@ -0,0 +1,11 @@ +// Number keys are distinct from string keys that would name the same property. + +var s = new Set; + +s.add(17); +assertEq(s.has("17"), false); +assertEq(s.has(17), true); +s.add("17"); +assertEq(s.delete(17), true); +assertEq(s.has("17"), true); +assertEq(s.has(17), false); diff --git a/js/src/jit-test/tests/collections/key-equality-NaN.js b/js/src/jit-test/tests/collections/key-equality-NaN.js new file mode 100644 index 0000000000..f26dc07605 --- /dev/null +++ b/js/src/jit-test/tests/collections/key-equality-NaN.js @@ -0,0 +1,15 @@ +// NaN is equal to itself for the purpose of key lookups. + +var m = new Map; +m.set(NaN, "ok"); +assertEq(m.has(NaN), true); +assertEq(m.get(NaN), "ok"); +assertEq(m.delete(NaN), true); +assertEq(m.has(NaN), false); +assertEq(m.get(NaN), undefined); + +var s = new Set; +s.add(NaN); +assertEq(s.has(NaN), true); +assertEq(s.delete(NaN), true); +assertEq(s.has(NaN), false); |