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