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 5var global = this; 6var globalProto = Object.getPrototypeOf(global); 7 8// Number of objects being tested. There is an assert ensuring this is correct. 9var objectCount = 21; 10 11 12function runTest(f) { 13 function restore(object, oldProto) { 14 delete object[Symbol.unscopables]; 15 delete object.x; 16 delete object.x_; 17 delete object.y; 18 delete object.z; 19 Object.setPrototypeOf(object, oldProto); 20 } 21 22 function getObject(i) { 23 var objects = [ 24 {}, 25 [], 26 function() {}, 27 function() { 28 return arguments; 29 }(), 30 function() { 31 'use strict'; 32 return arguments; 33 }(), 34 Object(1), 35 Object(true), 36 Object('bla'), 37 new Date, 38 new RegExp, 39 new Set, 40 new Map, 41 new WeakMap, 42 new WeakSet, 43 new ArrayBuffer(10), 44 new Int32Array(5), 45 Object, 46 Function, 47 Date, 48 RegExp, 49 global 50 ]; 51 52 assertEquals(objectCount, objects.length); 53 return objects[i]; 54 } 55 56 // Tests depends on this not being there to start with. 57 delete Array.prototype[Symbol.unscopables]; 58 59 if (f.length === 1) { 60 for (var i = 0; i < objectCount; i++) { 61 var object = getObject(i); 62 var oldObjectProto = Object.getPrototypeOf(object); 63 f(object); 64 restore(object, oldObjectProto); 65 } 66 } else { 67 for (var i = 0; i < objectCount; i++) { 68 for (var j = 0; j < objectCount; j++) { 69 var object = getObject(i); 70 var proto = getObject(j); 71 if (object === proto) { 72 continue; 73 } 74 var oldObjectProto = Object.getPrototypeOf(object); 75 var oldProtoProto = Object.getPrototypeOf(proto); 76 f(object, proto); 77 restore(object, oldObjectProto); 78 restore(proto, oldProtoProto); 79 } 80 } 81 } 82} 83 84// Test array first, since other tests are changing 85// Array.prototype[Symbol.unscopables]. 86function TestArrayPrototypeUnscopables() { 87 var descr = Object.getOwnPropertyDescriptor(Array.prototype, 88 Symbol.unscopables); 89 assertFalse(descr.enumerable); 90 assertFalse(descr.writable); 91 assertTrue(descr.configurable); 92 assertEquals(null, Object.getPrototypeOf(descr.value)); 93 94 var copyWithin = 'local copyWithin'; 95 var entries = 'local entries'; 96 var fill = 'local fill'; 97 var find = 'local find'; 98 var findIndex = 'local findIndex'; 99 var keys = 'local keys'; 100 var values = 'local values'; 101 102 var array = []; 103 array.toString = 42; 104 105 with (array) { 106 assertEquals('local copyWithin', copyWithin); 107 assertEquals('local entries', entries); 108 assertEquals('local fill', fill); 109 assertEquals('local find', find); 110 assertEquals('local findIndex', findIndex); 111 assertEquals('local keys', keys); 112 assertEquals('local values', values); 113 assertEquals(42, toString); 114 } 115} 116TestArrayPrototypeUnscopables(); 117 118 119 120function TestBasics(object) { 121 var x = 1; 122 var y = 2; 123 var z = 3; 124 object.x = 4; 125 object.y = 5; 126 127 with (object) { 128 assertEquals(4, x); 129 assertEquals(5, y); 130 assertEquals(3, z); 131 } 132 133 var truthyValues = [true, 1, 'x', {}, Symbol()]; 134 for (var truthyValue of truthyValues) { 135 object[Symbol.unscopables] = {x: truthyValue}; 136 with (object) { 137 assertEquals(1, x); 138 assertEquals(5, y); 139 assertEquals(3, z); 140 } 141 } 142 143 var falsyValues = [false, 0, -0, NaN, '', null, undefined]; 144 for (var falsyValue of falsyValues) { 145 object[Symbol.unscopables] = {x: falsyValue, y: true}; 146 with (object) { 147 assertEquals(4, x); 148 assertEquals(2, y); 149 assertEquals(3, z); 150 } 151 } 152 153 for (var xFalsy of falsyValues) { 154 for (var yFalsy of falsyValues) { 155 object[Symbol.unscopables] = {x: xFalsy, y: yFalsy}; 156 with (object) { 157 assertEquals(4, x); 158 assertEquals(5, y); 159 assertEquals(3, z); 160 } 161 } 162 } 163} 164runTest(TestBasics); 165 166 167function TestUnscopableChain(object) { 168 var x = 1; 169 object.x = 2; 170 171 with (object) { 172 assertEquals(2, x); 173 } 174 175 object[Symbol.unscopables] = { 176 __proto__: {x: true} 177 }; 178 with (object) { 179 assertEquals(1, x); 180 } 181 182 object[Symbol.unscopables] = { 183 __proto__: {x: undefined} 184 }; 185 with (object) { 186 assertEquals(2, x); 187 } 188} 189runTest(TestUnscopableChain); 190 191 192function TestBasicsSet(object) { 193 var x = 1; 194 object.x = 2; 195 196 with (object) { 197 assertEquals(2, x); 198 } 199 200 object[Symbol.unscopables] = {x: true}; 201 with (object) { 202 assertEquals(1, x); 203 x = 3; 204 assertEquals(3, x); 205 } 206 207 assertEquals(3, x); 208 assertEquals(2, object.x); 209} 210runTest(TestBasicsSet); 211 212 213function TestOnProto(object, proto) { 214 var x = 1; 215 var y = 2; 216 var z = 3; 217 proto.x = 4; 218 219 Object.setPrototypeOf(object, proto); 220 object.y = 5; 221 222 with (object) { 223 assertEquals(4, x); 224 assertEquals(5, y); 225 assertEquals(3, z); 226 } 227 228 proto[Symbol.unscopables] = {x: true}; 229 with (object) { 230 assertEquals(1, x); 231 assertEquals(5, y); 232 assertEquals(3, z); 233 } 234 235 object[Symbol.unscopables] = {y: true}; 236 with (object) { 237 assertEquals(4, x); 238 assertEquals(2, y); 239 assertEquals(3, z); 240 } 241 242 proto[Symbol.unscopables] = {y: true}; 243 object[Symbol.unscopables] = {x: true}; 244 with (object) { 245 assertEquals(1, x); 246 assertEquals(5, y); 247 assertEquals(3, z); 248 } 249 250 proto[Symbol.unscopables] = {y: true}; 251 object[Symbol.unscopables] = {x: true, y: undefined}; 252 with (object) { 253 assertEquals(1, x); 254 assertEquals(5, y); 255 assertEquals(3, z); 256 } 257} 258runTest(TestOnProto); 259 260 261function TestSetBlockedOnProto(object, proto) { 262 var x = 1; 263 object.x = 2; 264 265 with (object) { 266 assertEquals(2, x); 267 } 268 269 Object.setPrototypeOf(object, proto); 270 proto[Symbol.unscopables] = {x: true}; 271 with (object) { 272 assertEquals(1, x); 273 x = 3; 274 assertEquals(3, x); 275 } 276 277 assertEquals(3, x); 278 assertEquals(2, object.x); 279} 280runTest(TestSetBlockedOnProto); 281 282 283function TestNonObject(object) { 284 var x = 1; 285 var y = 2; 286 object.x = 3; 287 object.y = 4; 288 289 object[Symbol.unscopables] = 'xy'; 290 with (object) { 291 assertEquals(3, x); 292 assertEquals(4, y); 293 } 294 295 object[Symbol.unscopables] = null; 296 with (object) { 297 assertEquals(3, x); 298 assertEquals(4, y); 299 } 300} 301runTest(TestNonObject); 302 303 304function TestChangeDuringWith(object) { 305 var x = 1; 306 var y = 2; 307 object.x = 3; 308 object.y = 4; 309 310 with (object) { 311 assertEquals(3, x); 312 assertEquals(4, y); 313 object[Symbol.unscopables] = {x: true}; 314 assertEquals(1, x); 315 assertEquals(4, y); 316 } 317} 318runTest(TestChangeDuringWith); 319 320 321function TestChangeDuringWithWithPossibleOptimization(object) { 322 var x = 1; 323 object.x = 2; 324 with (object) { 325 for (var i = 0; i < 1000; i++) { 326 if (i === 500) object[Symbol.unscopables] = {x: true}; 327 assertEquals(i < 500 ? 2: 1, x); 328 } 329 } 330} 331TestChangeDuringWithWithPossibleOptimization({}); 332 333 334function TestChangeDuringWithWithPossibleOptimization2(object) { 335 var x = 1; 336 object.x = 2; 337 object[Symbol.unscopables] = {x: true}; 338 with (object) { 339 for (var i = 0; i < 1000; i++) { 340 if (i === 500) delete object[Symbol.unscopables]; 341 assertEquals(i < 500 ? 1 : 2, x); 342 } 343 } 344} 345TestChangeDuringWithWithPossibleOptimization2({}); 346 347 348function TestChangeDuringWithWithPossibleOptimization3(object) { 349 var x = 1; 350 object.x = 2; 351 object[Symbol.unscopables] = {}; 352 with (object) { 353 for (var i = 0; i < 1000; i++) { 354 if (i === 500) object[Symbol.unscopables].x = true; 355 assertEquals(i < 500 ? 2 : 1, x); 356 } 357 } 358} 359TestChangeDuringWithWithPossibleOptimization3({}); 360 361 362function TestChangeDuringWithWithPossibleOptimization4(object) { 363 var x = 1; 364 object.x = 2; 365 object[Symbol.unscopables] = {x: true}; 366 with (object) { 367 for (var i = 0; i < 1000; i++) { 368 if (i === 500) delete object[Symbol.unscopables].x; 369 assertEquals(i < 500 ? 1 : 2, x); 370 } 371 } 372} 373TestChangeDuringWithWithPossibleOptimization4({}); 374 375 376function TestChangeDuringWithWithPossibleOptimization4(object) { 377 var x = 1; 378 object.x = 2; 379 object[Symbol.unscopables] = {x: true}; 380 with (object) { 381 for (var i = 0; i < 1000; i++) { 382 if (i === 500) object[Symbol.unscopables].x = undefined; 383 assertEquals(i < 500 ? 1 : 2, x); 384 } 385 } 386} 387TestChangeDuringWithWithPossibleOptimization4({}); 388 389 390function TestAccessorReceiver(object, proto) { 391 var x = 'local'; 392 393 Object.defineProperty(proto, 'x', { 394 get: function() { 395 assertEquals(object, this); 396 return this.x_; 397 }, 398 configurable: true 399 }); 400 proto.x_ = 'proto'; 401 402 Object.setPrototypeOf(object, proto); 403 proto.x_ = 'object'; 404 405 with (object) { 406 assertEquals('object', x); 407 } 408} 409runTest(TestAccessorReceiver); 410 411 412function TestUnscopablesGetter(object) { 413 // This test gets really messy when object is the global since the assert 414 // functions are properties on the global object and the call count gets 415 // completely different. 416 if (object === global) return; 417 418 var x = 'local'; 419 object.x = 'object'; 420 421 var callCount = 0; 422 Object.defineProperty(object, Symbol.unscopables, { 423 get: function() { 424 callCount++; 425 return {}; 426 }, 427 configurable: true 428 }); 429 with (object) { 430 assertEquals('object', x); 431 } 432 // Once for HasBinding 433 assertEquals(1, callCount); 434 435 callCount = 0; 436 Object.defineProperty(object, Symbol.unscopables, { 437 get: function() { 438 callCount++; 439 return {x: true}; 440 }, 441 configurable: true 442 }); 443 with (object) { 444 assertEquals('local', x); 445 } 446 // Once for HasBinding 447 assertEquals(1, callCount); 448 449 callCount = 0; 450 Object.defineProperty(object, Symbol.unscopables, { 451 get: function() { 452 callCount++; 453 return callCount == 1 ? {} : {x: true}; 454 }, 455 configurable: true 456 }); 457 with (object) { 458 x = 1; 459 } 460 // Once for HasBinding 461 assertEquals(1, callCount); 462 assertEquals(1, object.x); 463 assertEquals('local', x); 464 with (object) { 465 x = 2; 466 } 467 // One more HasBinding. 468 assertEquals(2, callCount); 469 assertEquals(1, object.x); 470 assertEquals(2, x); 471} 472runTest(TestUnscopablesGetter); 473 474 475var global = this; 476function TestUnscopablesGetter2() { 477 var x = 'local'; 478 479 var globalProto = Object.getPrototypeOf(global); 480 var protos = [{}, [], function() {}, global]; 481 var objects = [{}, [], function() {}]; 482 483 protos.forEach(function(proto) { 484 objects.forEach(function(object) { 485 Object.defineProperty(proto, 'x', { 486 get: function() { 487 assertEquals(object, this); 488 return 'proto'; 489 }, 490 configurable: true 491 }); 492 493 object.__proto__ = proto; 494 Object.defineProperty(object, 'x', { 495 get: function() { 496 assertEquals(object, this); 497 return 'object'; 498 }, 499 configurable: true 500 }); 501 502 with (object) { 503 assertEquals('object', x); 504 } 505 506 object[Symbol.unscopables] = {x: true}; 507 with (object) { 508 assertEquals('local', x); 509 } 510 511 delete proto[Symbol.unscopables]; 512 delete object[Symbol.unscopables]; 513 }); 514 }); 515 516 delete global.x; 517 Object.setPrototypeOf(global, globalProto); 518} 519TestUnscopablesGetter2(); 520 521 522function TestSetterOnBlacklisted(object, proto) { 523 var x = 'local'; 524 Object.defineProperty(proto, 'x', { 525 set: function(x) { 526 assertUnreachable(); 527 }, 528 get: function() { 529 return 'proto'; 530 }, 531 configurable: true 532 }); 533 Object.setPrototypeOf(object, proto); 534 Object.defineProperty(object, 'x', { 535 get: function() { 536 return this.x_; 537 }, 538 set: function(x) { 539 this.x_ = x; 540 }, 541 configurable: true 542 }); 543 object.x_ = 1; 544 545 with (object) { 546 x = 2; 547 assertEquals(2, x); 548 } 549 550 assertEquals(2, object.x); 551 552 object[Symbol.unscopables] = {x: true}; 553 554 with (object) { 555 x = 3; 556 assertEquals(3, x); 557 } 558 559 assertEquals(2, object.x); 560} 561runTest(TestSetterOnBlacklisted); 562 563 564function TestObjectsAsUnscopables(object, unscopables) { 565 var x = 1; 566 object.x = 2; 567 568 with (object) { 569 assertEquals(2, x); 570 object[Symbol.unscopables] = unscopables; 571 assertEquals(2, x); 572 } 573} 574runTest(TestObjectsAsUnscopables); 575 576 577function TestAccessorOnUnscopables(object) { 578 var x = 1; 579 object.x = 2; 580 581 var calls = 0; 582 var unscopables = { 583 get x() { 584 calls++; 585 return calls === 1 ? true : undefined; 586 } 587 }; 588 589 with (object) { 590 assertEquals(2, x); 591 object[Symbol.unscopables] = unscopables; 592 assertEquals(1, x); 593 assertEquals(2, x); 594 } 595 assertEquals(2, calls); 596} 597runTest(TestAccessorOnUnscopables); 598 599 600function TestLengthUnscopables(object, proto) { 601 var length = 2; 602 with (object) { 603 assertEquals(1, length); 604 object[Symbol.unscopables] = {length: true}; 605 assertEquals(2, length); 606 delete object[Symbol.unscopables]; 607 assertEquals(1, length); 608 } 609} 610TestLengthUnscopables([1], Array.prototype); 611TestLengthUnscopables(function(x) {}, Function.prototype); 612TestLengthUnscopables(new String('x'), String.prototype); 613 614 615function TestFunctionNameUnscopables(object) { 616 var name = 'local'; 617 with (object) { 618 assertEquals('f', name); 619 object[Symbol.unscopables] = {name: true}; 620 assertEquals('local', name); 621 delete object[Symbol.unscopables]; 622 assertEquals('f', name); 623 } 624} 625TestFunctionNameUnscopables(function f() {}); 626 627 628function TestFunctionPrototypeUnscopables() { 629 var prototype = 'local'; 630 var f = function() {}; 631 var g = function() {}; 632 Object.setPrototypeOf(f, g); 633 var fp = f.prototype; 634 var gp = g.prototype; 635 with (f) { 636 assertEquals(fp, prototype); 637 f[Symbol.unscopables] = {prototype: true}; 638 assertEquals('local', prototype); 639 delete f[Symbol.unscopables]; 640 assertEquals(fp, prototype); 641 } 642} 643TestFunctionPrototypeUnscopables(function() {}); 644 645 646function TestFunctionArgumentsUnscopables() { 647 var func = function() { 648 var arguments = 'local'; 649 var args = func.arguments; 650 with (func) { 651 assertEquals(args, arguments); 652 func[Symbol.unscopables] = {arguments: true}; 653 assertEquals('local', arguments); 654 delete func[Symbol.unscopables]; 655 assertEquals(args, arguments); 656 } 657 } 658 func(1); 659} 660TestFunctionArgumentsUnscopables(); 661 662 663function TestArgumentsLengthUnscopables() { 664 var func = function() { 665 var length = 'local'; 666 with (arguments) { 667 assertEquals(1, length); 668 arguments[Symbol.unscopables] = {length: true}; 669 assertEquals('local', length); 670 } 671 } 672 func(1); 673} 674TestArgumentsLengthUnscopables(); 675 676 677function TestFunctionCallerUnscopables() { 678 var func = function() { 679 var caller = 'local'; 680 with (func) { 681 assertEquals(TestFunctionCallerUnscopables, caller); 682 func[Symbol.unscopables] = {caller: true}; 683 assertEquals('local', caller); 684 delete func[Symbol.unscopables]; 685 assertEquals(TestFunctionCallerUnscopables, caller); 686 } 687 } 688 func(1); 689} 690TestFunctionCallerUnscopables(); 691 692 693function TestGetUnscopablesGetterThrows() { 694 var object = { 695 get x() { 696 assertUnreachable(); 697 } 698 }; 699 function CustomError() {} 700 Object.defineProperty(object, Symbol.unscopables, { 701 get: function() { 702 throw new CustomError(); 703 } 704 }); 705 assertThrows(function() { 706 with (object) { 707 x; 708 } 709 }, CustomError); 710} 711TestGetUnscopablesGetterThrows(); 712 713 714function TestGetUnscopablesGetterThrows2() { 715 var object = { 716 get x() { 717 assertUnreachable(); 718 } 719 }; 720 function CustomError() {} 721 722 object[Symbol.unscopables] = { 723 get x() { 724 throw new CustomError(); 725 } 726 }; 727 assertThrows(function() { 728 with (object) { 729 x; 730 } 731 }, CustomError); 732} 733TestGetUnscopablesGetterThrows(); 734