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 var global = this; 6 var globalProto = Object.getPrototypeOf(global); 7 8 // Number of objects being tested. There is an assert ensuring this is correct. 9 var objectCount = 21; 10 11 12 function 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]. 86 function 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 } 116 TestArrayPrototypeUnscopables(); 117 118 119 120 function 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 object[Symbol.unscopables] = {x: true}; 134 with (object) { 135 assertEquals(1, x); 136 assertEquals(5, y); 137 assertEquals(3, z); 138 } 139 140 object[Symbol.unscopables] = {x: 0, y: true}; 141 with (object) { 142 assertEquals(1, x); 143 assertEquals(2, y); 144 assertEquals(3, z); 145 } 146 } 147 runTest(TestBasics); 148 149 150 function TestUnscopableChain(object) { 151 var x = 1; 152 object.x = 2; 153 154 with (object) { 155 assertEquals(2, x); 156 } 157 158 object[Symbol.unscopables] = { 159 __proto__: {x: true} 160 }; 161 with (object) { 162 assertEquals(1, x); 163 } 164 } 165 runTest(TestUnscopableChain); 166 167 168 function TestBasicsSet(object) { 169 var x = 1; 170 object.x = 2; 171 172 with (object) { 173 assertEquals(2, x); 174 } 175 176 object[Symbol.unscopables] = {x: true}; 177 with (object) { 178 assertEquals(1, x); 179 x = 3; 180 assertEquals(3, x); 181 } 182 183 assertEquals(3, x); 184 assertEquals(2, object.x); 185 } 186 runTest(TestBasicsSet); 187 188 189 function TestOnProto(object, proto) { 190 var x = 1; 191 var y = 2; 192 var z = 3; 193 proto.x = 4; 194 195 Object.setPrototypeOf(object, proto); 196 object.y = 5; 197 198 with (object) { 199 assertEquals(4, x); 200 assertEquals(5, y); 201 assertEquals(3, z); 202 } 203 204 proto[Symbol.unscopables] = {x: true}; 205 with (object) { 206 assertEquals(1, x); 207 assertEquals(5, y); 208 assertEquals(3, z); 209 } 210 211 object[Symbol.unscopables] = {y: true}; 212 with (object) { 213 assertEquals(4, x); 214 assertEquals(2, y); 215 assertEquals(3, z); 216 } 217 218 proto[Symbol.unscopables] = {y: true}; 219 object[Symbol.unscopables] = {x: true}; 220 with (object) { 221 assertEquals(1, x); 222 assertEquals(5, y); 223 assertEquals(3, z); 224 } 225 } 226 runTest(TestOnProto); 227 228 229 function TestSetBlockedOnProto(object, proto) { 230 var x = 1; 231 object.x = 2; 232 233 with (object) { 234 assertEquals(2, x); 235 } 236 237 Object.setPrototypeOf(object, proto); 238 proto[Symbol.unscopables] = {x: true}; 239 with (object) { 240 assertEquals(1, x); 241 x = 3; 242 assertEquals(3, x); 243 } 244 245 assertEquals(3, x); 246 assertEquals(2, object.x); 247 } 248 runTest(TestSetBlockedOnProto); 249 250 251 function TestNonObject(object) { 252 var x = 1; 253 var y = 2; 254 object.x = 3; 255 object.y = 4; 256 257 object[Symbol.unscopables] = 'xy'; 258 with (object) { 259 assertEquals(3, x); 260 assertEquals(4, y); 261 } 262 263 object[Symbol.unscopables] = null; 264 with (object) { 265 assertEquals(3, x); 266 assertEquals(4, y); 267 } 268 } 269 runTest(TestNonObject); 270 271 272 function TestChangeDuringWith(object) { 273 var x = 1; 274 var y = 2; 275 object.x = 3; 276 object.y = 4; 277 278 with (object) { 279 assertEquals(3, x); 280 assertEquals(4, y); 281 object[Symbol.unscopables] = {x: true}; 282 assertEquals(1, x); 283 assertEquals(4, y); 284 } 285 } 286 runTest(TestChangeDuringWith); 287 288 289 function TestChangeDuringWithWithPossibleOptimization(object) { 290 var x = 1; 291 object.x = 2; 292 with (object) { 293 for (var i = 0; i < 1000; i++) { 294 if (i === 500) object[Symbol.unscopables] = {x: true}; 295 assertEquals(i < 500 ? 2: 1, x); 296 } 297 } 298 } 299 TestChangeDuringWithWithPossibleOptimization({}); 300 301 302 function TestChangeDuringWithWithPossibleOptimization2(object) { 303 var x = 1; 304 object.x = 2; 305 object[Symbol.unscopables] = {x: true}; 306 with (object) { 307 for (var i = 0; i < 1000; i++) { 308 if (i === 500) delete object[Symbol.unscopables]; 309 assertEquals(i < 500 ? 1 : 2, x); 310 } 311 } 312 } 313 TestChangeDuringWithWithPossibleOptimization2({}); 314 315 316 function TestChangeDuringWithWithPossibleOptimization3(object) { 317 var x = 1; 318 object.x = 2; 319 object[Symbol.unscopables] = {}; 320 with (object) { 321 for (var i = 0; i < 1000; i++) { 322 if (i === 500) object[Symbol.unscopables].x = true; 323 assertEquals(i < 500 ? 2 : 1, x); 324 } 325 } 326 } 327 TestChangeDuringWithWithPossibleOptimization3({}); 328 329 330 function TestChangeDuringWithWithPossibleOptimization4(object) { 331 var x = 1; 332 object.x = 2; 333 object[Symbol.unscopables] = {x: true}; 334 with (object) { 335 for (var i = 0; i < 1000; i++) { 336 if (i === 500) delete object[Symbol.unscopables].x; 337 assertEquals(i < 500 ? 1 : 2, x); 338 } 339 } 340 } 341 TestChangeDuringWithWithPossibleOptimization4({}); 342 343 344 function TestAccessorReceiver(object, proto) { 345 var x = 'local'; 346 347 Object.defineProperty(proto, 'x', { 348 get: function() { 349 assertEquals(object, this); 350 return this.x_; 351 }, 352 configurable: true 353 }); 354 proto.x_ = 'proto'; 355 356 Object.setPrototypeOf(object, proto); 357 proto.x_ = 'object'; 358 359 with (object) { 360 assertEquals('object', x); 361 } 362 } 363 runTest(TestAccessorReceiver); 364 365 366 function TestUnscopablesGetter(object) { 367 // This test gets really messy when object is the global since the assert 368 // functions are properties on the global object and the call count gets 369 // completely different. 370 if (object === global) return; 371 372 var x = 'local'; 373 object.x = 'object'; 374 375 var callCount = 0; 376 Object.defineProperty(object, Symbol.unscopables, { 377 get: function() { 378 callCount++; 379 return {}; 380 }, 381 configurable: true 382 }); 383 with (object) { 384 assertEquals('object', x); 385 } 386 // Once for HasBinding 387 assertEquals(1, callCount); 388 389 callCount = 0; 390 Object.defineProperty(object, Symbol.unscopables, { 391 get: function() { 392 callCount++; 393 return {x: true}; 394 }, 395 configurable: true 396 }); 397 with (object) { 398 assertEquals('local', x); 399 } 400 // Once for HasBinding 401 assertEquals(1, callCount); 402 403 callCount = 0; 404 Object.defineProperty(object, Symbol.unscopables, { 405 get: function() { 406 callCount++; 407 return callCount == 1 ? {} : {x: true}; 408 }, 409 configurable: true 410 }); 411 with (object) { 412 x = 1; 413 } 414 // Once for HasBinding 415 assertEquals(1, callCount); 416 assertEquals(1, object.x); 417 assertEquals('local', x); 418 with (object) { 419 x = 2; 420 } 421 // One more HasBinding. 422 assertEquals(2, callCount); 423 assertEquals(1, object.x); 424 assertEquals(2, x); 425 } 426 runTest(TestUnscopablesGetter); 427 428 429 var global = this; 430 function TestUnscopablesGetter2() { 431 var x = 'local'; 432 433 var globalProto = Object.getPrototypeOf(global); 434 var protos = [{}, [], function() {}, global]; 435 var objects = [{}, [], function() {}]; 436 437 protos.forEach(function(proto) { 438 objects.forEach(function(object) { 439 Object.defineProperty(proto, 'x', { 440 get: function() { 441 assertEquals(object, this); 442 return 'proto'; 443 }, 444 configurable: true 445 }); 446 447 object.__proto__ = proto; 448 Object.defineProperty(object, 'x', { 449 get: function() { 450 assertEquals(object, this); 451 return 'object'; 452 }, 453 configurable: true 454 }); 455 456 with (object) { 457 assertEquals('object', x); 458 } 459 460 object[Symbol.unscopables] = {x: true}; 461 with (object) { 462 assertEquals('local', x); 463 } 464 465 delete proto[Symbol.unscopables]; 466 delete object[Symbol.unscopables]; 467 }); 468 }); 469 470 delete global.x; 471 Object.setPrototypeOf(global, globalProto); 472 } 473 TestUnscopablesGetter2(); 474 475 476 function TestSetterOnBlacklisted(object, proto) { 477 var x = 'local'; 478 Object.defineProperty(proto, 'x', { 479 set: function(x) { 480 assertUnreachable(); 481 }, 482 get: function() { 483 return 'proto'; 484 }, 485 configurable: true 486 }); 487 Object.setPrototypeOf(object, proto); 488 Object.defineProperty(object, 'x', { 489 get: function() { 490 return this.x_; 491 }, 492 set: function(x) { 493 this.x_ = x; 494 }, 495 configurable: true 496 }); 497 object.x_ = 1; 498 499 with (object) { 500 x = 2; 501 assertEquals(2, x); 502 } 503 504 assertEquals(2, object.x); 505 506 object[Symbol.unscopables] = {x: true}; 507 508 with (object) { 509 x = 3; 510 assertEquals(3, x); 511 } 512 513 assertEquals(2, object.x); 514 } 515 runTest(TestSetterOnBlacklisted); 516 517 518 function TestObjectsAsUnscopables(object, unscopables) { 519 var x = 1; 520 object.x = 2; 521 522 with (object) { 523 assertEquals(2, x); 524 object[Symbol.unscopables] = unscopables; 525 assertEquals(2, x); 526 } 527 } 528 runTest(TestObjectsAsUnscopables); 529 530 531 function TestAccessorOnUnscopables(object) { 532 var x = 1; 533 object.x = 2; 534 535 var unscopables = { 536 get x() { 537 assertUnreachable(); 538 } 539 }; 540 541 with (object) { 542 assertEquals(2, x); 543 object[Symbol.unscopables] = unscopables; 544 assertEquals(1, x); 545 } 546 } 547 runTest(TestAccessorOnUnscopables); 548 549 550 function TestLengthUnscopables(object, proto) { 551 var length = 2; 552 with (object) { 553 assertEquals(1, length); 554 object[Symbol.unscopables] = {length: true}; 555 assertEquals(2, length); 556 delete object[Symbol.unscopables]; 557 assertEquals(1, length); 558 } 559 } 560 TestLengthUnscopables([1], Array.prototype); 561 TestLengthUnscopables(function(x) {}, Function.prototype); 562 TestLengthUnscopables(new String('x'), String.prototype); 563 564 565 function TestFunctionNameUnscopables(object) { 566 var name = 'local'; 567 with (object) { 568 assertEquals('f', name); 569 object[Symbol.unscopables] = {name: true}; 570 assertEquals('local', name); 571 delete object[Symbol.unscopables]; 572 assertEquals('f', name); 573 } 574 } 575 TestFunctionNameUnscopables(function f() {}); 576 577 578 function TestFunctionPrototypeUnscopables() { 579 var prototype = 'local'; 580 var f = function() {}; 581 var g = function() {}; 582 Object.setPrototypeOf(f, g); 583 var fp = f.prototype; 584 var gp = g.prototype; 585 with (f) { 586 assertEquals(fp, prototype); 587 f[Symbol.unscopables] = {prototype: true}; 588 assertEquals('local', prototype); 589 delete f[Symbol.unscopables]; 590 assertEquals(fp, prototype); 591 } 592 } 593 TestFunctionPrototypeUnscopables(function() {}); 594 595 596 function TestFunctionArgumentsUnscopables() { 597 var func = function() { 598 var arguments = 'local'; 599 var args = func.arguments; 600 with (func) { 601 assertEquals(args, arguments); 602 func[Symbol.unscopables] = {arguments: true}; 603 assertEquals('local', arguments); 604 delete func[Symbol.unscopables]; 605 assertEquals(args, arguments); 606 } 607 } 608 func(1); 609 } 610 TestFunctionArgumentsUnscopables(); 611 612 613 function TestArgumentsLengthUnscopables() { 614 var func = function() { 615 var length = 'local'; 616 with (arguments) { 617 assertEquals(1, length); 618 arguments[Symbol.unscopables] = {length: true}; 619 assertEquals('local', length); 620 } 621 } 622 func(1); 623 } 624 TestArgumentsLengthUnscopables(); 625 626 627 function TestFunctionCallerUnscopables() { 628 var func = function() { 629 var caller = 'local'; 630 with (func) { 631 assertEquals(TestFunctionCallerUnscopables, caller); 632 func[Symbol.unscopables] = {caller: true}; 633 assertEquals('local', caller); 634 delete func[Symbol.unscopables]; 635 assertEquals(TestFunctionCallerUnscopables, caller); 636 } 637 } 638 func(1); 639 } 640 TestFunctionCallerUnscopables(); 641 642 643 function TestGetUnscopablesGetterThrows() { 644 var object = { 645 get x() { 646 assertUnreachable(); 647 } 648 }; 649 function CustomError() {} 650 Object.defineProperty(object, Symbol.unscopables, { 651 get: function() { 652 throw new CustomError(); 653 } 654 }); 655 assertThrows(function() { 656 with (object) { 657 x; 658 } 659 }, CustomError); 660 } 661 TestGetUnscopablesGetterThrows(); 662