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 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} 147runTest(TestBasics); 148 149 150function 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} 165runTest(TestUnscopableChain); 166 167 168function 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} 186runTest(TestBasicsSet); 187 188 189function 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} 226runTest(TestOnProto); 227 228 229function 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} 248runTest(TestSetBlockedOnProto); 249 250 251function 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} 269runTest(TestNonObject); 270 271 272function 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} 286runTest(TestChangeDuringWith); 287 288 289function 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} 299TestChangeDuringWithWithPossibleOptimization({}); 300 301 302function 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} 313TestChangeDuringWithWithPossibleOptimization2({}); 314 315 316function 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} 327TestChangeDuringWithWithPossibleOptimization3({}); 328 329 330function 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} 341TestChangeDuringWithWithPossibleOptimization4({}); 342 343 344function 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} 363runTest(TestAccessorReceiver); 364 365 366function 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} 426runTest(TestUnscopablesGetter); 427 428 429var global = this; 430function 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} 473TestUnscopablesGetter2(); 474 475 476function 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} 515runTest(TestSetterOnBlacklisted); 516 517 518function 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} 528runTest(TestObjectsAsUnscopables); 529 530 531function 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} 547runTest(TestAccessorOnUnscopables); 548 549 550function 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} 560TestLengthUnscopables([1], Array.prototype); 561TestLengthUnscopables(function(x) {}, Function.prototype); 562TestLengthUnscopables(new String('x'), String.prototype); 563 564 565function 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} 575TestFunctionNameUnscopables(function f() {}); 576 577 578function 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} 593TestFunctionPrototypeUnscopables(function() {}); 594 595 596function 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} 610TestFunctionArgumentsUnscopables(); 611 612 613function 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} 624TestArgumentsLengthUnscopables(); 625 626 627function 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} 640TestFunctionCallerUnscopables(); 641 642 643function 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} 661TestGetUnscopablesGetterThrows(); 662