1// Copyright 2014 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// Largely ported from 6// https://github.com/tc39/Array.prototype.includes/tree/master/test 7// using https://www.npmjs.org/package/test262-to-mjsunit with further edits 8 9 10// Array.prototype.includes sees a new element added by a getter that is hit 11// during iteration 12(function() { 13 var arrayLike = { 14 length: 5, 15 0: "a", 16 17 get 1() { 18 this[2] = "c"; 19 return "b"; 20 } 21 }; 22 23 assertTrue(Array.prototype.includes.call(arrayLike, "c")); 24})(); 25 26 27// Array.prototype.includes works on array-like objects 28(function() { 29 var arrayLike1 = { 30 length: 5, 31 0: "a", 32 1: "b" 33 }; 34 35 assertTrue(Array.prototype.includes.call(arrayLike1, "a")); 36 assertFalse(Array.prototype.includes.call(arrayLike1, "c")); 37 38 var arrayLike2 = { 39 length: 2, 40 0: "a", 41 1: "b", 42 2: "c" 43 }; 44 45 assertTrue(Array.prototype.includes.call(arrayLike2, "b")); 46 assertFalse(Array.prototype.includes.call(arrayLike2, "c")); 47})(); 48 49 50// Array.prototype.includes should fail if used on a null or undefined this 51(function() { 52 assertThrows(function() { 53 Array.prototype.includes.call(null, "a"); 54 }, TypeError); 55 56 assertThrows(function() { 57 Array.prototype.includes.call(undefined, "a"); 58 }, TypeError); 59})(); 60 61 62// Array.prototype.includes should terminate if getting an index throws an 63// exception 64(function() { 65 function Test262Error() {} 66 67 var trappedZero = { 68 length: 2, 69 70 get 0() { 71 throw new Test262Error(); 72 }, 73 74 get 1() { 75 assertUnreachable("Should not try to get the first element"); 76 } 77 }; 78 79 assertThrows(function() { 80 Array.prototype.includes.call(trappedZero, "a"); 81 }, Test262Error); 82})(); 83 84 85// Array.prototype.includes should terminate if ToNumber ends up being called on 86// a symbol fromIndex 87(function() { 88 var trappedZero = { 89 length: 1, 90 91 get 0() { 92 assertUnreachable("Should not try to get the zeroth element"); 93 } 94 }; 95 96 assertThrows(function() { 97 Array.prototype.includes.call(trappedZero, "a", Symbol()); 98 }, TypeError); 99})(); 100 101 102// Array.prototype.includes should terminate if an exception occurs converting 103// the fromIndex to a number 104(function() { 105 function Test262Error() {} 106 107 var fromIndex = { 108 valueOf: function() { 109 throw new Test262Error(); 110 } 111 }; 112 113 var trappedZero = { 114 length: 1, 115 116 get 0() { 117 assertUnreachable("Should not try to get the zeroth element"); 118 } 119 }; 120 121 assertThrows(function() { 122 Array.prototype.includes.call(trappedZero, "a", fromIndex); 123 }, Test262Error); 124})(); 125 126 127// Array.prototype.includes should terminate if an exception occurs getting the 128// length 129(function() { 130 function Test262Error() {} 131 132 var fromIndexTrap = { 133 valueOf: function() { 134 assertUnreachable("Should not try to call ToInteger on valueOf"); 135 } 136 }; 137 138 var throwingLength = { 139 get length() { 140 throw new Test262Error(); 141 }, 142 143 get 0() { 144 assertUnreachable("Should not try to get the zeroth element"); 145 } 146 }; 147 148 assertThrows(function() { 149 Array.prototype.includes.call(throwingLength, "a", fromIndexTrap); 150 }, Test262Error); 151})(); 152 153 154// Array.prototype.includes should terminate if ToLength ends up being called on 155// a symbol length 156(function() { 157 var fromIndexTrap = { 158 valueOf: function() { 159 assertUnreachable("Should not try to call ToInteger on valueOf"); 160 } 161 }; 162 163 var badLength = { 164 length: Symbol(), 165 166 get 0() { 167 assertUnreachable("Should not try to get the zeroth element"); 168 } 169 }; 170 171 assertThrows(function() { 172 Array.prototype.includes.call(badLength, "a", fromIndexTrap); 173 }, TypeError); 174})(); 175 176 177// Array.prototype.includes should terminate if an exception occurs converting 178// the length to a number 179(function() { 180 function Test262Error() {} 181 182 var fromIndexTrap = { 183 valueOf: function() { 184 assertUnreachable("Should not try to call ToInteger on valueOf"); 185 } 186 }; 187 188 var badLength = { 189 length: { 190 valueOf: function() { 191 throw new Test262Error(); 192 } 193 }, 194 195 get 0() { 196 assertUnreachable("Should not try to get the zeroth element"); 197 } 198 }; 199 200 assertThrows(function() { 201 Array.prototype.includes.call(badLength, "a", fromIndexTrap); 202 }, Test262Error); 203})(); 204 205 206// Array.prototype.includes should search the whole array, as the optional 207// second argument fromIndex defaults to 0 208(function() { 209 assertTrue([10, 11].includes(10)); 210 assertTrue([10, 11].includes(11)); 211 212 var arrayLike = { 213 length: 2, 214 215 get 0() { 216 return "1"; 217 }, 218 219 get 1() { 220 return "2"; 221 } 222 }; 223 224 assertTrue(Array.prototype.includes.call(arrayLike, "1")); 225 assertTrue(Array.prototype.includes.call(arrayLike, "2")); 226})(); 227 228 229// Array.prototype.includes returns false if fromIndex is greater or equal to 230// the length of the array 231(function() { 232 assertFalse([1, 2].includes(2, 3)); 233 assertFalse([1, 2].includes(2, 2)); 234 235 var arrayLikeWithTrap = { 236 length: 2, 237 238 get 0() { 239 assertUnreachable("Getter for 0 was called"); 240 }, 241 242 get 1() { 243 assertUnreachable("Getter for 1 was called"); 244 } 245 }; 246 247 assertFalse(Array.prototype.includes.call(arrayLikeWithTrap, "c", 2)); 248 assertFalse(Array.prototype.includes.call(arrayLikeWithTrap, "c", 3)); 249})(); 250 251 252// Array.prototype.includes searches the whole array if the computed index from 253// the given negative fromIndex argument is less than 0 254(function() { 255 assertTrue([1, 3].includes(1, -4)); 256 assertTrue([1, 3].includes(3, -4)); 257 258 var arrayLike = { 259 length: 2, 260 0: "a", 261 262 get 1() { 263 return "b"; 264 }, 265 266 get "-1"() { 267 assertUnreachable("Should not try to get the element at index -1"); 268 } 269 }; 270 271 assertTrue(Array.prototype.includes.call(arrayLike, "a", -4)); 272 assertTrue(Array.prototype.includes.call(arrayLike, "b", -4)); 273})(); 274 275 276// Array.prototype.includes should use a negative value as the offset from the 277// end of the array to compute fromIndex 278(function() { 279 assertTrue([12, 13].includes(13, -1)); 280 assertFalse([12, 13].includes(12, -1)); 281 assertTrue([12, 13].includes(12, -2)); 282 283 var arrayLike = { 284 length: 2, 285 286 get 0() { 287 return "a"; 288 }, 289 290 get 1() { 291 return "b"; 292 } 293 }; 294 295 assertTrue(Array.prototype.includes.call(arrayLike, "b", -1)); 296 assertFalse(Array.prototype.includes.call(arrayLike, "a", -1)); 297 assertTrue(Array.prototype.includes.call(arrayLike, "a", -2)); 298})(); 299 300 301// Array.prototype.includes converts its fromIndex parameter to an integer 302(function() { 303 assertFalse(["a", "b"].includes("a", 2.3)); 304 305 var arrayLikeWithTraps = { 306 length: 2, 307 308 get 0() { 309 assertUnreachable("Getter for 0 was called"); 310 }, 311 312 get 1() { 313 assertUnreachable("Getter for 1 was called"); 314 } 315 }; 316 317 assertFalse(Array.prototype.includes.call(arrayLikeWithTraps, "c", 2.1)); 318 assertFalse(Array.prototype.includes.call(arrayLikeWithTraps, "c", +Infinity)); 319 assertTrue(["a", "b", "c"].includes("a", -Infinity)); 320 assertTrue(["a", "b", "c"].includes("c", 2.9)); 321 assertTrue(["a", "b", "c"].includes("c", NaN)); 322 323 var arrayLikeWithTrapAfterZero = { 324 length: 2, 325 326 get 0() { 327 return "a"; 328 }, 329 330 get 1() { 331 assertUnreachable("Getter for 1 was called"); 332 } 333 }; 334 335 assertTrue(Array.prototype.includes.call(arrayLikeWithTrapAfterZero, "a", NaN)); 336 337 var numberLike = { 338 valueOf: function() { 339 return 2; 340 } 341 }; 342 343 assertFalse(["a", "b", "c"].includes("a", numberLike)); 344 assertFalse(["a", "b", "c"].includes("a", "2")); 345 assertTrue(["a", "b", "c"].includes("c", numberLike)); 346 assertTrue(["a", "b", "c"].includes("c", "2")); 347})(); 348 349 350// Array.prototype.includes should have length 1 351(function() { 352 assertEquals(1, Array.prototype.includes.length); 353})(); 354 355 356// Array.prototype.includes should have name property with value 'includes' 357(function() { 358 assertEquals("includes", Array.prototype.includes.name); 359})(); 360 361 362// !!! Test failed to convert: 363// Cannot convert tests with includes. 364// !!! 365 366 367// Array.prototype.includes does not skip holes; if the array has a prototype it 368// gets from that 369(function() { 370 var holesEverywhere = [,,,]; 371 372 holesEverywhere.__proto__ = { 373 1: "a" 374 }; 375 376 holesEverywhere.__proto__.__proto__ = Array.prototype; 377 assertTrue(holesEverywhere.includes("a")); 378 var oneHole = ["a", "b",, "d"]; 379 380 oneHole.__proto__ = { 381 get 2() { 382 return "c"; 383 } 384 }; 385 386 assertTrue(Array.prototype.includes.call(oneHole, "c")); 387})(); 388 389 390// Array.prototype.includes does not skip holes; instead it treates them as 391// undefined 392(function() { 393 assertTrue([,,,].includes(undefined)); 394 assertTrue(["a", "b",, "d"].includes(undefined)); 395})(); 396 397 398// Array.prototype.includes gets length property from the prototype if it's 399// available 400(function() { 401 var proto = { 402 length: 1 403 }; 404 405 var arrayLike = Object.create(proto); 406 arrayLike[0] = "a"; 407 408 Object.defineProperty(arrayLike, "1", { 409 get: function() { 410 assertUnreachable("Getter for 1 was called"); 411 } 412 }); 413 414 assertTrue(Array.prototype.includes.call(arrayLike, "a")); 415})(); 416 417 418// Array.prototype.includes treats a missing length property as zero 419(function() { 420 var arrayLikeWithTraps = { 421 get 0() { 422 assertUnreachable("Getter for 0 was called"); 423 }, 424 425 get 1() { 426 assertUnreachable("Getter for 1 was called"); 427 } 428 }; 429 430 assertFalse(Array.prototype.includes.call(arrayLikeWithTraps, "a")); 431})(); 432 433 434// Array.prototype.includes should always return false on negative-length 435// objects 436(function() { 437 assertFalse(Array.prototype.includes.call({ 438 length: -1 439 }, 2)); 440 441 assertFalse(Array.prototype.includes.call({ 442 length: -2 443 })); 444 445 assertFalse(Array.prototype.includes.call({ 446 length: -Infinity 447 }, undefined)); 448 449 assertFalse(Array.prototype.includes.call({ 450 length: -Math.pow(2, 53) 451 }, NaN)); 452 453 assertFalse(Array.prototype.includes.call({ 454 length: -1, 455 "-1": 2 456 }, 2)); 457 458 assertFalse(Array.prototype.includes.call({ 459 length: -3, 460 "-1": 2 461 }, 2)); 462 463 assertFalse(Array.prototype.includes.call({ 464 length: -Infinity, 465 "-1": 2 466 }, 2)); 467 468 var arrayLikeWithTrap = { 469 length: -1, 470 471 get 0() { 472 assertUnreachable("Getter for 0 was called"); 473 } 474 }; 475 476 assertFalse(Array.prototype.includes.call(arrayLikeWithTrap, 2)); 477})(); 478 479 480// Array.prototype.includes should clamp positive lengths to 2^53 - 1 481(function() { 482 var fromIndexForLargeIndexTests = 9007199254740990; 483 484 assertFalse(Array.prototype.includes.call({ 485 length: 1 486 }, 2)); 487 488 assertTrue(Array.prototype.includes.call({ 489 length: 1, 490 0: "a" 491 }, "a")); 492 493 assertTrue(Array.prototype.includes.call({ 494 length: +Infinity, 495 0: "a" 496 }, "a")); 497 498 assertFalse(Array.prototype.includes.call({ 499 length: +Infinity 500 }, "a", fromIndexForLargeIndexTests)); 501 502 var arrayLikeWithTrap = { 503 length: +Infinity, 504 505 get 9007199254740992() { 506 assertUnreachable("Getter for 9007199254740992 (i.e. 2^53) was called"); 507 }, 508 509 "9007199254740993": "a" 510 }; 511 512 assertFalse( 513 Array.prototype.includes.call(arrayLikeWithTrap, "a", fromIndexForLargeIndexTests) 514 ); 515 516 var arrayLikeWithTooBigLength = { 517 length: 9007199254740996, 518 "9007199254740992": "a" 519 }; 520 521 assertFalse( 522 Array.prototype.includes.call(arrayLikeWithTooBigLength, "a", fromIndexForLargeIndexTests) 523 ); 524})(); 525 526 527// Array.prototype.includes should always return false on zero-length objects 528(function() { 529 assertFalse([].includes(2)); 530 assertFalse([].includes()); 531 assertFalse([].includes(undefined)); 532 assertFalse([].includes(NaN)); 533 534 assertFalse(Array.prototype.includes.call({ 535 length: 0 536 }, 2)); 537 538 assertFalse(Array.prototype.includes.call({ 539 length: 0 540 })); 541 542 assertFalse(Array.prototype.includes.call({ 543 length: 0 544 }, undefined)); 545 546 assertFalse(Array.prototype.includes.call({ 547 length: 0 548 }, NaN)); 549 550 assertFalse(Array.prototype.includes.call({ 551 length: 0, 552 0: 2 553 }, 2)); 554 555 assertFalse(Array.prototype.includes.call({ 556 length: 0, 557 0: undefined 558 })); 559 560 assertFalse(Array.prototype.includes.call({ 561 length: 0, 562 0: undefined 563 }, undefined)); 564 565 assertFalse(Array.prototype.includes.call({ 566 length: 0, 567 0: NaN 568 }, NaN)); 569 570 var arrayLikeWithTrap = { 571 length: 0, 572 573 get 0() { 574 assertUnreachable("Getter for 0 was called"); 575 } 576 }; 577 578 Array.prototype.includes.call(arrayLikeWithTrap); 579 580 var trappedFromIndex = { 581 valueOf: function() { 582 assertUnreachable("Should not try to convert fromIndex to a number on a zero-length array"); 583 } 584 }; 585 586 [].includes("a", trappedFromIndex); 587 588 Array.prototype.includes.call({ 589 length: 0 590 }, trappedFromIndex); 591})(); 592 593 594// Array.prototype.includes works on objects 595(function() { 596 assertFalse(["a", "b", "c"].includes({})); 597 assertFalse([{}, {}].includes({})); 598 var obj = {}; 599 assertTrue([obj].includes(obj)); 600 assertFalse([obj].includes(obj, 1)); 601 assertTrue([obj, obj].includes(obj, 1)); 602 603 var stringyObject = { 604 toString: function() { 605 return "a"; 606 } 607 }; 608 609 assertFalse(["a", "b", obj].includes(stringyObject)); 610})(); 611 612 613// Array.prototype.includes does not see an element removed by a getter that is 614// hit during iteration 615(function() { 616 var arrayLike = { 617 length: 5, 618 0: "a", 619 620 get 1() { 621 delete this[2]; 622 return "b"; 623 }, 624 625 2: "c" 626 }; 627 628 assertFalse(Array.prototype.includes.call(arrayLike, "c")); 629})(); 630 631 632// Array.prototype.includes should use the SameValueZero algorithm to compare 633(function() { 634 assertTrue([1, 2, 3].includes(2)); 635 assertFalse([1, 2, 3].includes(4)); 636 assertTrue([1, 2, NaN].includes(NaN)); 637 assertTrue([1, 2, -0].includes(+0)); 638 assertTrue([1, 2, -0].includes(-0)); 639 assertTrue([1, 2, +0].includes(-0)); 640 assertTrue([1, 2, +0].includes(+0)); 641 assertFalse([1, 2, -Infinity].includes(+Infinity)); 642 assertTrue([1, 2, -Infinity].includes(-Infinity)); 643 assertFalse([1, 2, +Infinity].includes(-Infinity)); 644 assertTrue([1, 2, +Infinity].includes(+Infinity)); 645})(); 646 647 648// Array.prototype.includes stops once it hits the length of an array-like, even 649// if there are more after 650(function() { 651 var arrayLike = { 652 length: 2, 653 0: "a", 654 1: "b", 655 656 get 2() { 657 assertUnreachable("Should not try to get the second element"); 658 } 659 }; 660 661 assertFalse(Array.prototype.includes.call(arrayLike, "c")); 662})(); 663 664 665// Array.prototype.includes works on typed arrays 666(function() { 667 assertTrue(Array.prototype.includes.call(new Uint8Array([1, 2, 3]), 2)); 668 669 assertTrue( 670 Array.prototype.includes.call(new Float32Array([2.5, 3.14, Math.PI]), 3.1415927410125732) 671 ); 672 673 assertFalse(Array.prototype.includes.call(new Uint8Array([1, 2, 3]), 4)); 674 assertFalse(Array.prototype.includes.call(new Uint8Array([1, 2, 3]), 2, 2)); 675})(); 676