• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2012 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28// Flags: --expose-gc --allow-natives-syntax
29
30
31function assertSize(expected, collection) {
32  if (collection instanceof Map || collection instanceof Set) {
33    assertEquals(expected, collection.size);
34  }
35}
36
37
38// Test valid getter and setter calls on Sets and WeakSets
39function TestValidSetCalls(m) {
40  assertDoesNotThrow(function () { m.add(new Object) });
41  assertDoesNotThrow(function () { m.has(new Object) });
42  assertDoesNotThrow(function () { m.delete(new Object) });
43}
44TestValidSetCalls(new Set);
45TestValidSetCalls(new WeakSet);
46
47
48// Test valid getter and setter calls on Maps and WeakMaps
49function TestValidMapCalls(m) {
50  assertDoesNotThrow(function () { m.get(new Object) });
51  assertDoesNotThrow(function () { m.set(new Object) });
52  assertDoesNotThrow(function () { m.has(new Object) });
53  assertDoesNotThrow(function () { m.delete(new Object) });
54}
55TestValidMapCalls(new Map);
56TestValidMapCalls(new WeakMap);
57
58
59// Test invalid getter and setter calls for WeakMap only
60function TestInvalidCalls(m) {
61  assertThrows(function () { m.get(undefined) }, TypeError);
62  assertThrows(function () { m.set(undefined, 0) }, TypeError);
63  assertThrows(function () { m.get(null) }, TypeError);
64  assertThrows(function () { m.set(null, 0) }, TypeError);
65  assertThrows(function () { m.get(0) }, TypeError);
66  assertThrows(function () { m.set(0, 0) }, TypeError);
67  assertThrows(function () { m.get('a-key') }, TypeError);
68  assertThrows(function () { m.set('a-key', 0) }, TypeError);
69}
70TestInvalidCalls(new WeakMap);
71
72
73// Test expected behavior for Sets and WeakSets
74function TestSet(set, key) {
75  assertFalse(set.has(key));
76  assertSame(set, set.add(key));
77  assertTrue(set.has(key));
78  assertTrue(set.delete(key));
79  assertFalse(set.has(key));
80  assertFalse(set.delete(key));
81  assertFalse(set.has(key));
82}
83function TestSetBehavior(set) {
84  for (var i = 0; i < 20; i++) {
85    TestSet(set, new Object);
86    TestSet(set, i);
87    TestSet(set, i / 100);
88    TestSet(set, 'key-' + i);
89  }
90  var keys = [ +0, -0, +Infinity, -Infinity, true, false, null, undefined ];
91  for (var i = 0; i < keys.length; i++) {
92    TestSet(set, keys[i]);
93  }
94}
95TestSetBehavior(new Set);
96TestSet(new WeakSet, new Object);
97
98
99// Test expected mapping behavior for Maps and WeakMaps
100function TestMapping(map, key, value) {
101  assertSame(map, map.set(key, value));
102  assertSame(value, map.get(key));
103}
104function TestMapBehavior1(m) {
105  TestMapping(m, new Object, 23);
106  TestMapping(m, new Object, 'the-value');
107  TestMapping(m, new Object, new Object);
108}
109TestMapBehavior1(new Map);
110TestMapBehavior1(new WeakMap);
111
112
113// Test expected mapping behavior for Maps only
114function TestMapBehavior2(m) {
115  for (var i = 0; i < 20; i++) {
116    TestMapping(m, i, new Object);
117    TestMapping(m, i / 10, new Object);
118    TestMapping(m, 'key-' + i, new Object);
119  }
120  // -0 is handled in TestMinusZeroMap
121  var keys = [ 0, +Infinity, -Infinity, true, false, null, undefined ];
122  for (var i = 0; i < keys.length; i++) {
123    TestMapping(m, keys[i], new Object);
124  }
125}
126TestMapBehavior2(new Map);
127
128
129// Test expected querying behavior of Maps and WeakMaps
130function TestQuery(m) {
131  var key = new Object;
132  var values = [ 'x', 0, +Infinity, -Infinity, true, false, null, undefined ];
133  for (var i = 0; i < values.length; i++) {
134    TestMapping(m, key, values[i]);
135    assertTrue(m.has(key));
136    assertFalse(m.has(new Object));
137  }
138}
139TestQuery(new Map);
140TestQuery(new WeakMap);
141
142
143// Test expected deletion behavior of Maps and WeakMaps
144function TestDelete(m) {
145  var key = new Object;
146  TestMapping(m, key, 'to-be-deleted');
147  assertTrue(m.delete(key));
148  assertFalse(m.delete(key));
149  assertFalse(m.delete(new Object));
150  assertSame(m.get(key), undefined);
151}
152TestDelete(new Map);
153TestDelete(new WeakMap);
154
155
156// Test GC of Maps and WeakMaps with entry
157function TestGC1(m) {
158  var key = new Object;
159  m.set(key, 'not-collected');
160  gc();
161  assertSame('not-collected', m.get(key));
162}
163TestGC1(new Map);
164TestGC1(new WeakMap);
165
166
167// Test GC of Maps and WeakMaps with chained entries
168function TestGC2(m) {
169  var head = new Object;
170  for (key = head, i = 0; i < 10; i++, key = m.get(key)) {
171    m.set(key, new Object);
172  }
173  gc();
174  var count = 0;
175  for (key = head; key != undefined; key = m.get(key)) {
176    count++;
177  }
178  assertEquals(11, count);
179}
180TestGC2(new Map);
181TestGC2(new WeakMap);
182
183
184// Test property attribute [[Enumerable]]
185function TestEnumerable(func) {
186  function props(x) {
187    var array = [];
188    for (var p in x) array.push(p);
189    return array.sort();
190  }
191  assertArrayEquals([], props(func));
192  assertArrayEquals([], props(func.prototype));
193  assertArrayEquals([], props(new func()));
194}
195TestEnumerable(Set);
196TestEnumerable(Map);
197TestEnumerable(WeakMap);
198TestEnumerable(WeakSet);
199
200
201// Test arbitrary properties on Maps and WeakMaps
202function TestArbitrary(m) {
203  function TestProperty(map, property, value) {
204    map[property] = value;
205    assertEquals(value, map[property]);
206  }
207  for (var i = 0; i < 20; i++) {
208    TestProperty(m, i, 'val' + i);
209    TestProperty(m, 'foo' + i, 'bar' + i);
210  }
211  TestMapping(m, new Object, 'foobar');
212}
213TestArbitrary(new Map);
214TestArbitrary(new WeakMap);
215
216
217// Test direct constructor call
218assertThrows(function() { Set(); }, TypeError);
219assertThrows(function() { Map(); }, TypeError);
220assertThrows(function() { WeakMap(); }, TypeError);
221assertThrows(function() { WeakSet(); }, TypeError);
222
223
224// Test whether NaN values as keys are treated correctly.
225var s = new Set;
226assertFalse(s.has(NaN));
227assertFalse(s.has(NaN + 1));
228assertFalse(s.has(23));
229s.add(NaN);
230assertTrue(s.has(NaN));
231assertTrue(s.has(NaN + 1));
232assertFalse(s.has(23));
233var m = new Map;
234assertFalse(m.has(NaN));
235assertFalse(m.has(NaN + 1));
236assertFalse(m.has(23));
237m.set(NaN, 'a-value');
238assertTrue(m.has(NaN));
239assertTrue(m.has(NaN + 1));
240assertFalse(m.has(23));
241
242
243// Test some common JavaScript idioms for Sets
244var s = new Set;
245assertTrue(s instanceof Set);
246assertTrue(Set.prototype.add instanceof Function)
247assertTrue(Set.prototype.has instanceof Function)
248assertTrue(Set.prototype.delete instanceof Function)
249assertTrue(Set.prototype.clear instanceof Function)
250
251
252// Test some common JavaScript idioms for Maps
253var m = new Map;
254assertTrue(m instanceof Map);
255assertTrue(Map.prototype.set instanceof Function)
256assertTrue(Map.prototype.get instanceof Function)
257assertTrue(Map.prototype.has instanceof Function)
258assertTrue(Map.prototype.delete instanceof Function)
259assertTrue(Map.prototype.clear instanceof Function)
260
261
262// Test some common JavaScript idioms for WeakMaps
263var m = new WeakMap;
264assertTrue(m instanceof WeakMap);
265assertTrue(WeakMap.prototype.set instanceof Function)
266assertTrue(WeakMap.prototype.get instanceof Function)
267assertTrue(WeakMap.prototype.has instanceof Function)
268assertTrue(WeakMap.prototype.delete instanceof Function)
269assertTrue(WeakMap.prototype.clear instanceof Function)
270
271
272// Test some common JavaScript idioms for WeakSets
273var s = new WeakSet;
274assertTrue(s instanceof WeakSet);
275assertTrue(WeakSet.prototype.add instanceof Function)
276assertTrue(WeakSet.prototype.has instanceof Function)
277assertTrue(WeakSet.prototype.delete instanceof Function)
278assertTrue(WeakSet.prototype.clear instanceof Function)
279
280
281// Test class of instance and prototype.
282assertEquals("Set", %_ClassOf(new Set))
283assertEquals("Object", %_ClassOf(Set.prototype))
284assertEquals("Map", %_ClassOf(new Map))
285assertEquals("Object", %_ClassOf(Map.prototype))
286assertEquals("WeakMap", %_ClassOf(new WeakMap))
287assertEquals("Object", %_ClassOf(WeakMap.prototype))
288assertEquals("WeakSet", %_ClassOf(new WeakSet))
289assertEquals("Object", %_ClassOf(WeakMap.prototype))
290
291
292// Test name of constructor.
293assertEquals("Set", Set.name);
294assertEquals("Map", Map.name);
295assertEquals("WeakMap", WeakMap.name);
296assertEquals("WeakSet", WeakSet.name);
297
298
299// Test prototype property of Set, Map, WeakMap and WeakSet.
300function TestPrototype(C) {
301  assertTrue(C.prototype instanceof Object);
302  assertEquals({
303    value: {},
304    writable: false,
305    enumerable: false,
306    configurable: false
307  }, Object.getOwnPropertyDescriptor(C, "prototype"));
308}
309TestPrototype(Set);
310TestPrototype(Map);
311TestPrototype(WeakMap);
312TestPrototype(WeakSet);
313
314
315// Test constructor property of the Set, Map, WeakMap and WeakSet prototype.
316function TestConstructor(C) {
317  assertFalse(C === Object.prototype.constructor);
318  assertSame(C, C.prototype.constructor);
319  assertSame(C, (new C).__proto__.constructor);
320  assertEquals(1, C.length);
321}
322TestConstructor(Set);
323TestConstructor(Map);
324TestConstructor(WeakMap);
325TestConstructor(WeakSet);
326
327
328// Test the Set, Map, WeakMap and WeakSet global properties themselves.
329function TestDescriptor(global, C) {
330  assertEquals({
331    value: C,
332    writable: true,
333    enumerable: false,
334    configurable: true
335  }, Object.getOwnPropertyDescriptor(global, C.name));
336}
337TestDescriptor(this, Set);
338TestDescriptor(this, Map);
339TestDescriptor(this, WeakMap);
340TestDescriptor(this, WeakSet);
341
342
343// Regression test for WeakMap prototype.
344assertTrue(WeakMap.prototype.constructor === WeakMap)
345assertTrue(Object.getPrototypeOf(WeakMap.prototype) === Object.prototype)
346
347
348// Regression test for issue 1617: The prototype of the WeakMap constructor
349// needs to be unique (i.e. different from the one of the Object constructor).
350assertFalse(WeakMap.prototype === Object.prototype);
351var o = Object.create({});
352assertFalse("get" in o);
353assertFalse("set" in o);
354assertEquals(undefined, o.get);
355assertEquals(undefined, o.set);
356var o = Object.create({}, { myValue: {
357  value: 10,
358  enumerable: false,
359  configurable: true,
360  writable: true
361}});
362assertEquals(10, o.myValue);
363
364
365// Regression test for issue 1884: Invoking any of the methods for Harmony
366// maps, sets, or weak maps, with a wrong type of receiver should be throwing
367// a proper TypeError.
368var alwaysBogus = [ undefined, null, true, "x", 23, {} ];
369var bogusReceiversTestSet = [
370  { proto: Set.prototype,
371    funcs: [ 'add', 'has', 'delete' ],
372    receivers: alwaysBogus.concat([ new Map, new WeakMap, new WeakSet ]),
373  },
374  { proto: Map.prototype,
375    funcs: [ 'get', 'set', 'has', 'delete' ],
376    receivers: alwaysBogus.concat([ new Set, new WeakMap, new WeakSet ]),
377  },
378  { proto: WeakMap.prototype,
379    funcs: [ 'get', 'set', 'has', 'delete' ],
380    receivers: alwaysBogus.concat([ new Set, new Map, new WeakSet ]),
381  },
382  { proto: WeakSet.prototype,
383    funcs: [ 'add', 'has', 'delete' ],
384    receivers: alwaysBogus.concat([ new Set, new Map, new WeakMap ]),
385  },
386];
387function TestBogusReceivers(testSet) {
388  for (var i = 0; i < testSet.length; i++) {
389    var proto = testSet[i].proto;
390    var funcs = testSet[i].funcs;
391    var receivers = testSet[i].receivers;
392    for (var j = 0; j < funcs.length; j++) {
393      var func = proto[funcs[j]];
394      for (var k = 0; k < receivers.length; k++) {
395        assertThrows(function () { func.call(receivers[k], {}) }, TypeError);
396      }
397    }
398  }
399}
400TestBogusReceivers(bogusReceiversTestSet);
401
402
403// Stress Test
404// There is a proposed stress-test available at the es-discuss mailing list
405// which cannot be reasonably automated.  Check it out by hand if you like:
406// https://mail.mozilla.org/pipermail/es-discuss/2011-May/014096.html
407
408
409// Set and Map size getters
410var setSizeDescriptor = Object.getOwnPropertyDescriptor(Set.prototype, 'size');
411assertEquals(undefined, setSizeDescriptor.value);
412assertEquals(undefined, setSizeDescriptor.set);
413assertTrue(setSizeDescriptor.get instanceof Function);
414assertEquals(undefined, setSizeDescriptor.get.prototype);
415assertFalse(setSizeDescriptor.enumerable);
416assertTrue(setSizeDescriptor.configurable);
417
418var s = new Set();
419assertFalse(s.hasOwnProperty('size'));
420for (var i = 0; i < 10; i++) {
421  assertEquals(i, s.size);
422  s.add(i);
423}
424for (var i = 9; i >= 0; i--) {
425  s.delete(i);
426  assertEquals(i, s.size);
427}
428
429
430var mapSizeDescriptor = Object.getOwnPropertyDescriptor(Map.prototype, 'size');
431assertEquals(undefined, mapSizeDescriptor.value);
432assertEquals(undefined, mapSizeDescriptor.set);
433assertTrue(mapSizeDescriptor.get instanceof Function);
434assertEquals(undefined, mapSizeDescriptor.get.prototype);
435assertFalse(mapSizeDescriptor.enumerable);
436assertTrue(mapSizeDescriptor.configurable);
437
438var m = new Map();
439assertFalse(m.hasOwnProperty('size'));
440for (var i = 0; i < 10; i++) {
441  assertEquals(i, m.size);
442  m.set(i, i);
443}
444for (var i = 9; i >= 0; i--) {
445  m.delete(i);
446  assertEquals(i, m.size);
447}
448
449
450// Test Set clear
451(function() {
452  var s = new Set();
453  s.add(42);
454  assertTrue(s.has(42));
455  assertEquals(1, s.size);
456  s.clear();
457  assertFalse(s.has(42));
458  assertEquals(0, s.size);
459})();
460
461
462// Test Map clear
463(function() {
464  var m = new Map();
465  m.set(42, true);
466  assertTrue(m.has(42));
467  assertEquals(1, m.size);
468  m.clear();
469  assertFalse(m.has(42));
470  assertEquals(0, m.size);
471})();
472
473
474// Test WeakMap clear
475(function() {
476  var k = new Object();
477  var w = new WeakMap();
478  w.set(k, 23);
479  assertTrue(w.has(k));
480  assertEquals(23, w.get(k));
481  w.clear();
482  assertFalse(w.has(k));
483  assertEquals(undefined, w.get(k));
484})();
485
486
487// Test WeakSet clear
488(function() {
489  var k = new Object();
490  var w = new WeakSet();
491  w.add(k);
492  assertTrue(w.has(k));
493  w.clear();
494  assertFalse(w.has(k));
495})();
496
497
498(function TestMinusZeroSet() {
499  var s = new Set();
500  s.add(-0);
501  assertSame(0, s.values().next().value);
502  s.add(0);
503  assertEquals(1, s.size);
504  assertTrue(s.has(0));
505  assertTrue(s.has(-0));
506})();
507
508
509(function TestMinusZeroMap() {
510  var m = new Map();
511  m.set(-0, 'minus');
512  assertSame(0, m.keys().next().value);
513  m.set(0, 'plus');
514  assertEquals(1, m.size);
515  assertTrue(m.has(0));
516  assertTrue(m.has(-0));
517  assertEquals('plus', m.get(0));
518  assertEquals('plus', m.get(-0));
519})();
520
521
522(function TestSetForEachInvalidTypes() {
523  assertThrows(function() {
524    Set.prototype.set.forEach.call({});
525  }, TypeError);
526
527  var set = new Set();
528  assertThrows(function() {
529    set.forEach({});
530  }, TypeError);
531})();
532
533
534(function TestSetForEach() {
535  var set = new Set();
536  set.add('a');
537  set.add('b');
538  set.add('c');
539
540  var buffer = '';
541  var receiver = {};
542  set.forEach(function(v, k, s) {
543    assertSame(v, k);
544    assertSame(set, s);
545    assertSame(this, receiver);
546    buffer += v;
547    if (v === 'a') {
548      set.delete('b');
549      set.add('d');
550      set.add('e');
551      set.add('f');
552    } else if (v === 'c') {
553      set.add('b');
554      set.delete('e');
555    }
556  }, receiver);
557
558  assertEquals('acdfb', buffer);
559})();
560
561
562(function TestSetForEachAddAtEnd() {
563  var set = new Set();
564  set.add('a');
565  set.add('b');
566
567  var buffer = '';
568  set.forEach(function(v) {
569    buffer += v;
570    if (v === 'b') {
571      set.add('c');
572    }
573  });
574
575  assertEquals('abc', buffer);
576})();
577
578
579(function TestSetForEachDeleteNext() {
580  var set = new Set();
581  set.add('a');
582  set.add('b');
583  set.add('c');
584
585  var buffer = '';
586  set.forEach(function(v) {
587    buffer += v;
588    if (v === 'b') {
589      set.delete('c');
590    }
591  });
592
593  assertEquals('ab', buffer);
594})();
595
596
597(function TestSetForEachDeleteVisitedAndAddAgain() {
598  var set = new Set();
599  set.add('a');
600  set.add('b');
601  set.add('c');
602
603  var buffer = '';
604  set.forEach(function(v) {
605    buffer += v;
606    if (v === 'b') {
607      set.delete('a');
608    } else if (v === 'c') {
609      set.add('a');
610    }
611  });
612
613  assertEquals('abca', buffer);
614})();
615
616
617(function TestSetForEachClear() {
618  var set = new Set();
619  set.add('a');
620  set.add('b');
621  set.add('c');
622
623  var buffer = '';
624  set.forEach(function(v) {
625    buffer += v;
626    if (v === 'a') {
627      set.clear();
628      set.add('d');
629      set.add('e');
630    }
631  });
632
633  assertEquals('ade', buffer);
634})();
635
636
637(function TestSetForEachNested() {
638  var set = new Set();
639  set.add('a');
640  set.add('b');
641  set.add('c');
642
643  var buffer = '';
644  set.forEach(function(v) {
645    buffer += v;
646    set.forEach(function(v) {
647      buffer += v;
648      if (v === 'a') {
649        set.delete('b');
650      }
651    });
652  });
653
654  assertEquals('aaccac', buffer);
655})();
656
657
658(function TestSetForEachEarlyExit() {
659  var set = new Set();
660  set.add('a');
661  set.add('b');
662  set.add('c');
663
664  var buffer = '';
665  var ex = {};
666  try {
667    set.forEach(function(v) {
668      buffer += v;
669      throw ex;
670    });
671  } catch (e) {
672    assertEquals(ex, e);
673  }
674  assertEquals('a', buffer);
675})();
676
677
678(function TestSetForEachGC() {
679  var set = new Set();
680  for (var i = 0; i < 100; i++) {
681    set.add(i);
682  }
683
684  var accumulated = 0;
685  set.forEach(function(v) {
686    accumulated += v;
687    if (v % 10 === 0) {
688      gc();
689    }
690  });
691  assertEquals(4950, accumulated);
692})();
693
694(function TestMapForEachInvalidTypes() {
695  assertThrows(function() {
696    Map.prototype.map.forEach.call({});
697  }, TypeError);
698
699  var map = new Map();
700  assertThrows(function() {
701    map.forEach({});
702  }, TypeError);
703})();
704
705
706(function TestMapForEach() {
707  var map = new Map();
708  map.set(0, 'a');
709  map.set(1, 'b');
710  map.set(2, 'c');
711
712  var buffer = [];
713  var receiver = {};
714  map.forEach(function(v, k, m) {
715    assertEquals(map, m);
716    assertEquals(this, receiver);
717    buffer.push(k, v);
718    if (k === 0) {
719      map.delete(1);
720      map.set(3, 'd');
721      map.set(4, 'e');
722      map.set(5, 'f');
723    } else if (k === 2) {
724      map.set(1, 'B');
725      map.delete(4);
726    }
727  }, receiver);
728
729  assertArrayEquals([0, 'a', 2, 'c', 3, 'd', 5, 'f', 1, 'B'], buffer);
730})();
731
732
733(function TestMapForEachAddAtEnd() {
734  var map = new Map();
735  map.set(0, 'a');
736  map.set(1, 'b');
737
738  var buffer = [];
739  map.forEach(function(v, k) {
740    buffer.push(k, v);
741    if (k === 1) {
742      map.set(2, 'c');
743    }
744  });
745
746  assertArrayEquals([0, 'a', 1, 'b', 2, 'c'], buffer);
747})();
748
749
750(function TestMapForEachDeleteNext() {
751  var map = new Map();
752  map.set(0, 'a');
753  map.set(1, 'b');
754  map.set(2, 'c');
755
756  var buffer = [];
757  map.forEach(function(v, k) {
758    buffer.push(k, v);
759    if (k === 1) {
760      map.delete(2);
761    }
762  });
763
764  assertArrayEquals([0, 'a', 1, 'b'], buffer);
765})();
766
767
768(function TestSetForEachDeleteVisitedAndAddAgain() {
769  var map = new Map();
770  map.set(0, 'a');
771  map.set(1, 'b');
772  map.set(2, 'c');
773
774  var buffer = [];
775  map.forEach(function(v, k) {
776    buffer.push(k, v);
777    if (k === 1) {
778      map.delete(0);
779    } else if (k === 2) {
780      map.set(0, 'a');
781    }
782  });
783
784  assertArrayEquals([0, 'a', 1, 'b', 2, 'c', 0, 'a'], buffer);
785})();
786
787
788(function TestMapForEachClear() {
789  var map = new Map();
790  map.set(0, 'a');
791  map.set(1, 'b');
792  map.set(2, 'c');
793
794  var buffer = [];
795  map.forEach(function(v, k) {
796    buffer.push(k, v);
797    if (k === 0) {
798      map.clear();
799      map.set(3, 'd');
800      map.set(4, 'e');
801    }
802  });
803
804  assertArrayEquals([0, 'a', 3, 'd', 4, 'e'], buffer);
805})();
806
807
808(function TestMapForEachNested() {
809  var map = new Map();
810  map.set(0, 'a');
811  map.set(1, 'b');
812  map.set(2, 'c');
813
814  var buffer = [];
815  map.forEach(function(v, k) {
816    buffer.push(k, v);
817    map.forEach(function(v, k) {
818      buffer.push(k, v);
819      if (k === 0) {
820        map.delete(1);
821      }
822    });
823  });
824
825  assertArrayEquals([0, 'a', 0, 'a', 2, 'c', 2, 'c', 0, 'a', 2, 'c'], buffer);
826})();
827
828
829(function TestMapForEachEarlyExit() {
830  var map = new Map();
831  map.set(0, 'a');
832  map.set(1, 'b');
833  map.set(2, 'c');
834
835  var buffer = [];
836  var ex = {};
837  try {
838    map.forEach(function(v, k) {
839      buffer.push(k, v);
840      throw ex;
841    });
842  } catch (e) {
843    assertEquals(ex, e);
844  }
845  assertArrayEquals([0, 'a'], buffer);
846})();
847
848
849(function TestMapForEachGC() {
850  var map = new Map();
851  for (var i = 0; i < 100; i++) {
852    map.set(i, i);
853  }
854
855  var accumulated = 0;
856  map.forEach(function(v) {
857    accumulated += v;
858    if (v % 10 === 0) {
859      gc();
860    }
861  });
862  assertEquals(4950, accumulated);
863})();
864
865
866(function TestMapForEachAllRemovedTransition() {
867  var map = new Map;
868  map.set(0, 0);
869
870  var buffer = [];
871  map.forEach(function(v) {
872    buffer.push(v);
873    if (v === 0) {
874      for (var i = 1; i < 4; i++) {
875        map.set(i, i);
876      }
877    }
878
879    if (v === 3) {
880      for (var i = 0; i < 4; i++) {
881        map.delete(i);
882      }
883      for (var i = 4; i < 8; i++) {
884        map.set(i, i);
885      }
886    }
887  });
888
889  assertArrayEquals([0, 1, 2, 3, 4, 5, 6, 7], buffer);
890})();
891
892
893(function TestMapForEachClearTransition() {
894  var map = new Map;
895  map.set(0, 0);
896
897  var i = 0;
898  var buffer = [];
899  map.forEach(function(v) {
900    buffer.push(v);
901    if (++i < 5) {
902      for (var j = 0; j < 5; j++) {
903        map.clear();
904        map.set(i, i);
905      }
906    }
907  });
908
909  assertArrayEquals([0, 1, 2, 3, 4], buffer);
910})();
911
912
913(function TestMapForEachNestedNonTrivialTransition() {
914  var map = new Map;
915  map.set(0, 0);
916  map.set(1, 1);
917  map.set(2, 2);
918  map.set(3, 3);
919  map.delete(0);
920
921  var i = 0;
922  var buffer = [];
923  map.forEach(function(v) {
924    if (++i > 10) return;
925
926    buffer.push(v);
927
928    if (v == 3) {
929      map.delete(1);
930      for (var j = 4; j < 10; j++) {
931        map.set(j, j);
932      }
933      for (var j = 4; j < 10; j += 2) {
934        map.delete(j);
935      }
936      map.delete(2);
937
938      for (var j = 10; j < 20; j++) {
939        map.set(j, j);
940      }
941      for (var j = 10; j < 20; j += 2) {
942        map.delete(j);
943      }
944
945      map.delete(3);
946    }
947  });
948
949  assertArrayEquals([1, 2, 3, 5, 7, 9, 11, 13, 15, 17], buffer);
950})();
951
952
953(function TestMapForEachAllRemovedTransitionNoClear() {
954  var map = new Map;
955  map.set(0, 0);
956
957  var buffer = [];
958  map.forEach(function(v) {
959    buffer.push(v);
960    if (v === 0) {
961      for (var i = 1; i < 8; i++) {
962        map.set(i, i);
963      }
964    }
965
966    if (v === 4) {
967      for (var i = 0; i < 8; i++) {
968        map.delete(i);
969      }
970    }
971  });
972
973  assertArrayEquals([0, 1, 2, 3, 4], buffer);
974})();
975
976
977(function TestMapForEachNoMoreElementsAfterTransition() {
978  var map = new Map;
979  map.set(0, 0);
980
981  var buffer = [];
982  map.forEach(function(v) {
983    buffer.push(v);
984    if (v === 0) {
985      for (var i = 1; i < 16; i++) {
986        map.set(i, i);
987      }
988    }
989
990    if (v === 4) {
991      for (var i = 5; i < 16; i++) {
992        map.delete(i);
993      }
994    }
995  });
996
997  assertArrayEquals([0, 1, 2, 3, 4], buffer);
998})();
999
1000
1001// Allows testing iterator-based constructors easily.
1002var oneAndTwo = new Map();
1003var k0 = {key: 0};
1004var k1 = {key: 1};
1005var k2 = {key: 2};
1006oneAndTwo.set(k1, 1);
1007oneAndTwo.set(k2, 2);
1008
1009
1010function TestSetConstructor(ctor) {
1011  var s = new ctor(null);
1012  assertSize(0, s);
1013
1014  s = new ctor(undefined);
1015  assertSize(0, s);
1016
1017  // No @@iterator
1018  assertThrows(function() {
1019    new ctor({});
1020  }, TypeError);
1021  assertThrows(function() {
1022    new ctor(true);
1023  }, TypeError);
1024
1025  // @@iterator not callable
1026  assertThrows(function() {
1027    var object = {};
1028    object[Symbol.iterator] = 42;
1029    new ctor(object);
1030  }, TypeError);
1031
1032  // @@iterator result not object
1033  assertThrows(function() {
1034    var object = {};
1035    object[Symbol.iterator] = function() {
1036      return 42;
1037    };
1038    new ctor(object);
1039  }, TypeError);
1040
1041  var s2 = new Set();
1042  s2.add(k0);
1043  s2.add(k1);
1044  s2.add(k2);
1045  s = new ctor(s2.values());
1046  assertSize(3, s);
1047  assertTrue(s.has(k0));
1048  assertTrue(s.has(k1));
1049  assertTrue(s.has(k2));
1050}
1051TestSetConstructor(Set);
1052TestSetConstructor(WeakSet);
1053
1054
1055function TestSetConstructorAddNotCallable(ctor) {
1056  var originalPrototypeAdd = ctor.prototype.add;
1057  assertThrows(function() {
1058    ctor.prototype.add = 42;
1059    new ctor(oneAndTwo.values());
1060  }, TypeError);
1061  ctor.prototype.add = originalPrototypeAdd;
1062}
1063TestSetConstructorAddNotCallable(Set);
1064TestSetConstructorAddNotCallable(WeakSet);
1065
1066
1067function TestSetConstructorGetAddOnce(ctor) {
1068  var originalPrototypeAdd = ctor.prototype.add;
1069  var getAddCount = 0;
1070  Object.defineProperty(ctor.prototype, 'add', {
1071    get: function() {
1072      getAddCount++;
1073      return function() {};
1074    }
1075  });
1076  var s = new ctor(oneAndTwo.values());
1077  assertEquals(1, getAddCount);
1078  assertSize(0, s);
1079  Object.defineProperty(ctor.prototype, 'add', {
1080    value: originalPrototypeAdd,
1081    writable: true
1082  });
1083}
1084TestSetConstructorGetAddOnce(Set);
1085TestSetConstructorGetAddOnce(WeakSet);
1086
1087
1088function TestSetConstructorAddReplaced(ctor) {
1089  var originalPrototypeAdd = ctor.prototype.add;
1090  var addCount = 0;
1091  ctor.prototype.add = function(value) {
1092    addCount++;
1093    originalPrototypeAdd.call(this, value);
1094    ctor.prototype.add = null;
1095  };
1096  var s = new ctor(oneAndTwo.keys());
1097  assertEquals(2, addCount);
1098  assertSize(2, s);
1099  ctor.prototype.add = originalPrototypeAdd;
1100}
1101TestSetConstructorAddReplaced(Set);
1102TestSetConstructorAddReplaced(WeakSet);
1103
1104
1105function TestSetConstructorOrderOfDoneValue(ctor) {
1106  var valueCount = 0, doneCount = 0;
1107  var iterator = {
1108    next: function() {
1109      return {
1110        get value() {
1111          valueCount++;
1112        },
1113        get done() {
1114          doneCount++;
1115          throw new Error();
1116        }
1117      };
1118    }
1119  };
1120  iterator[Symbol.iterator] = function() {
1121    return this;
1122  };
1123  assertThrows(function() {
1124    new ctor(iterator);
1125  });
1126  assertEquals(1, doneCount);
1127  assertEquals(0, valueCount);
1128}
1129TestSetConstructorOrderOfDoneValue(Set);
1130TestSetConstructorOrderOfDoneValue(WeakSet);
1131
1132
1133function TestSetConstructorNextNotAnObject(ctor) {
1134  var iterator = {
1135    next: function() {
1136      return 'abc';
1137    }
1138  };
1139  iterator[Symbol.iterator] = function() {
1140    return this;
1141  };
1142  assertThrows(function() {
1143    new ctor(iterator);
1144  }, TypeError);
1145}
1146TestSetConstructorNextNotAnObject(Set);
1147TestSetConstructorNextNotAnObject(WeakSet);
1148
1149
1150(function TestWeakSetConstructorNonObjectKeys() {
1151  assertThrows(function() {
1152    new WeakSet([1]);
1153  }, TypeError);
1154})();
1155
1156
1157function TestSetConstructorIterableValue(ctor) {
1158  'use strict';
1159  // Strict mode is required to prevent implicit wrapping in the getter.
1160  Object.defineProperty(Number.prototype, Symbol.iterator, {
1161    get: function() {
1162      assertEquals('object', typeof this);
1163      return function() {
1164        return oneAndTwo.keys();
1165      };
1166    },
1167    configurable: true
1168  });
1169
1170  var set = new ctor(42);
1171  assertSize(2, set);
1172  assertTrue(set.has(k1));
1173  assertTrue(set.has(k2));
1174
1175  delete Number.prototype[Symbol.iterator];
1176}
1177TestSetConstructorIterableValue(Set);
1178TestSetConstructorIterableValue(WeakSet);
1179
1180
1181(function TestSetConstructorStringValue() {
1182  var s = new Set('abc');
1183  assertSize(3, s);
1184  assertTrue(s.has('a'));
1185  assertTrue(s.has('b'));
1186  assertTrue(s.has('c'));
1187})();
1188
1189
1190function TestMapConstructor(ctor) {
1191  var m = new ctor(null);
1192  assertSize(0, m);
1193
1194  m = new ctor(undefined);
1195  assertSize(0, m);
1196
1197  // No @@iterator
1198  assertThrows(function() {
1199    new ctor({});
1200  }, TypeError);
1201  assertThrows(function() {
1202    new ctor(true);
1203  }, TypeError);
1204
1205  // @@iterator not callable
1206  assertThrows(function() {
1207    var object = {};
1208    object[Symbol.iterator] = 42;
1209    new ctor(object);
1210  }, TypeError);
1211
1212  // @@iterator result not object
1213  assertThrows(function() {
1214    var object = {};
1215    object[Symbol.iterator] = function() {
1216      return 42;
1217    };
1218    new ctor(object);
1219  }, TypeError);
1220
1221  var m2 = new Map();
1222  m2.set(k0, 'a');
1223  m2.set(k1, 'b');
1224  m2.set(k2, 'c');
1225  m = new ctor(m2.entries());
1226  assertSize(3, m);
1227  assertEquals('a', m.get(k0));
1228  assertEquals('b', m.get(k1));
1229  assertEquals('c', m.get(k2));
1230}
1231TestMapConstructor(Map);
1232TestMapConstructor(WeakMap);
1233
1234
1235function TestMapConstructorSetNotCallable(ctor) {
1236  var originalPrototypeSet = ctor.prototype.set;
1237  assertThrows(function() {
1238    ctor.prototype.set = 42;
1239    new ctor(oneAndTwo.entries());
1240  }, TypeError);
1241  ctor.prototype.set = originalPrototypeSet;
1242}
1243TestMapConstructorSetNotCallable(Map);
1244TestMapConstructorSetNotCallable(WeakMap);
1245
1246
1247function TestMapConstructorGetAddOnce(ctor) {
1248  var originalPrototypeSet = ctor.prototype.set;
1249  var getSetCount = 0;
1250  Object.defineProperty(ctor.prototype, 'set', {
1251    get: function() {
1252      getSetCount++;
1253      return function() {};
1254    }
1255  });
1256  var m = new ctor(oneAndTwo.entries());
1257  assertEquals(1, getSetCount);
1258  assertSize(0, m);
1259  Object.defineProperty(ctor.prototype, 'set', {
1260    value: originalPrototypeSet,
1261    writable: true
1262  });
1263}
1264TestMapConstructorGetAddOnce(Map);
1265TestMapConstructorGetAddOnce(WeakMap);
1266
1267
1268function TestMapConstructorSetReplaced(ctor) {
1269  var originalPrototypeSet = ctor.prototype.set;
1270  var setCount = 0;
1271  ctor.prototype.set = function(key, value) {
1272    setCount++;
1273    originalPrototypeSet.call(this, key, value);
1274    ctor.prototype.set = null;
1275  };
1276  var m = new ctor(oneAndTwo.entries());
1277  assertEquals(2, setCount);
1278  assertSize(2, m);
1279  ctor.prototype.set = originalPrototypeSet;
1280}
1281TestMapConstructorSetReplaced(Map);
1282TestMapConstructorSetReplaced(WeakMap);
1283
1284
1285function TestMapConstructorOrderOfDoneValue(ctor) {
1286  var valueCount = 0, doneCount = 0;
1287  function FakeError() {}
1288  var iterator = {
1289    next: function() {
1290      return {
1291        get value() {
1292          valueCount++;
1293        },
1294        get done() {
1295          doneCount++;
1296          throw new FakeError();
1297        }
1298      };
1299    }
1300  };
1301  iterator[Symbol.iterator] = function() {
1302    return this;
1303  };
1304  assertThrows(function() {
1305    new ctor(iterator);
1306  }, FakeError);
1307  assertEquals(1, doneCount);
1308  assertEquals(0, valueCount);
1309}
1310TestMapConstructorOrderOfDoneValue(Map);
1311TestMapConstructorOrderOfDoneValue(WeakMap);
1312
1313
1314function TestMapConstructorNextNotAnObject(ctor) {
1315  var iterator = {
1316    next: function() {
1317      return 'abc';
1318    }
1319  };
1320  iterator[Symbol.iterator] = function() {
1321    return this;
1322  };
1323  assertThrows(function() {
1324    new ctor(iterator);
1325  }, TypeError);
1326}
1327TestMapConstructorNextNotAnObject(Map);
1328TestMapConstructorNextNotAnObject(WeakMap);
1329
1330
1331function TestMapConstructorIteratorNotObjectValues(ctor) {
1332  assertThrows(function() {
1333    new ctor(oneAndTwo.values());
1334  }, TypeError);
1335}
1336TestMapConstructorIteratorNotObjectValues(Map);
1337TestMapConstructorIteratorNotObjectValues(WeakMap);
1338
1339
1340(function TestWeakMapConstructorNonObjectKeys() {
1341  assertThrows(function() {
1342    new WeakMap([[1, 2]])
1343  }, TypeError);
1344})();
1345
1346
1347function TestMapConstructorIterableValue(ctor) {
1348  'use strict';
1349  // Strict mode is required to prevent implicit wrapping in the getter.
1350  Object.defineProperty(Number.prototype, Symbol.iterator, {
1351    get: function() {
1352      assertEquals('object', typeof this);
1353      return function() {
1354        return oneAndTwo.entries();
1355      };
1356    },
1357    configurable: true
1358  });
1359
1360  var map = new ctor(42);
1361  assertSize(2, map);
1362  assertEquals(1, map.get(k1));
1363  assertEquals(2, map.get(k2));
1364
1365  delete Number.prototype[Symbol.iterator];
1366}
1367TestMapConstructorIterableValue(Map);
1368TestMapConstructorIterableValue(WeakMap);
1369