unscopablesWithScopeTest.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
  6. var tests = [
  7. {
  8. name: "Check if Symbol.unscopables is defined",
  9. body: function ()
  10. {
  11. assert.isTrue(Array.prototype.hasOwnProperty(Symbol.unscopables), "Array should have Array.prototype[@@unscopables] property");
  12. }
  13. },
  14. {
  15. name: "Global scope test on Arrays",
  16. body: function ()
  17. {
  18. var globalScope = -1;
  19. var find = globalScope;
  20. var findIndex = globalScope;
  21. var entries = globalScope;
  22. var keys = globalScope;
  23. var values = globalScope;
  24. with([])
  25. {
  26. assert.areEqual(globalScope, find, "find property is not brought into scope by the with statement");
  27. assert.areEqual(globalScope, findIndex, "findIndex property is not brought into scope by the with statement");
  28. assert.areEqual(globalScope, entries, "entries property is not brought into scope by the with statement");
  29. assert.areEqual(globalScope, keys, "keys property is not brought into scope by the with statement");
  30. assert.areEqual(globalScope, values, "values property is not brought into scope by the with statement");
  31. }
  32. }
  33. },
  34. {
  35. name: "Add to Array.prototype[@@unscopables] blacklist",
  36. body: function ()
  37. {
  38. var globalScope = -1;
  39. var find = globalScope;
  40. var findIndex = globalScope;
  41. var entries = globalScope;
  42. var keys = globalScope;
  43. var values = globalScope;
  44. var slice = globalScope;
  45. var a = [];
  46. a[Symbol.unscopables]["slice"] = true;
  47. with(a)
  48. {
  49. assert.areEqual(globalScope, find, "find property is not brought into scope by the with statement");
  50. assert.areEqual(globalScope, findIndex,"findIndex property is not brought into scope by the with statement");
  51. assert.areEqual(globalScope, entries, "entries property is not brought into scope by the with statement");
  52. assert.areEqual(globalScope, keys, "keys property is not brought into scope by the with statement");
  53. assert.areEqual(globalScope, values, "values property is not brought into scope by the with statement");
  54. assert.areEqual(globalScope, slice, "slice property is not brought into scope by the with statement");
  55. }
  56. }
  57. },
  58. {
  59. name: "Overwrite @@unscopables blacklist",
  60. body: function ()
  61. {
  62. var globalScope = -1;
  63. var c =
  64. {
  65. find : function () {},
  66. slice: function () {},
  67. [Symbol.unscopables]: {find : true }
  68. };
  69. var find = globalScope;
  70. var slice = globalScope;
  71. with(c)
  72. {
  73. assert.isTrue(globalScope != slice, "slice should be on Array scope");
  74. assert.areEqual(globalScope, find, "find should not be on Array scope");
  75. }
  76. var props = {"slice" : true};
  77. c[Symbol.unscopables] = props;
  78. with(c)
  79. {
  80. assert.isTrue(globalScope != find, "find should be on Array scope");
  81. assert.areEqual(globalScope, slice, "slice should not be on Array scope");
  82. }
  83. }
  84. },
  85. {
  86. name: "Adding to @@unscopables blacklist in a with statement",
  87. body: function ()
  88. {
  89. var globalScope = -1;
  90. var find = globalScope;
  91. var slice = globalScope;
  92. var c =
  93. {
  94. find : function () {},
  95. slice: function () {},
  96. [Symbol.unscopables]: {"find" : true}
  97. };
  98. with(c)
  99. {
  100. assert.areEqual(globalScope, find, "find property is not brought into scope by the with statement");
  101. c[Symbol.unscopables]["slice"] = true;
  102. assert.areEqual(globalScope, slice, "slice property is not brought into scope by the with statement");
  103. }
  104. }
  105. },
  106. {
  107. name: "Make sure we did not break with Scope",
  108. body: function ()
  109. {
  110. var b =1;
  111. var c =
  112. {
  113. get : function ()
  114. {
  115. return 4;
  116. },
  117. valueOf: function ()
  118. {
  119. WScript.Echo("valueOf");
  120. return {}; // not a primitive
  121. },
  122. toString: function ()
  123. {
  124. WScript.Echo("toString");
  125. return {}; // not a primitive
  126. }
  127. }
  128. with ({a: 1 , e: { l : 1, w:2}})
  129. {
  130. function f()
  131. {
  132. a = 2; //Set test
  133. b = a; //Get test
  134. }
  135. f();
  136. assert.areEqual(3,Object.keys(c).length,"There are three properties on c");
  137. assert.areEqual(2,Object.keys(e).length,"There are two properties on e");
  138. delete e;
  139. assert.throws(function(){e.l},ReferenceError,"e should no longer exist");
  140. assert.areEqual(2,a,"a should be 2");
  141. assert.areEqual(2,b,"b should be 2");
  142. }
  143. with(c)
  144. {
  145. assert.areEqual(4,get(),"get is "+get()+" c.get() should be 4");
  146. }
  147. }
  148. },
  149. {
  150. name: "Make sure we do not expose the with Object",
  151. body: function ()
  152. {
  153. var o = {f: function(){ return this}, x : 2, [Symbol.unscopables]: {"x" : true}};
  154. var x = -1;
  155. var testValue = o.f();
  156. with (o)
  157. {
  158. eval("var b = f();");
  159. assert.areEqual(testValue,b,"This should be handled by the ScopedLdInst unwrapping, which should mean testValue and b are equivalent");
  160. var a = f();
  161. // if this is broken We will get an Assert in the WithScopeObject on chk builds before the areEqual call but I'll leave this for fre builds
  162. assert.areEqual(testValue,a,"This test checks testValue and a are equivalent");
  163. assert.areEqual(-1,x,"x is not brought into scope by the with statement");
  164. assert.areEqual(o.x,b.x,"x is not brought into scope by the with statement");
  165. assert.areEqual(o.x,a.x,"x is not brought into scope by the with statement");
  166. }
  167. }
  168. },
  169. {
  170. name: "Lambda expressions",
  171. body: function ()
  172. {
  173. var adder = function (x)
  174. {
  175. return function (y)
  176. {
  177. return x + y;
  178. };
  179. };
  180. var find = -1;
  181. var findArray = [].find;
  182. with([])
  183. {
  184. find = adder(5);
  185. assert.areEqual(6,find(1),"This should equal 6");
  186. }
  187. assert.isTrue(-1 != find,"This should now be equal to a lambda");
  188. find = findArray;
  189. }
  190. },
  191. {
  192. name: "Operator precedence test",
  193. body: function ()
  194. {
  195. var obj = {a : 1 };
  196. var a = false;
  197. with(obj)
  198. {
  199. obj[Symbol.unscopables] = {}
  200. a = obj[Symbol.unscopables]["a"] = true;
  201. }
  202. assert.areEqual(1, obj.a, " should still be 1");
  203. assert.areEqual(true, a,"a.root should be set to true RHS evaluated before assignment");
  204. }
  205. },
  206. {
  207. name: "Nested functions in with",
  208. body: function ()
  209. {
  210. var globalScope = -1;
  211. var find = globalScope;
  212. var findIndex = globalScope;
  213. var entries = globalScope;
  214. var keys = globalScope;
  215. var values = globalScope;
  216. with([])
  217. {
  218. function foo()
  219. {
  220. function bar()
  221. {
  222. function f00()
  223. {
  224. function bat()
  225. {
  226. assert.areEqual(globalScope, find, "find property is not brought into scope by the with statement");
  227. assert.areEqual(globalScope, findIndex,"findIndex property is not brought into scope by the with statement");
  228. assert.areEqual(globalScope, entries, "entries property is not brought into scope by the with statement");
  229. assert.areEqual(globalScope, keys, "keys property is not brought into scope by the with statement");
  230. assert.areEqual(globalScope, values, "values property is not brought into scope by the with statement");
  231. }
  232. }
  233. }
  234. }
  235. }
  236. }
  237. },
  238. {
  239. name: "Nested with statements",
  240. body: function ()
  241. {
  242. var str =
  243. {
  244. search : function () {},
  245. split: function () {},
  246. concat : function () {},
  247. reduce: function () {},
  248. [Symbol.unscopables]: {"search" : true,"split": true,"concat" : true,"reduce" : true}
  249. };
  250. var arr =
  251. {
  252. find : function () {},
  253. keys: function () {},
  254. concat : function () {},
  255. reduce: function () {},
  256. [Symbol.unscopables]: {"find" : true,"keys" : true}
  257. };
  258. var globalScope = -1;
  259. var find = globalScope;
  260. var keys = globalScope;
  261. var search = globalScope;
  262. var split = globalScope;
  263. var reduce = globalScope;
  264. var concat = globalScope;
  265. var arrConcat = arr.concat;
  266. var arrReduce = arr.reduce;
  267. with(arr)
  268. {
  269. with(str)
  270. {
  271. assert.areEqual(globalScope, search, "search property is not brought into scope by the with statement");
  272. assert.areEqual(globalScope, split, "split property is not brought into scope by the with statement");
  273. assert.areEqual(arrConcat, concat, "concat should be on the array scope");
  274. assert.areEqual(arrReduce, reduce, "toString should be on the array scope");
  275. assert.areEqual(globalScope, find, "find property is not brought into scope by the with statement");
  276. assert.areEqual(globalScope, keys, "keys property is not brought into scope by the with statement");
  277. }
  278. }
  279. arr[Symbol.unscopables]["concat"] = true;
  280. arr[Symbol.unscopables]["reduce"] = true;
  281. with(arr)
  282. {
  283. with(str)
  284. {
  285. assert.areEqual(globalScope, search, "search property is not brought into scope by the with statement");
  286. assert.areEqual(globalScope, split, "split property is not brought into scope by the with statement");
  287. assert.areEqual(globalScope, concat, "concat property is not brought into scope by the with statement");
  288. assert.areEqual(globalScope, reduce, "toString property is not brought into scope by the with statement");
  289. assert.areEqual(globalScope, find, "find property is not brought into scope by the with statement");
  290. assert.areEqual(globalScope, keys, "keys property is not brought into scope by the with statement");
  291. }
  292. }
  293. }
  294. },
  295. {
  296. name: "Inheritance test",
  297. body: function ()
  298. {
  299. function foo ()
  300. {
  301. var p = {a: 1};
  302. var obj = {__proto__: p, [Symbol.unscopables]: {'a' : true}};
  303. var a = 2;
  304. with (obj)
  305. {
  306. assert.areEqual(2,a,""); //Spec change we no longer inherit
  307. }
  308. }
  309. foo();
  310. let p = {a: 1};
  311. let obj = {__proto__: p, [Symbol.unscopables]: {'a' : true}};
  312. let a = 2;
  313. with (obj)
  314. {
  315. assert.areEqual(2,a,"");
  316. }
  317. }
  318. },
  319. {
  320. name: "Per object unscopables check",
  321. body: function ()
  322. {
  323. var globalScope = -1;
  324. var proto = { a: 1, b: 2, c: 3, [Symbol.unscopables]: {'a' : true} };
  325. var child = {__proto__: proto, [Symbol.unscopables]: {'b' : true} };
  326. var child2 = {__proto__: proto, b: 21, c: 31, [Symbol.unscopables]: {'b' : true} };
  327. var a = globalScope;
  328. var b = globalScope;
  329. with(child)
  330. {
  331. assert.areEqual(1, a, "Get @@unscopables finds {'b' : true} on child fist so a is not unscoped");
  332. assert.areEqual(globalScope, b, "b is blacklisted in child and we don't property walk to find b on proto");
  333. assert.areEqual(3, c, "c is only on the proto");
  334. a = 3;
  335. b = 4;
  336. assert.areEqual(2,proto.b,"proto.b is never set b\c child b is unnscopable");
  337. }
  338. assert.areEqual(4,b,"root.b is set to 4 b\c child b is unscopable");
  339. b = globalScope;
  340. assert.areEqual(3,child.a,"child.a should be set to 3");
  341. assert.areEqual(1,proto.a,"proto.a should be set to 1");
  342. var a = globalScope;
  343. proto[Symbol.unscopables]["c"] = true;
  344. with(child2)
  345. {
  346. assert.areEqual(1, a, "Get @@unscopables finds {'b' : true} on child fist so a is not unscoped");
  347. assert.areEqual(globalScope, b, "b is blacklisted in child2 and we don't property walk to find b on proto");
  348. assert.areEqual(31, c, "c is blacklisted in proto but not child2");
  349. delete c;
  350. assert.areEqual(3,proto.c,"No delete should have happened");
  351. assert.areEqual(3,child2.c,"delete should have happened to 31 should now be 3");
  352. delete c;
  353. assert.areEqual(3,proto.c, "No delete should have happened");
  354. assert.areEqual(3,child2.c,"child2 is still 3");
  355. }
  356. }
  357. },
  358. {
  359. name: "@@unscopables overwritten as something other than an object",
  360. body: function ()
  361. {
  362. var globalScope = -1;
  363. var find = globalScope;
  364. var values = globalScope;
  365. var c =
  366. {
  367. find : function () {},
  368. values: function () {},
  369. [Symbol.unscopables]: {"find" : true, "values" : true }
  370. };
  371. c[Symbol.unscopables] = 5;
  372. with(c)
  373. {
  374. assert.isTrue(globalScope != find, "find should be on Array scope");
  375. assert.isTrue(globalScope != values, "values should be on Array scope");
  376. }
  377. }
  378. },
  379. {
  380. name: "Eval tests",
  381. body: function ()
  382. {
  383. var globalScope = -1;
  384. var find = globalScope;
  385. var c =
  386. {
  387. find : function () {},
  388. [Symbol.unscopables]: {"find" : true }
  389. };
  390. with (c)
  391. {
  392. assert.areEqual(globalScope, eval("find"),"This property is not brought into scope by the with statement");
  393. eval("find = 2");
  394. assert.areEqual(2, eval("find"),"This property is not brought into scope by the with statement");
  395. assert.areEqual(false, eval("delete find"),"You can only delete properties");
  396. }
  397. }
  398. },
  399. {
  400. name: "Mutation test (like the redefinition test just in the with statement)",
  401. body: function ()
  402. {
  403. var o = {a: 1};
  404. var a = 2;
  405. with (o)
  406. {
  407. o[Symbol.unscopables] = {'a' : true }
  408. assert.areEqual(2,a, "root.a should have been set");
  409. }
  410. }
  411. },
  412. {
  413. name: "Compound assignment",
  414. body: function ()
  415. {
  416. var o = {a: 1};
  417. var a = 2;
  418. with (o)
  419. {
  420. a += (o[Symbol.unscopables] = {'a' : true },2);
  421. // This is a modification of Brian's Operator precedence test above
  422. // This is a tricky one a is originally not blacklisted so the a we use is o.a
  423. // then the assignment happens after the blacklisting
  424. assert.areEqual(3,a, "should be 1+2");
  425. }
  426. assert.areEqual(1,o.a, "root.a should not have changed");
  427. assert.areEqual(3,a, "should be 1+2");
  428. }
  429. },
  430. {
  431. name: "Global Object affect",
  432. body: function ()
  433. {
  434. var a = 1
  435. this[Symbol.unscopables] = {"a" : true }
  436. assert.areEqual(1,a, "No with statement so blacklist should never hit");
  437. var b;
  438. this[Symbol.unscopables]["b"] = true;
  439. b = 1;
  440. assert.areEqual(1,b, "No with statement so blacklist should never hit");
  441. this[Symbol.unscopables]["c"] = true;
  442. var c = 1;
  443. assert.areEqual(1,b, "No with statement so blacklist should never hit");
  444. }
  445. },
  446. {
  447. name: "Set test",
  448. body: function ()
  449. {
  450. with ([])
  451. {
  452. find = 2;
  453. assert.areEqual(2, find,"find property is not brought into scope by the with statement");
  454. }
  455. assert.areEqual(2, find, "find property is not brought into scope by the with statement");
  456. // strict mode
  457. with ([])
  458. {
  459. function test()
  460. {
  461. "use strict";
  462. assert.throws( function () {findIndex = 2;},ReferenceError,"In strict mode the variable is undefined");
  463. }
  464. test();
  465. }
  466. // assignment test with let
  467. let o = {[Symbol.unscopables]: {'b' : true }};
  468. let b = -1;
  469. with (o)
  470. {
  471. b = 1;
  472. }
  473. // assignment test with evals
  474. assert.areEqual(undefined,o.b,"o.b should never have been set");
  475. assert.areEqual(1,b, "root.a should have been set");
  476. with(o)
  477. {
  478. eval("b =2;");
  479. }
  480. assert.areEqual(undefined,o.b,"o.b should never have been set");
  481. assert.areEqual(2,b, "root.a should have been set");
  482. }
  483. },
  484. {
  485. name: "Define unscopables for RegExp and then check Global Scope",
  486. body: function ()
  487. {
  488. var globalScope = -1;
  489. var input = globalScope;
  490. var lastMatch = globalScope;
  491. var lastParen = globalScope;
  492. var leftContext = globalScope;
  493. var props = {"input" : true, "lastMatch" : true ,"lastParen" : true ,"leftContext" : true};
  494. RegExp[Symbol.unscopables] = props;
  495. for( i in RegExp[Symbol.unscopables])
  496. {
  497. assert.areEqual(props[i], RegExp[Symbol.unscopables][i]);
  498. }
  499. assert.isTrue(RegExp.hasOwnProperty(Symbol.unscopables), "RegExp should have RegExp.prototype[@@unscopables] property after definition");
  500. with(RegExp)
  501. {
  502. assert.areEqual(globalScope, input, "input property is not brought into scope by the with statement");
  503. assert.areEqual(globalScope, lastMatch, "lastMatch property is not brought into scope by the with statement");
  504. assert.areEqual(globalScope, lastParen, "lastParen property is not brought into scope by the with statement");
  505. assert.areEqual(globalScope, leftContext, "leftContext property is not brought into scope by the with statement");
  506. }
  507. }
  508. },
  509. {
  510. name: "Confirm a call to @@unscopables happens if the environment record property is called",
  511. body: function ()
  512. {
  513. var env = {x : 1};
  514. var callCount = 0;
  515. Object.defineProperty(env, Symbol.unscopables, {
  516. get: function() {
  517. callCount += 1;
  518. }
  519. });
  520. with (env) {
  521. void x;
  522. }
  523. assert.areEqual(1, callCount, "The environment record has the requested property confirm a call happens");
  524. }
  525. },
  526. {
  527. name: "Spec Bug Fix for OS 4892049",
  528. body: function ()
  529. {
  530. var x = 0;
  531. var env = {};
  532. var callCount = 0;
  533. Object.defineProperty(env, Symbol.unscopables, {
  534. get: function() {
  535. callCount += 1;
  536. }
  537. });
  538. with (env) {
  539. void x;
  540. }
  541. assert.areEqual(0, callCount, "If the environment record does not have requested property don't look up unscopables blacklist");
  542. var x = 0;
  543. var env = { x: 1 };
  544. env[Symbol.unscopables] = {};
  545. env[Symbol.unscopables].x = false;
  546. with (env) {
  547. assert.areEqual(1, x, "8.1.1.2.1 step 9a return ToBoolean on the getProperty, if false property is not blacklisted");
  548. }
  549. }
  550. },
  551. {
  552. name: "Let unscopables be Get(bindings, @@unscopables) should do prototype chain lookups on the blacklist",
  553. body: function ()
  554. {
  555. var blackList = { x : true };
  556. Object.setPrototypeOf(blackList, { y: true });
  557. var env = { x : 1, y : 2, [Symbol.unscopables] : blackList};
  558. var x = -1;
  559. var y = -2;
  560. with(env)
  561. {
  562. assert.areEqual(-1, x, "x is blacklist on the @@unscopables object");
  563. assert.areEqual(-2, y, "y is blacklist on the @@unscopables prototype");
  564. }
  565. }
  566. },
  567. {
  568. name: "Array.prototype[@@unscopables] [[prototype]] slot is null",
  569. body: function ()
  570. {
  571. assert.areEqual(null, Object.getPrototypeOf(Array.prototype[Symbol.unscopables]), "Array.prototype[@@unscopables].__proto__ === null");
  572. }
  573. },
  574. {
  575. name: "Array.prototype[@@unscopables] property descriptor",
  576. body: function ()
  577. {
  578. var p = Object.getOwnPropertyDescriptor(Array.prototype, Symbol.unscopables);
  579. assert.isFalse(p.writable, "Object.getOwnPropertyDescriptor(Array.prototype, Symbol.unscopables).writable === false");
  580. assert.isFalse(p.enumerable, "Object.getOwnPropertyDescriptor(Array.prototype, Symbol.unscopables).enumerable === false");
  581. assert.isTrue(p.configurable, "Object.getOwnPropertyDescriptor(Array.prototype, Symbol.unscopables).configurable === true");
  582. }
  583. },
  584. ];
  585. testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });