forloops-per-iteration-bindings.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  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. // ES6 for/for-in/for-of loops per iteration loop variable bindings tests
  6. WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
  7. var tests = [
  8. {
  9. name: "[slot array] for-in and for-of loops have per iteration bindings for let and const variables",
  10. body: function () {
  11. var a = [];
  12. var i = 0;
  13. for (let x in { a: 1, b: 2, c: 3 }) {
  14. a[i++] = function () { return x; };
  15. }
  16. for (const x in { a: 1, b: 2, c: 3 }) {
  17. a[i++] = function () { return x; };
  18. }
  19. for (let x of [ 1, 2, 3 ]) {
  20. a[i++] = function () { return x; };
  21. }
  22. for (const x of [ 1, 2, 3 ]) {
  23. a[i++] = function () { return x; };
  24. }
  25. assert.areEqual('a', a[0](), "first for-let-in function captures x when it is equal to 'a'");
  26. assert.areEqual('b', a[1](), "second for-let-in function captures x when it is equal to 'b'");
  27. assert.areEqual('c', a[2](), "third for-let-in function captures x when it is equal to 'c'");
  28. assert.areEqual('a', a[3](), "first for-const-in function captures x when it is equal to 'a'");
  29. assert.areEqual('b', a[4](), "second for-const-in function captures x when it is equal to 'b'");
  30. assert.areEqual('c', a[5](), "third for-const-in function captures x when it is equal to 'c'");
  31. assert.areEqual(1, a[6](), "first for-let-of function captures x when it is equal to 1");
  32. assert.areEqual(2, a[7](), "second for-let-of function captures x when it is equal to 2");
  33. assert.areEqual(3, a[8](), "third for-let-of function captures x when it is equal to 3");
  34. assert.areEqual(1, a[9](), "first for-const-of function captures x when it is equal to 1");
  35. assert.areEqual(2, a[10](), "second for-const-of function captures x when it is equal to 2");
  36. assert.areEqual(3, a[11](), "third for-const-of function captures x when it is equal to 3");
  37. }
  38. },
  39. {
  40. name: "[activation object] for-in and for-of loops have per iteration bindings for let and const variables",
  41. body: function () {
  42. var a = [];
  43. var i = 0;
  44. for (let x in { a: 1, b: 2, c: 3 }) {
  45. a[i++] = function () { return eval("x"); };
  46. }
  47. for (const x in { a: 1, b: 2, c: 3 }) {
  48. a[i++] = function () { return eval("x"); };
  49. }
  50. for (let x of [ 1, 2, 3 ]) {
  51. a[i++] = function () { return eval("x"); };
  52. }
  53. for (const x of [ 1, 2, 3 ]) {
  54. a[i++] = function () { return eval("x"); };
  55. }
  56. assert.areEqual('a', a[0](), "first for-let-in function captures x when it is equal to 'a'");
  57. assert.areEqual('b', a[1](), "second for-let-in function captures x when it is equal to 'b'");
  58. assert.areEqual('c', a[2](), "third for-let-in function captures x when it is equal to 'c'");
  59. assert.areEqual('a', a[3](), "first for-const-in function captures x when it is equal to 'a'");
  60. assert.areEqual('b', a[4](), "second for-const-in function captures x when it is equal to 'b'");
  61. assert.areEqual('c', a[5](), "third for-const-in function captures x when it is equal to 'c'");
  62. assert.areEqual(1, a[6](), "first for-let-of function captures x when it is equal to 1");
  63. assert.areEqual(2, a[7](), "second for-let-of function captures x when it is equal to 2");
  64. assert.areEqual(3, a[8](), "third for-let-of function captures x when it is equal to 3");
  65. assert.areEqual(1, a[9](), "first for-const-of function captures x when it is equal to 1");
  66. assert.areEqual(2, a[10](), "second for-const-of function captures x when it is equal to 2");
  67. assert.areEqual(3, a[11](), "third for-const-of function captures x when it is equal to 3");
  68. }
  69. },
  70. {
  71. name: "const variables in for-in and for-of loops cannot be assigned",
  72. body: function () {
  73. assert.throws(function () { for (const x in { a: 1 }) { x = 1; } }, TypeError, "assignment to const known at parse time in for-in loop", "Assignment to const");
  74. assert.throws(function () { for (const x of [ 0 ]) { x = 1; } }, TypeError, "assignment to const known at parse time in for-of loop", "Assignment to const");
  75. assert.throws(function () { eval("for (const x in { a: 1 }) { x = 1; }"); }, TypeError, "assignment to const known at eval parse time in for-in loop", "Assignment to const");
  76. assert.throws(function () { for (const x in { a: 1 }) { eval("x = 1;"); } }, TypeError, "assignment to const only known at run time in for-in loop", "Assignment to const");
  77. assert.throws(function () { eval("for (const x of [ 0 ]) { x = 1; }"); }, TypeError, "assignment to const known at eval parse time in for-of loop", "Assignment to const");
  78. assert.throws(function () { for (const x of [ 0 ]) { eval("x = 1;"); } }, TypeError, "assignment to const only known at run time in for-of loop", "Assignment to const");
  79. }
  80. },
  81. {
  82. name: "Referring to a let or const loop variable in the collection expression of a for-in or for-of loop is a use before declaration",
  83. body: function () {
  84. assert.throws(function () { for (let x in { a: x }) { } }, ReferenceError, "for-let-in register use before declaration", "Use before declaration");
  85. assert.throws(function () { for (const x in { a: x }) { } }, ReferenceError, "for-const-in register use before declaration", "Use before declaration");
  86. assert.throws(function () { for (let x of [ x ]) { } }, ReferenceError, "for-let-of register use before declaration", "Use before declaration");
  87. assert.throws(function () { for (const x of [ x ]) { } }, ReferenceError, "for-const-of register use before declaration", "Use before declaration");
  88. assert.throws(function () { for (let x in { a: (() => x)() }) { } }, ReferenceError, "for-let-in slot array use before declaration", "Use before declaration");
  89. assert.throws(function () { for (const x in { a: (() => x)() }) { } }, ReferenceError, "for-const-in slot array use before declaration", "Use before declaration");
  90. assert.throws(function () { for (let x of [ (() => x)() ]) { } }, ReferenceError, "for-let-of slot array use before declaration", "Use before declaration");
  91. assert.throws(function () { for (const x of [ (() => x)() ]) { } }, ReferenceError, "for-const-of slot array use before declaration", "Use before declaration");
  92. assert.throws(function () { for (let x in { a: eval("x") }) { } }, ReferenceError, "for-let-in activation object use before declaration", "Use before declaration");
  93. assert.throws(function () { for (const x in { a: eval("x") }) { } }, ReferenceError, "for-const-in activation object use before declaration", "Use before declaration");
  94. assert.throws(function () { for (let x of [ eval("x") ]) { } }, ReferenceError, "for-let-of activation object use before declaration", "Use before declaration");
  95. assert.throws(function () { for (const x of [ eval("x") ]) { } }, ReferenceError, "for-const-of activation object use before declaration", "Use before declaration");
  96. var rx0 = null, rx1 = null;
  97. var ry0 = null, ry1 = null;
  98. function registerVars() {
  99. for (var x in { a: rx0 = x }) {
  100. rx1 = x;
  101. }
  102. for (var y of [ ry0 = y ]) {
  103. ry1 = y;
  104. }
  105. }
  106. registerVars();
  107. assert.areEqual(undefined, rx0, "register var declaration in for-in loop can be referenced before initialization");
  108. assert.areEqual('a', rx1, "sanity check that the for-in loop runs as expected");
  109. assert.areEqual(undefined, ry0, "register var declaration in for-of loop can be referenced before initialization");
  110. assert.areEqual(undefined, ry1, "sanity check that the for-of loop runs as expected");
  111. var sax0 = null, sax1 = null;
  112. var say0 = null, say1 = null;
  113. function slotArrayVars() {
  114. for (var x in { a: sax0 = x }) {
  115. sax1 = (function () { return x; })();
  116. }
  117. for (var y of [ say0 = y ]) {
  118. say1 = (function () { return y; })();
  119. }
  120. }
  121. slotArrayVars();
  122. assert.areEqual(undefined, sax0, "slot array var declaration in for-in loop can be referenced before initialization");
  123. assert.areEqual('a', sax1, "sanity check that the for-in loop runs as expected");
  124. assert.areEqual(undefined, say0, "slot array var declaration in for-of loop can be referenced before initialization");
  125. assert.areEqual(undefined, say1, "sanity check that the for-of loop runs as expected");
  126. var aox0 = null, aox1 = null;
  127. var aoy0 = null, aoy1 = null;
  128. function activationObjectVars() {
  129. for (var x in { a: aox0 = x }) {
  130. aox1 = eval("x");
  131. }
  132. for (var y of [ aoy0 = y ]) {
  133. aoy1 = eval("y");
  134. }
  135. }
  136. activationObjectVars();
  137. assert.areEqual(undefined, aox0, "slot array var declaration in for-in loop can be referenced before initialization");
  138. assert.areEqual('a', aox1, "sanity check that the for-in loop runs as expected");
  139. assert.areEqual(undefined, aoy0, "slot array var declaration in for-of loop can be referenced before initialization");
  140. assert.areEqual(undefined, aoy1, "sanity check that the for-of loop runs as expected");
  141. }
  142. },
  143. {
  144. name: "Capturing a let or const loop variable in the collection expression of a for-in or for-of loop still leads to a use before declaration",
  145. body: function () {
  146. var a = [], b = [];
  147. var i = 0, j = 0;
  148. for (let x in { a: a[i++] = () => x }) { b[j++] = () => x; }
  149. for (const x in { a: a[i++] = () => x }) { b[j++] = () => x; }
  150. for (let x of [ a[i++] = () => x ]) { b[j++] = () => x; }
  151. for (const x of [ a[i++] = () => x ]) { b[j++] = () => x; }
  152. for (let x in { a: a[i++] = () => eval("x") }) { b[j++] = () => eval("x"); }
  153. for (const x in { a: a[i++] = () => eval("x") }) { b[j++] = () => eval("x"); }
  154. for (let x of [ a[i++] = () => eval("x") ]) { b[j++] = () => eval("x"); }
  155. for (const x of [ a[i++] = () => eval("x") ]) { b[j++] = () => eval("x"); }
  156. assert.throws(a[0], ReferenceError, "for-let-in slot array capture use before declaration", "Use before declaration");
  157. assert.throws(a[1], ReferenceError, "for-const-in slot array capture use before declaration", "Use before declaration");
  158. assert.throws(a[2], ReferenceError, "for-let-of slot array capture use before declaration", "Use before declaration");
  159. assert.throws(a[3], ReferenceError, "for-const-of slot array capture use before declaration", "Use before declaration");
  160. assert.throws(a[4], ReferenceError, "for-let-in activation object capture use before declaration", "Use before declaration");
  161. assert.throws(a[5], ReferenceError, "for-const-in activation object capture use before declaration", "Use before declaration");
  162. assert.throws(a[6], ReferenceError, "for-let-of activation object capture use before declaration", "Use before declaration");
  163. assert.throws(a[7], ReferenceError, "for-const-of activation object capture use before declaration", "Use before declaration");
  164. assert.areEqual('a', b[0](), "sanity check for-let-in slot array capture body still initialized", "Use before declaration");
  165. assert.areEqual('a', b[1](), "sanity check for-const-in slot array capture body still initialized", "Use before declaration");
  166. assert.areEqual(a[2], b[2](), "sanity check for-let-of slot array capture body still initialized", "Use before declaration");
  167. assert.areEqual(a[3], b[3](), "sanity check for-const-of slot array capture body still initialized", "Use before declaration");
  168. assert.areEqual('a', b[4](), "sanity check for-let-in activation object capture body still initialized", "Use before declaration");
  169. assert.areEqual('a', b[5](), "sanity check for-const-in activation object capture body still initialized", "Use before declaration");
  170. assert.areEqual(a[6], b[6](), "sanity check for-let-of activation object capture body still initialized", "Use before declaration");
  171. assert.areEqual(a[7], b[7](), "sanity check for-const-of activation object capture body still initialized", "Use before declaration");
  172. }
  173. },
  174. {
  175. name: "[slot array] for loops have per iteration bindings for let variables",
  176. body: function () {
  177. var a = [];
  178. var i = 0;
  179. for (let x = 0; x < 3; x += 1) {
  180. a[i++] = function () { return x; };
  181. }
  182. assert.areEqual(0, a[0](), "first for-let function captures x when it is equal to 0");
  183. assert.areEqual(1, a[1](), "second for-let function captures x when it is equal to 1");
  184. assert.areEqual(2, a[2](), "third for-let function captures x when it is equal to 2");
  185. }
  186. },
  187. {
  188. name: "[activation object] for loops have per iteration bindings for let variables",
  189. body: function () {
  190. var a = [];
  191. var i = 0;
  192. for (let x = 0; x < 3; x += 1) {
  193. a[i++] = function () { return eval("x"); };
  194. }
  195. assert.areEqual(0, a[0](), "first for-let function captures x when it is equal to 0");
  196. assert.areEqual(1, a[1](), "second for-let function captures x when it is equal to 1");
  197. assert.areEqual(2, a[2](), "third for-let function captures x when it is equal to 2");
  198. }
  199. },
  200. {
  201. name: "for loops allow const loop variables but cannot assign to them anywhere including the increment expression", // so they're kinda useless
  202. body: function () {
  203. assert.throws(function () { for (const x = 0; x++ < 3; ) { } }, TypeError, "assignment to const known at parse time in the test expression", "Assignment to const");
  204. assert.throws(function () { for (const x = 0; x < 3; x += 1) { } }, TypeError, "assignment to const known at parse time in the increment expression", "Assignment to const");
  205. assert.throws(function () { for (const x = 0; x < 3; ) { x += 1; } }, TypeError, "assignment to const known at parse time in the body", "Assignment to const");
  206. assert.throws(function () { eval("for (const x = 0; x++ < 3; ) { }"); }, TypeError, "assignment to const known at eval parse time in the test expression", "Assignment to const");
  207. assert.throws(function () { for (const x = 0; eval("x++") < 3; ) { } }, TypeError, "assignment to const known at run time in the test expression", "Assignment to const");
  208. assert.throws(function () { eval("for (const x = 0; x < 3; x += 1) { }"); }, TypeError, "assignment to const known at eval parse time in the increment expression", "Assignment to const");
  209. assert.throws(function () { for (const x = 0; x < 3; eval("x += 1")) { } }, TypeError, "assignment to const known at run time in the increment expression", "Assignment to const");
  210. assert.throws(function () { eval("for (const x = 0; x < 3; ) { x += 1; }"); }, TypeError, "assignment to const known at eval parse time in the body", "Assignment to const");
  211. assert.throws(function () { for (const x = 0; x < 3; ) { eval("x += 1"); } }, TypeError, "assignment to const known at run time in the body", "Assignment to const");
  212. }
  213. },
  214. {
  215. name: "for loops have separate binding for the initializer expression for let variables",
  216. body: function () {
  217. var a = [];
  218. for (let x = (a[0] = () => x, 0); x < 1; x += 1) {
  219. x += 1;
  220. a[1] = () => x;
  221. }
  222. assert.areEqual(0, a[0](), "x captured in the initializer expression is a binding scoped only to the initializer expression");
  223. assert.areEqual(1, a[1](), "x captured in the initializer expression is a binding scoped only to the initializer expression");
  224. }
  225. },
  226. {
  227. name: "for loop per iteration binding includes the test and increment expressions and ends before the increment expression",
  228. body: function () {
  229. var t = [], ti = 0;
  230. var b = [], bi = 0;
  231. var i = [], ii = 0;
  232. var s = [], si = 0;
  233. for (let x = 0;
  234. // test expression
  235. t[ti++] = () => x,
  236. s[si++] = y => x = y,
  237. x < 2;
  238. // increment expression
  239. i[ii++] = () => x,
  240. x += 1) {
  241. // body
  242. b[bi++] = () => x;
  243. }
  244. assert.areEqual(0, t[0](), "Initially first test function captures first loop binding with value 0");
  245. assert.areEqual(0, b[0](), "Initially first body function captures first loop binding with value 0");
  246. assert.areEqual(1, i[0](), "Initially first increment function captures second loop binding with value 1");
  247. s[0]('a');
  248. assert.areEqual('a', t[0](), "Now first test function returns the new value of the first loop binding of x, 'a'");
  249. assert.areEqual('a', b[0](), "Now first body function returns the new value of the first loop binding of x, 'a'");
  250. assert.areEqual(1, i[0](), "But first increment function still returns 1 because it is a separate binding");
  251. assert.areEqual(1, t[1](), "Initially second test function captures second loop binding with value 1");
  252. assert.areEqual(1, b[1](), "Initially second body function captures second loop binding with value 1");
  253. assert.areEqual(2, i[1](), "Initially second increment function captures third loop binding with value 2");
  254. s[1]('b');
  255. assert.areEqual('b', i[0](), "And now first increment function returns 'b'");
  256. assert.areEqual('b', t[1](), "Now second test function returns the new value of the second loop binding of x, 'b'");
  257. assert.areEqual('b', b[1](), "Now second body function returns the new value of the second loop binding of x, 'b'");
  258. assert.areEqual(2, i[1](), "But second increment function still returns 2 because it is a separate binding");
  259. assert.areEqual(2, t[2](), "Initially third test function captures third loop binding with value 2");
  260. assert.areEqual(undefined, b[2], "There is no third body function because the loop terminated");
  261. assert.areEqual(undefined, i[2], "There is no third increment function because the loop terminated");
  262. s[2]('c');
  263. assert.areEqual('b', i[0](), "And now second increment function returns 'c'");
  264. assert.areEqual('b', t[1](), "Now third test function returns the new value of the third loop binding of x, 'c'");
  265. }
  266. },
  267. {
  268. name: "Destructuring adds the possibility of more than one loop variable -- quick smoke test",
  269. body: function () {
  270. var a = [];
  271. var i = 0;
  272. for (let [ x, y, z ] in { abc: 0, def: 1 }) {
  273. a[i++] = [ () => x, () => y, () => z ];
  274. }
  275. for (const { x, y, z } of [ { x: 1, y: 2, z: 3 }, { x: 4, y: 5, z: 6 } ]) {
  276. a[i++] = [ () => x, () => y, () => z ];
  277. }
  278. for (let [ x, y, z ] = [ 0, 1, 2 ];
  279. x < 2;
  280. [ x, y, z ] = [ x + 1, y + 2, z + 3 ]) {
  281. a[i++] = [ () => x, () => y, () => z ];
  282. }
  283. assert.areEqual('a', a[0][0](), "for-let-in array destructuring first x gets 'a' from 'abc'");
  284. assert.areEqual('b', a[0][1](), "for-let-in array destructuring first y gets 'b' from 'abc'");
  285. assert.areEqual('c', a[0][2](), "for-let-in array destructuring first z gets 'c' from 'abc'");
  286. assert.areEqual('d', a[1][0](), "for-let-in array destructuring second x gets 'd' from 'def'");
  287. assert.areEqual('e', a[1][1](), "for-let-in array destructuring second y gets 'e' from 'def'");
  288. assert.areEqual('f', a[1][2](), "for-let-in array destructuring second z gets 'f' from 'def'");
  289. assert.areEqual(1, a[2][0](), "for-const-of object destructuring first x gets 1 from x");
  290. assert.areEqual(2, a[2][1](), "for-const-of object destructuring first y gets 2 from y");
  291. assert.areEqual(3, a[2][2](), "for-const-of object destructuring first z gets 3 from z");
  292. assert.areEqual(4, a[3][0](), "for-const-of object destructuring second x gets 4 from x");
  293. assert.areEqual(5, a[3][1](), "for-const-of object destructuring second y gets 5 from y");
  294. assert.areEqual(6, a[3][2](), "for-const-of object destructuring second z gets 6 from z");
  295. assert.areEqual(0, a[4][0](), "for-let array destructuring first x gets 0 from [ 0, 1, 2 ]");
  296. assert.areEqual(1, a[4][1](), "for-let array destructuring first y gets 1 from [ 0, 1, 2 ]");
  297. assert.areEqual(2, a[4][2](), "for-let array destructuring first z gets 2 from [ 0, 1, 2 ]");
  298. assert.areEqual(1, a[5][0](), "for-let array destructuring second x gets 4 from [ x + 1, y + 2, z + 3 ]");
  299. assert.areEqual(3, a[5][1](), "for-let array destructuring second y gets 5 from [ x + 1, y + 2, z + 3 ]");
  300. assert.areEqual(5, a[5][2](), "for-let array destructuring second z gets 6 from [ x + 1, y + 2, z + 3 ]");
  301. }
  302. },
  303. ];
  304. testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });