spreadIterator.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  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 emptyIterator = function(){ return {next: function () { return { done: true, value: 0};}}}
  7. var simpleIterator = function(args) {
  8. var iter = (function(args) {
  9. var j = 0;
  10. var length = args.length;
  11. return function Iterator() {
  12. return {next: function() {
  13. if (j < length)
  14. {
  15. return { value: args[j++], done: false };
  16. }
  17. else
  18. {
  19. j = 0;
  20. return { done: true };
  21. }
  22. }}}})(args)
  23. return iter;};
  24. function __createIterableObject(a, b, c) {
  25. if (typeof Symbol === "function" && Symbol.iterator) {
  26. var arr = [a, b, c, ,];
  27. var iterable = {
  28. next: function() {
  29. return { value: arr.shift(), done: arr.length <= 0 };
  30. },
  31. };
  32. iterable[Symbol.iterator] = function(){ return iterable; }
  33. return iterable;
  34. }
  35. else {
  36. return eval("(function*() { yield a; yield b; yield c; }())");
  37. }
  38. }
  39. var tests = [
  40. {
  41. name: "Spread TypeError: Function expected",
  42. body: function ()
  43. {
  44. var a = [1, 2];
  45. a[Symbol.iterator] = 0;
  46. assert.throws(function () { var b = [...1]; }, TypeError, "1 is not iterable", "Function expected");
  47. assert.throws(function () { var b = [...a]; }, TypeError, "Spread when the iterator is not a function", "Function expected");
  48. var noNextIterator = function(){ return {done: true, value: 0};}
  49. a[Symbol.iterator] = noNextIterator;
  50. assert.throws(function () { var b = [...a]; }, TypeError, "Spread when the iterator does not have a next is not valid", "Function expected");
  51. a = function() {}
  52. assert.throws(function () { var b = [...a]; }, TypeError, "Spread when the iterator is not a function", "Function expected");
  53. a = {}
  54. assert.throws(function () { var b = [...a]; }, TypeError, "Spread when the iterator is not a function", "Function expected");
  55. a = /r/g;
  56. assert.throws(function () { var b = [...a]; }, TypeError, "Spread when the iterator is not a function", "Function expected");
  57. }
  58. },
  59. {
  60. name: "Kangax Tests",
  61. body: function ()
  62. {
  63. assert.areEqual("𠮷", Array(..."𠮷")[0]);
  64. assert.areEqual("a", Array(..."a")[0]);
  65. assert.areEqual("𠮷", [..."𠮷"][0]);
  66. assert.areEqual("a", [..."a" ][0]);
  67. var iterableNum = __createIterableObject(1, 2, 3);
  68. //assert.areEqual(3, Math.max(...Object.create(iterableNum))); //TODO look into why this doesn't work
  69. assert.areEqual(3, Math.max(...iterableNum));
  70. var iterableStr = __createIterableObject("b", "c", "d");
  71. assert.areEqual("d", ["a", ...iterableStr, "e"][3]);
  72. //assert.areEqual("d", ["a", ...Object.create(iterableStr), "e"][3]);
  73. }
  74. },
  75. {
  76. name: "Spread TypeError: Object expected",
  77. body: function ()
  78. {
  79. var a = [1, 2];
  80. a[Symbol.iterator] = function() {};
  81. assert.throws(function () { var b = [...a]; }, TypeError, "the @@iterator function should return an Object", "Object expected");
  82. }
  83. },
  84. {
  85. name: "A spread argument's @@iterator has changed",
  86. body: function ()
  87. {
  88. var a = [1, 2];
  89. var c = [4, 5];
  90. count = 0;
  91. a[Symbol.iterator] = function() {
  92. return { next: function() {
  93. if (count < 3)
  94. {
  95. return { value: count++, done: false };
  96. }
  97. else
  98. {
  99. return { done: true };
  100. }
  101. }};};
  102. var result = [-1,0,1,2,3,4,5,6];
  103. var b = [-1,...a,3,...c,6]
  104. assert.areEqual(result,b, "confirm only a spreads with a counting iterator");
  105. assert.areEqual(3,count,"confirm side effect of incrementing count equals the number we expect");
  106. c[Symbol.iterator] = simpleIterator(c);
  107. count = 0;
  108. var b = [-1,...a,3,...c,6];
  109. assert.areEqual(result,b, "confirm both a and c spread, with c using an iterator that increments c's array");
  110. }
  111. },
  112. {
  113. name: "Array Spread Iterator causes parameter side effects",
  114. body: function ()
  115. {
  116. var a = [1,2];
  117. var b = 4
  118. var c = { 0 : false };
  119. var d = "foo";
  120. var e = function foo() {}
  121. var simpleIteratorWithParamSideEffect = function(args) {
  122. var iter = (function(args) {
  123. var j = 0;
  124. var length = args.length;
  125. return function Iterator() {
  126. b = 5;
  127. c[0] = true;
  128. d = "bar";
  129. e = function goo() {}
  130. return {next: function() {
  131. if (j < length)
  132. {
  133. return { value: args[j++], done: false };
  134. }
  135. else
  136. {
  137. j = 0;
  138. return { done: true };
  139. }
  140. }}}})(args)
  141. return iter;};
  142. a[Symbol.iterator] = simpleIteratorWithParamSideEffect(a);
  143. var result = [...a,b,c,d,e];
  144. assert.areEqual(1,result[0]);
  145. assert.areEqual(2,result[1]);
  146. assert.areEqual(5,result[2]);
  147. assert.areEqual(true,result[3][0]);
  148. assert.areEqual("bar",result[4]);
  149. assert.areEqual("goo",result[5].name);
  150. }
  151. },
  152. {
  153. name: "Spread with String iterators",
  154. body: function ()
  155. {
  156. var a = "foobar";
  157. var b = [1,2,...a,4];
  158. var results = [1,2,'f','o','o','b','a','r',4];
  159. assert.areEqual(results,b, "confirm we can spread strings");
  160. var aa = new String(a);
  161. aa[Symbol.iterator] = simpleIterator(aa);
  162. var b = [1,2,...aa,4];
  163. assert.areEqual(results,b, "override the string iterator with a custom iterator that emulates the built in iterator");
  164. aa[Symbol.iterator] = emptyIterator;
  165. var b = [1,2,...aa,4];
  166. assert.areEqual([1,2,4],b, "override the string iterator with an empty iterator");
  167. }
  168. },
  169. {
  170. name: "Spread with typedArray iterators",
  171. body: function ()
  172. {
  173. var buf = [2, 3, 4, 5];
  174. var typedArrays = [new Int8Array(buf), new Uint8Array(buf), new Uint8ClampedArray(buf), new Uint16Array(buf),
  175. new Int16Array(buf), new Int32Array(buf), new Uint32Array(buf), new Float32Array(buf),
  176. new Float64Array(buf)];
  177. for(var i = 0; i < typedArrays.length;i++)
  178. {
  179. var a = typedArrays[i];
  180. var b = [1,...a,6];
  181. var results = [1,...buf,6];
  182. assert.areEqual(results,b, "confirm TypedArrays still spread");
  183. a[Symbol.iterator] = simpleIterator(a);
  184. var b = [1,...a,6];
  185. assert.areEqual(results,b, "force typed arrays to use a user defined iterator that emulates the buitin iterator behavior");
  186. a[Symbol.iterator] = emptyIterator;
  187. var b = [1,...a,6];
  188. assert.areEqual([1,6],b, " make typed arrays use an empty iterator");
  189. }
  190. }
  191. },
  192. {
  193. name: "Spread with Maps & Sets",
  194. body: function ()
  195. {
  196. var kvArray = [["one", 1], ["two", 2]];
  197. var myMap = new Map(kvArray);
  198. var b = [0,...myMap];
  199. assert.areEqual([0,["one", 1], ["two", 2]],b);
  200. var mapIter = myMap.keys();
  201. var b = [0,...mapIter];
  202. assert.areEqual([0, "one", "two"],b,"should show 0 and then myMap's keys");
  203. var mapIter = myMap.values();
  204. var b = [0,...mapIter];
  205. assert.areEqual([0, 1, 2],b,"should show 0 and then myMap's keys");
  206. var aSet = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
  207. var b = [0,...aSet, 6];
  208. assert.areEqual([0,1,2,3,4,5, 6],b);
  209. }
  210. },
  211. {
  212. name: "Spread with Objects",
  213. body: function ()
  214. {
  215. var obj = { 0 : 1, 1: 1, 2 : 1, 3 : 1, length : 4}
  216. assert.throws(function () { var b = [...obj]; }, TypeError, "Spread is no longer dependent on length", "Function expected");
  217. obj[Symbol.iterator] = simpleIterator(obj);
  218. var b = [...obj];
  219. assert.areEqual([1,1,1,1],b);
  220. }
  221. },
  222. {
  223. name: "Spread with WeakMaps & WeakSets",
  224. body: function ()
  225. {
  226. var kvArray = [[{animal: "dog"},"foo" ], [{animal: "cat"},"bar" ]];
  227. var myMap = new WeakMap(kvArray);
  228. myMap[Symbol.iterator] = emptyIterator;
  229. var b = [0,...myMap];
  230. assert.areEqual([0],b, "confirm any object can spread as long as it has an iterator");
  231. var aSet = new WeakSet([{},{},{},[4], [5], [5], [5], [5]]);
  232. aSet[Symbol.iterator] = emptyIterator;
  233. var b = [0,...aSet, 6];
  234. assert.areEqual([0,6],b,"confirm any object can spread as long as it has an iterator");
  235. }
  236. },
  237. {
  238. name: "Spread with arguments",
  239. body: function ()
  240. {
  241. var b = [];
  242. function bar(a,a){b = [...arguments];}
  243. bar([1],[2]);
  244. assert.areEqual([[1],[2]], b);
  245. class d { constructor() {arguments[Symbol.iterator] = simpleIterator(arguments); b = [...arguments]; } };
  246. new d(1,2,3);
  247. assert.areEqual([1,2,3],b, "confirm we can override the built iterator");
  248. new d();
  249. assert.areEqual([],b);
  250. function foo(a, b, c, ...rest) { return [a, b, c, ...rest]; }
  251. b= foo(1,2,3,4,5,6)
  252. assert.areEqual([1,2,3,4,5,6],b);
  253. b = foo(1,2,3);
  254. assert.areEqual([1,2,3],b);
  255. b = foo(1,2);
  256. assert.areEqual([1,2,undefined],b);
  257. }
  258. },
  259. {
  260. name: "Function Spread with typedArray iterators",
  261. body: function ()
  262. {
  263. var buf = [2, 3, 4];
  264. function test1(a,b,c,d,e)
  265. {
  266. var expected = [a, ...buf, e];
  267. var results = [a,b,c,d,e];
  268. assert.areEqual(results,expected, "confirm TypedArrays still spread");
  269. }
  270. function test2(a,b,c,d,e)
  271. {
  272. assert.areEqual(1,a, "should be nonspreadable numeric primitive 1");
  273. assert.areEqual(5,b, "should be nonspreadable numeric primitive 5");
  274. assert.areEqual(undefined,c);
  275. assert.areEqual(undefined,d);
  276. assert.areEqual(undefined,e);
  277. }
  278. var typedArrays = [new Int8Array(buf), new Uint8Array(buf), new Uint8ClampedArray(buf), new Uint16Array(buf),
  279. new Int16Array(buf), new Int32Array(buf), new Uint32Array(buf), new Float32Array(buf),
  280. new Float64Array(buf)];
  281. for(var i = 0; i < typedArrays.length;i++)
  282. {
  283. var a = typedArrays[i];
  284. test1(1,...a,5);
  285. a[Symbol.iterator] = simpleIterator(a);
  286. test1(1,...a,5);
  287. a[Symbol.iterator] = emptyIterator;
  288. test2(1,...a,5);
  289. }
  290. }
  291. },
  292. {
  293. name: "Function Spread TypeError: Function expected",
  294. body: function ()
  295. {
  296. var a = [1,2,3];
  297. a[Symbol.iterator] = 0;
  298. function f(z,...v) {}
  299. assert.throws(function () { f(0,...a); }, TypeError, "Spread when the iterator is not a function", "Function expected");
  300. var noNextIterator = function(){ return {done: true, value: 0};}
  301. a[Symbol.iterator] = noNextIterator;
  302. assert.throws(function () { f(0,...a); }, TypeError, "Spread when the iterator does not have a next is not valid", "Function expected");
  303. a = function() {}
  304. assert.throws(function () { f(0,...a); }, TypeError, "Spread when the iterator is not a function", "Function expected");
  305. a = {}
  306. assert.throws(function () { f(0,...a); }, TypeError, "Spread when the iterator is not a function", "Function expected");
  307. a = /r/g;
  308. assert.throws(function () { f(0,...a); }, TypeError, "Spread when the iterator is not a function", "Function expected");
  309. var b = 11;
  310. assert.throws(function () { f(0,...b); }, TypeError, "b is a primitive and thus is not iterable", "Function expected");
  311. }
  312. },
  313. {
  314. name: "Function Spread TypeError: Object expected",
  315. body: function ()
  316. {
  317. var a = [1, 2];
  318. a[Symbol.iterator] = function() {};
  319. function f(z,v) {}
  320. assert.throws(function () { f(0,...a); }, TypeError, "the @@iterator function should return an Object", "Object expected");
  321. }
  322. },
  323. {
  324. name: "Function Spread Iterator with Arguments",
  325. body: function ()
  326. {
  327. // constructor() { super(...arguments); } (equivalent to constructor(...args) { super(...args); } )
  328. class base {}
  329. class child extends base {}
  330. assert.doesNotThrow(function () { var o = new child(); }, "we should not, get a type Error, arguments has an iterator");
  331. var a = [1];
  332. var b = 'a';
  333. var c = new Set([1,1,1,1]);
  334. function test(a,b,c)
  335. {
  336. assert.areEqual([1], a);
  337. assert.areEqual('a', b);
  338. assert.areEqual([1], [...c]);
  339. }
  340. class child2 extends base {
  341. constructor(a,b,c) {
  342. super(...arguments);
  343. test(...arguments);
  344. }};
  345. var o = new child2(a, b, c);
  346. class child3 extends base {
  347. constructor(a,b,c) {
  348. arguments[Symbol.iterator] = simpleIterator(arguments);
  349. super(...arguments);
  350. test(...arguments);
  351. }};
  352. var o = new child3(a, b, c);
  353. }
  354. },
  355. {
  356. name: "Function Spread Iterator override for all objects",
  357. body: function ()
  358. {
  359. var a = new String("fox");
  360. var b = [1,2,3];
  361. var c = {0 : 1, 1 : 2, 2 : 3, length : 3};
  362. a[Symbol.iterator] = simpleIterator(a);
  363. b[Symbol.iterator] = simpleIterator(b);
  364. c[Symbol.iterator] = simpleIterator(c);
  365. function f(a, b, c, d, e, f, w, x, y, z)
  366. {
  367. assert.areEqual('f',a, "should be value at global string a[0]");
  368. assert.areEqual('o',b, "should be value at global string a[1]");
  369. assert.areEqual('x',c, "should be value at global string a[2]");
  370. assert.areEqual(1,d, "should be value at global array b[0]");
  371. assert.areEqual(2,e, "should be value at global array b[1]");
  372. assert.areEqual(3,f, "should be value at global array b[2]");
  373. assert.areEqual(1,w, "should be value at global object c[0]");
  374. assert.areEqual(2,x, "should be value at global object c[1]");
  375. assert.areEqual(3,y, "should be value at global object c[2]");
  376. assert.areEqual(4,z, "should be nonspreadable numeric primitive 4");
  377. }
  378. f(...a, ...b, ...c, 4);
  379. function d(a, b, c, d, e, f, w, x, y, z)
  380. {
  381. assert.areEqual(4,a, "should be nonspreadable numeric primitive 4");
  382. assert.areEqual(undefined,b);
  383. assert.areEqual(undefined,c);
  384. assert.areEqual(undefined,d);
  385. assert.areEqual(undefined,e);
  386. assert.areEqual(undefined,f);
  387. assert.areEqual(undefined,w);
  388. assert.areEqual(undefined,x);
  389. assert.areEqual(undefined,y);
  390. assert.areEqual(undefined,z);
  391. }
  392. a[Symbol.iterator] = emptyIterator;
  393. b[Symbol.iterator] = emptyIterator;
  394. c[Symbol.iterator] = emptyIterator;
  395. d(...a, ...b, ...c, 4);
  396. }
  397. },
  398. {
  399. name: "Function Spread Iterator causes parameter side effects",
  400. body: function ()
  401. {
  402. var a = [1,2];
  403. var b = [4,5];
  404. var c = [7,8];
  405. var simpleIteratorWithParamSideEffect = function(args) {
  406. var iter = (function(args) {
  407. var j = 0;
  408. var length = args.length;
  409. return function Iterator() {
  410. c[Symbol.iterator] = emptyIterator;
  411. a[Symbol.iterator] = emptyIterator;
  412. return {next: function() {
  413. if (j < length)
  414. {
  415. return { value: args[j++], done: false };
  416. }
  417. else
  418. {
  419. j = 0;
  420. return { done: true };
  421. }
  422. }}}})(args)
  423. return iter;};
  424. b[Symbol.iterator] = simpleIteratorWithParamSideEffect(b);
  425. function test(a,b,c,d,e,f)
  426. {
  427. assert.areEqual(1,a, "iterator side effect on array 'a' happens after spreading 'a'");
  428. assert.areEqual(2,b, "iterator side effect on array 'a' happens after spreading 'a'");
  429. assert.areEqual(4,c);
  430. assert.areEqual(5,d);
  431. assert.areEqual(undefined,e,"iterator side effect on array 'c' happens before spreading 'c'");
  432. assert.areEqual(undefined,f,"iterator side effect on array 'c' happens before spreading 'c'");
  433. }
  434. test(...a,...b,...c);
  435. }
  436. },
  437. {
  438. name: "Function Spread Iterator causes parameter side effects",
  439. body: function ()
  440. {
  441. var a = [1,2];
  442. var b = 4
  443. var c = { 0 : false };
  444. var d = "foo";
  445. var e = function foo() {}
  446. var simpleIteratorWithParamSideEffect = function(args) {
  447. var iter = (function(args) {
  448. var j = 0;
  449. var length = args.length;
  450. return function Iterator() {
  451. b = 5;
  452. c[0] = true;
  453. d = "bar";
  454. e = function goo() {}
  455. return {next: function() {
  456. if (j < length)
  457. {
  458. return { value: args[j++], done: false };
  459. }
  460. else
  461. {
  462. j = 0;
  463. return { done: true };
  464. }
  465. }}}})(args)
  466. return iter;};
  467. a[Symbol.iterator] = simpleIteratorWithParamSideEffect(a);
  468. function test(a,b,c,d,e,f)
  469. {
  470. assert.areEqual(1,a, "iterator side effect on array 'a' happens after spreading 'a'");
  471. assert.areEqual(2,b, "iterator side effect on array 'a' happens after spreading 'a'");
  472. assert.areEqual(5,c);
  473. assert.areEqual(true,d[0]);
  474. assert.areEqual("bar",e);
  475. assert.areEqual("goo",f.name);
  476. }
  477. test(...a,b,c,d,e);
  478. }
  479. },
  480. {
  481. name: "Function Spread Iterator override for some objects",
  482. body: function ()
  483. {
  484. var a = new String("fox");
  485. var b = [1,2,3];
  486. var c = {0 : 1, 1 : 2, 2 : 3, length : 3};
  487. a[Symbol.iterator] = simpleIterator(a);
  488. c[Symbol.iterator] = simpleIterator(c);
  489. function f(a, b, c, d, e, f, w, x, y, z)
  490. {
  491. assert.areEqual('f',a, "should be value at global string a[0]");
  492. assert.areEqual('o',b, "should be value at global string a[1]");
  493. assert.areEqual('x',c, "should be value at global string a[2]");
  494. assert.areEqual(1,d, "should be value at global array b[0]");
  495. assert.areEqual(2,e, "should be value at global array b[1]");
  496. assert.areEqual(3,f, "should be value at global array b[2]");
  497. assert.areEqual(1,w, "should be value at global object c[0]");
  498. assert.areEqual(2,x, "should be value at global object c[1]");
  499. assert.areEqual(3,y, "should be value at global object c[2]");
  500. assert.areEqual(4,z, "should be nonspreadable numeric primitive 4");
  501. }
  502. f(...a, ...b, ...c, 4);
  503. function d(a, b, c, d, e, f, w, x, y, z)
  504. {
  505. assert.areEqual(1,a, "should be value at global array b[0]");
  506. assert.areEqual(2,b, "should be value at global array b[1]");
  507. assert.areEqual(3,c, "should be value at global array b[2]");
  508. assert.areEqual(4,d, "should be nonspreadable numeric primitive 4");
  509. assert.areEqual(undefined,e);
  510. assert.areEqual(undefined,f);
  511. assert.areEqual(undefined,w);
  512. assert.areEqual(undefined,x);
  513. assert.areEqual(undefined,y);
  514. assert.areEqual(undefined,z);
  515. }
  516. a[Symbol.iterator] = emptyIterator;
  517. c[Symbol.iterator] = emptyIterator;
  518. d(...a, ...b, ...c, 4);
  519. }
  520. }
  521. ];
  522. testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });