summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/collections
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /js/src/jit-test/tests/collections
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jit-test/tests/collections')
-rw-r--r--js/src/jit-test/tests/collections/Array-of-1.js15
-rw-r--r--js/src/jit-test/tests/collections/Array-of-2.js14
-rw-r--r--js/src/jit-test/tests/collections/Array-of-3.js8
-rw-r--r--js/src/jit-test/tests/collections/Array-of-4.js13
-rw-r--r--js/src/jit-test/tests/collections/Array-of-cross-compartment.js10
-rw-r--r--js/src/jit-test/tests/collections/Array-of-generic-1.js25
-rw-r--r--js/src/jit-test/tests/collections/Array-of-generic-2.js11
-rw-r--r--js/src/jit-test/tests/collections/Array-of-generic-3.js7
-rw-r--r--js/src/jit-test/tests/collections/Array-of-length-setter-2.js13
-rw-r--r--js/src/jit-test/tests/collections/Array-of-length-setter.js26
-rw-r--r--js/src/jit-test/tests/collections/Array-of-nonconfigurable-1.js8
-rw-r--r--js/src/jit-test/tests/collections/Array-of-nonconfigurable-2.js16
-rw-r--r--js/src/jit-test/tests/collections/Array-of-ordering.js32
-rw-r--r--js/src/jit-test/tests/collections/Array-of-surfaces.js14
-rw-r--r--js/src/jit-test/tests/collections/Map-Set-moving-gc.js16
-rw-r--r--js/src/jit-test/tests/collections/Map-clear-1.js8
-rw-r--r--js/src/jit-test/tests/collections/Map-clear-2.js17
-rw-r--r--js/src/jit-test/tests/collections/Map-clear-3.js10
-rw-r--r--js/src/jit-test/tests/collections/Map-clear-4.js10
-rw-r--r--js/src/jit-test/tests/collections/Map-clear-5.js14
-rw-r--r--js/src/jit-test/tests/collections/Map-clear-6.js6
-rw-r--r--js/src/jit-test/tests/collections/Map-clear-iterators-1.js23
-rw-r--r--js/src/jit-test/tests/collections/Map-clear-iterators-2.js12
-rw-r--r--js/src/jit-test/tests/collections/Map-constructor-1.js14
-rw-r--r--js/src/jit-test/tests/collections/Map-constructor-2.js6
-rw-r--r--js/src/jit-test/tests/collections/Map-constructor-3.js9
-rw-r--r--js/src/jit-test/tests/collections/Map-constructor-4.js6
-rw-r--r--js/src/jit-test/tests/collections/Map-constructor-5.js15
-rw-r--r--js/src/jit-test/tests/collections/Map-constructor-duplicates.js8
-rw-r--r--js/src/jit-test/tests/collections/Map-constructor-generator-1.js19
-rw-r--r--js/src/jit-test/tests/collections/Map-constructor-generator-3.js7
-rw-r--r--js/src/jit-test/tests/collections/Map-constructor-generator-exception.js12
-rw-r--r--js/src/jit-test/tests/collections/Map-constructor-set.js204
-rw-r--r--js/src/jit-test/tests/collections/Map-delete-size.js14
-rw-r--r--js/src/jit-test/tests/collections/Map-delete.js18
-rw-r--r--js/src/jit-test/tests/collections/Map-forEach.js58
-rw-r--r--js/src/jit-test/tests/collections/Map-gc-4.js15
-rw-r--r--js/src/jit-test/tests/collections/Map-get.js41
-rw-r--r--js/src/jit-test/tests/collections/Map-iterator-1.js11
-rw-r--r--js/src/jit-test/tests/collections/Map-iterator-2.js11
-rw-r--r--js/src/jit-test/tests/collections/Map-iterator-add-1.js15
-rw-r--r--js/src/jit-test/tests/collections/Map-iterator-add-2.js10
-rw-r--r--js/src/jit-test/tests/collections/Map-iterator-add-remove.js14
-rw-r--r--js/src/jit-test/tests/collections/Map-iterator-already-done.js12
-rw-r--r--js/src/jit-test/tests/collections/Map-iterator-order.js15
-rw-r--r--js/src/jit-test/tests/collections/Map-iterator-pairs-1.js17
-rw-r--r--js/src/jit-test/tests/collections/Map-iterator-pairs-2.js13
-rw-r--r--js/src/jit-test/tests/collections/Map-iterator-pairs-3.js13
-rw-r--r--js/src/jit-test/tests/collections/Map-iterator-proxies-1.js8
-rw-r--r--js/src/jit-test/tests/collections/Map-iterator-proxies-2.js21
-rw-r--r--js/src/jit-test/tests/collections/Map-iterator-remove-1.js40
-rw-r--r--js/src/jit-test/tests/collections/Map-iterator-remove-2.js13
-rw-r--r--js/src/jit-test/tests/collections/Map-iterator-remove-3.js13
-rw-r--r--js/src/jit-test/tests/collections/Map-iterator-remove-4.js31
-rw-r--r--js/src/jit-test/tests/collections/Map-iterator-remove-5.js22
-rw-r--r--js/src/jit-test/tests/collections/Map-iterator-remove-6.js21
-rw-r--r--js/src/jit-test/tests/collections/Map-iterators-3.js10
-rw-r--r--js/src/jit-test/tests/collections/Map-scale.js8
-rw-r--r--js/src/jit-test/tests/collections/Map-set-returns-this.js7
-rw-r--r--js/src/jit-test/tests/collections/Map-set-size.js14
-rw-r--r--js/src/jit-test/tests/collections/Map-set-undefined.js15
-rw-r--r--js/src/jit-test/tests/collections/Map-size.js6
-rw-r--r--js/src/jit-test/tests/collections/Map-surfaces-1.js48
-rw-r--r--js/src/jit-test/tests/collections/Map-surfaces-2.js29
-rw-r--r--js/src/jit-test/tests/collections/Map-surfaces-3.js14
-rw-r--r--js/src/jit-test/tests/collections/Map-values-1.js14
-rw-r--r--js/src/jit-test/tests/collections/Map-values-2.js18
-rw-r--r--js/src/jit-test/tests/collections/Set-add-returns-this.js7
-rw-r--r--js/src/jit-test/tests/collections/Set-add-size.js11
-rw-r--r--js/src/jit-test/tests/collections/Set-clear-1.js8
-rw-r--r--js/src/jit-test/tests/collections/Set-clear-2.js16
-rw-r--r--js/src/jit-test/tests/collections/Set-clear-3.js10
-rw-r--r--js/src/jit-test/tests/collections/Set-clear-4.js10
-rw-r--r--js/src/jit-test/tests/collections/Set-clear-5.js14
-rw-r--r--js/src/jit-test/tests/collections/Set-clear-6.js6
-rw-r--r--js/src/jit-test/tests/collections/Set-clear-iterators-1.js23
-rw-r--r--js/src/jit-test/tests/collections/Set-clear-iterators-2.js11
-rw-r--r--js/src/jit-test/tests/collections/Set-clear-iterators-3.js10
-rw-r--r--js/src/jit-test/tests/collections/Set-constructor-1.js14
-rw-r--r--js/src/jit-test/tests/collections/Set-constructor-2.js18
-rw-r--r--js/src/jit-test/tests/collections/Set-constructor-3.js12
-rw-r--r--js/src/jit-test/tests/collections/Set-constructor-add.js183
-rw-r--r--js/src/jit-test/tests/collections/Set-constructor-generator-1.js12
-rw-r--r--js/src/jit-test/tests/collections/Set-delete-size.js15
-rw-r--r--js/src/jit-test/tests/collections/Set-forEach.js49
-rw-r--r--js/src/jit-test/tests/collections/Set-iterator-1.js11
-rw-r--r--js/src/jit-test/tests/collections/Set-iterator-2.js11
-rw-r--r--js/src/jit-test/tests/collections/Set-iterator-3.js11
-rw-r--r--js/src/jit-test/tests/collections/Set-iterator-add-1.js11
-rw-r--r--js/src/jit-test/tests/collections/Set-iterator-add-2.js10
-rw-r--r--js/src/jit-test/tests/collections/Set-iterator-add-remove.js13
-rw-r--r--js/src/jit-test/tests/collections/Set-iterator-gc-2.js8
-rw-r--r--js/src/jit-test/tests/collections/Set-iterator-gc-3.js20
-rw-r--r--js/src/jit-test/tests/collections/Set-iterator-order.js14
-rw-r--r--js/src/jit-test/tests/collections/Set-iterator-proxies-1.js8
-rw-r--r--js/src/jit-test/tests/collections/Set-iterator-proxies-2.js20
-rw-r--r--js/src/jit-test/tests/collections/Set-iterator-remove-1.js26
-rw-r--r--js/src/jit-test/tests/collections/Set-iterator-remove-2.js13
-rw-r--r--js/src/jit-test/tests/collections/Set-iterator-remove-3.js12
-rw-r--r--js/src/jit-test/tests/collections/Set-iterator-remove-4.js31
-rw-r--r--js/src/jit-test/tests/collections/Set-iterator-remove-5.js22
-rw-r--r--js/src/jit-test/tests/collections/Set-iterator-remove-6.js20
-rw-r--r--js/src/jit-test/tests/collections/Set-scale.js8
-rw-r--r--js/src/jit-test/tests/collections/Set-size.js7
-rw-r--r--js/src/jit-test/tests/collections/Set-surfaces-1.js47
-rw-r--r--js/src/jit-test/tests/collections/Set-surfaces-2.js28
-rw-r--r--js/src/jit-test/tests/collections/Set-surfaces-3.js10
-rw-r--r--js/src/jit-test/tests/collections/Set-values-1.js14
-rw-r--r--js/src/jit-test/tests/collections/Set-values-2.js18
-rw-r--r--js/src/jit-test/tests/collections/WeakMap-constructor-1.js11
-rw-r--r--js/src/jit-test/tests/collections/WeakMap-constructor-2.js37
-rw-r--r--js/src/jit-test/tests/collections/WeakMap-constructor-3.js35
-rw-r--r--js/src/jit-test/tests/collections/WeakMap-constructor-4.js6
-rw-r--r--js/src/jit-test/tests/collections/WeakMap-constructor-5.js23
-rw-r--r--js/src/jit-test/tests/collections/WeakMap-constructor-arraylike-exception.js23
-rw-r--r--js/src/jit-test/tests/collections/WeakMap-constructor-duplicates.js27
-rw-r--r--js/src/jit-test/tests/collections/WeakMap-constructor-generator-1.js25
-rw-r--r--js/src/jit-test/tests/collections/WeakMap-constructor-generator-3.js6
-rw-r--r--js/src/jit-test/tests/collections/WeakMap-constructor-generator-exception.js13
-rw-r--r--js/src/jit-test/tests/collections/WeakMap-constructor-iterable.js28
-rw-r--r--js/src/jit-test/tests/collections/WeakMap-constructor-non-iterable.js13
-rw-r--r--js/src/jit-test/tests/collections/WeakMap-constructor-nonnull.js11
-rw-r--r--js/src/jit-test/tests/collections/WeakMap-constructor-set.js199
-rw-r--r--js/src/jit-test/tests/collections/WeakMap-moving-gc.js12
-rw-r--r--js/src/jit-test/tests/collections/WeakMap-set-returns-this.js9
-rw-r--r--js/src/jit-test/tests/collections/WeakMap-surfaces.js32
-rw-r--r--js/src/jit-test/tests/collections/WeakSet-add-returns-this.js9
-rw-r--r--js/src/jit-test/tests/collections/WeakSet-constructor-1.js11
-rw-r--r--js/src/jit-test/tests/collections/WeakSet-constructor-add.js178
-rw-r--r--js/src/jit-test/tests/collections/WeakSet-constructor.js9
-rw-r--r--js/src/jit-test/tests/collections/WeakSet-delete.js32
-rw-r--r--js/src/jit-test/tests/collections/WeakSet-error.js22
-rw-r--r--js/src/jit-test/tests/collections/WeakSet-moving-gc.js12
-rw-r--r--js/src/jit-test/tests/collections/WeakSet-surface.js31
-rw-r--r--js/src/jit-test/tests/collections/bug-1381423.js5
-rw-r--r--js/src/jit-test/tests/collections/bug-1863391-1.js8
-rw-r--r--js/src/jit-test/tests/collections/bug-1863391-2.js6
-rw-r--r--js/src/jit-test/tests/collections/bug-1866636.js10
-rw-r--r--js/src/jit-test/tests/collections/bug-743101.js7
-rw-r--r--js/src/jit-test/tests/collections/constructor-errors.js19
-rw-r--r--js/src/jit-test/tests/collections/for-in.js25
-rw-r--r--js/src/jit-test/tests/collections/iterator-1.js13
-rw-r--r--js/src/jit-test/tests/collections/iterator-2.js12
-rw-r--r--js/src/jit-test/tests/collections/iterator-noSuchMethod.js24
-rw-r--r--js/src/jit-test/tests/collections/iterator-proto-1.js18
-rw-r--r--js/src/jit-test/tests/collections/iterator-proto-2.js13
-rw-r--r--js/src/jit-test/tests/collections/iterator-proto-surfaces.js39
-rw-r--r--js/src/jit-test/tests/collections/key-equality-0.js43
-rw-r--r--js/src/jit-test/tests/collections/key-equality-1.js28
-rw-r--r--js/src/jit-test/tests/collections/key-equality-2.js11
-rw-r--r--js/src/jit-test/tests/collections/key-equality-NaN.js15
151 files changed, 3189 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-1863391-1.js b/js/src/jit-test/tests/collections/bug-1863391-1.js
new file mode 100644
index 0000000000..e3c8914975
--- /dev/null
+++ b/js/src/jit-test/tests/collections/bug-1863391-1.js
@@ -0,0 +1,8 @@
+// |jit-test| --enable-symbols-as-weakmap-keys; skip-if: helperThreadCount() === 0 || getBuildConfiguration("release_or_beta")
+evalInWorker(`
+ a = new WeakMap
+ b = Symbol
+ a.set(b )
+ c = b.hasInstance;
+ a.get(c)
+`);
diff --git a/js/src/jit-test/tests/collections/bug-1863391-2.js b/js/src/jit-test/tests/collections/bug-1863391-2.js
new file mode 100644
index 0000000000..5174f5ff16
--- /dev/null
+++ b/js/src/jit-test/tests/collections/bug-1863391-2.js
@@ -0,0 +1,6 @@
+// |jit-test| --enable-symbols-as-weakmap-keys; skip-if: helperThreadCount() === 0 || getBuildConfiguration("release_or_beta")
+evalInWorker(`
+ a = new WeakMap
+ b = Symbol.hasInstance;
+ a.set(b, 2);
+`);
diff --git a/js/src/jit-test/tests/collections/bug-1866636.js b/js/src/jit-test/tests/collections/bug-1866636.js
new file mode 100644
index 0000000000..b535ecd2f6
--- /dev/null
+++ b/js/src/jit-test/tests/collections/bug-1866636.js
@@ -0,0 +1,10 @@
+// |jit-test| --enable-symbols-as-weakmap-keys; skip-if: getBuildConfiguration("release_or_beta")
+b = new WeakMap();
+c = Symbol();
+b.set(c);
+c = gczeal(10);
+for (i=0; i<1000; ++i) {
+ try {
+ x;
+ } catch {}
+}
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);