| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- // ES6 for/for-in/for-of loops per iteration loop variable bindings tests
- WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
- var tests = [
- {
- name: "[slot array] for-in and for-of loops have per iteration bindings for let and const variables",
- body: function () {
- var a = [];
- var i = 0;
- for (let x in { a: 1, b: 2, c: 3 }) {
- a[i++] = function () { return x; };
- }
- for (const x in { a: 1, b: 2, c: 3 }) {
- a[i++] = function () { return x; };
- }
- for (let x of [ 1, 2, 3 ]) {
- a[i++] = function () { return x; };
- }
- for (const x of [ 1, 2, 3 ]) {
- a[i++] = function () { return x; };
- }
- assert.areEqual('a', a[0](), "first for-let-in function captures x when it is equal to 'a'");
- assert.areEqual('b', a[1](), "second for-let-in function captures x when it is equal to 'b'");
- assert.areEqual('c', a[2](), "third for-let-in function captures x when it is equal to 'c'");
- assert.areEqual('a', a[3](), "first for-const-in function captures x when it is equal to 'a'");
- assert.areEqual('b', a[4](), "second for-const-in function captures x when it is equal to 'b'");
- assert.areEqual('c', a[5](), "third for-const-in function captures x when it is equal to 'c'");
- assert.areEqual(1, a[6](), "first for-let-of function captures x when it is equal to 1");
- assert.areEqual(2, a[7](), "second for-let-of function captures x when it is equal to 2");
- assert.areEqual(3, a[8](), "third for-let-of function captures x when it is equal to 3");
- assert.areEqual(1, a[9](), "first for-const-of function captures x when it is equal to 1");
- assert.areEqual(2, a[10](), "second for-const-of function captures x when it is equal to 2");
- assert.areEqual(3, a[11](), "third for-const-of function captures x when it is equal to 3");
- }
- },
- {
- name: "[activation object] for-in and for-of loops have per iteration bindings for let and const variables",
- body: function () {
- var a = [];
- var i = 0;
- for (let x in { a: 1, b: 2, c: 3 }) {
- a[i++] = function () { return eval("x"); };
- }
- for (const x in { a: 1, b: 2, c: 3 }) {
- a[i++] = function () { return eval("x"); };
- }
- for (let x of [ 1, 2, 3 ]) {
- a[i++] = function () { return eval("x"); };
- }
- for (const x of [ 1, 2, 3 ]) {
- a[i++] = function () { return eval("x"); };
- }
- assert.areEqual('a', a[0](), "first for-let-in function captures x when it is equal to 'a'");
- assert.areEqual('b', a[1](), "second for-let-in function captures x when it is equal to 'b'");
- assert.areEqual('c', a[2](), "third for-let-in function captures x when it is equal to 'c'");
- assert.areEqual('a', a[3](), "first for-const-in function captures x when it is equal to 'a'");
- assert.areEqual('b', a[4](), "second for-const-in function captures x when it is equal to 'b'");
- assert.areEqual('c', a[5](), "third for-const-in function captures x when it is equal to 'c'");
- assert.areEqual(1, a[6](), "first for-let-of function captures x when it is equal to 1");
- assert.areEqual(2, a[7](), "second for-let-of function captures x when it is equal to 2");
- assert.areEqual(3, a[8](), "third for-let-of function captures x when it is equal to 3");
- assert.areEqual(1, a[9](), "first for-const-of function captures x when it is equal to 1");
- assert.areEqual(2, a[10](), "second for-const-of function captures x when it is equal to 2");
- assert.areEqual(3, a[11](), "third for-const-of function captures x when it is equal to 3");
- }
- },
- {
- name: "const variables in for-in and for-of loops cannot be assigned",
- body: function () {
- 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");
- 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");
- 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");
- 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");
- 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");
- 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");
- }
- },
- {
- 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",
- body: function () {
- assert.throws(function () { for (let x in { a: x }) { } }, ReferenceError, "for-let-in register use before declaration", "Use before declaration");
- assert.throws(function () { for (const x in { a: x }) { } }, ReferenceError, "for-const-in register use before declaration", "Use before declaration");
- assert.throws(function () { for (let x of [ x ]) { } }, ReferenceError, "for-let-of register use before declaration", "Use before declaration");
- assert.throws(function () { for (const x of [ x ]) { } }, ReferenceError, "for-const-of register use before declaration", "Use before declaration");
- assert.throws(function () { for (let x in { a: (() => x)() }) { } }, ReferenceError, "for-let-in slot array use before declaration", "Use before declaration");
- assert.throws(function () { for (const x in { a: (() => x)() }) { } }, ReferenceError, "for-const-in slot array use before declaration", "Use before declaration");
- assert.throws(function () { for (let x of [ (() => x)() ]) { } }, ReferenceError, "for-let-of slot array use before declaration", "Use before declaration");
- assert.throws(function () { for (const x of [ (() => x)() ]) { } }, ReferenceError, "for-const-of slot array use before declaration", "Use before declaration");
- assert.throws(function () { for (let x in { a: eval("x") }) { } }, ReferenceError, "for-let-in activation object use before declaration", "Use before declaration");
- assert.throws(function () { for (const x in { a: eval("x") }) { } }, ReferenceError, "for-const-in activation object use before declaration", "Use before declaration");
- assert.throws(function () { for (let x of [ eval("x") ]) { } }, ReferenceError, "for-let-of activation object use before declaration", "Use before declaration");
- assert.throws(function () { for (const x of [ eval("x") ]) { } }, ReferenceError, "for-const-of activation object use before declaration", "Use before declaration");
- var rx0 = null, rx1 = null;
- var ry0 = null, ry1 = null;
- function registerVars() {
- for (var x in { a: rx0 = x }) {
- rx1 = x;
- }
- for (var y of [ ry0 = y ]) {
- ry1 = y;
- }
- }
- registerVars();
- assert.areEqual(undefined, rx0, "register var declaration in for-in loop can be referenced before initialization");
- assert.areEqual('a', rx1, "sanity check that the for-in loop runs as expected");
- assert.areEqual(undefined, ry0, "register var declaration in for-of loop can be referenced before initialization");
- assert.areEqual(undefined, ry1, "sanity check that the for-of loop runs as expected");
- var sax0 = null, sax1 = null;
- var say0 = null, say1 = null;
- function slotArrayVars() {
- for (var x in { a: sax0 = x }) {
- sax1 = (function () { return x; })();
- }
- for (var y of [ say0 = y ]) {
- say1 = (function () { return y; })();
- }
- }
- slotArrayVars();
- assert.areEqual(undefined, sax0, "slot array var declaration in for-in loop can be referenced before initialization");
- assert.areEqual('a', sax1, "sanity check that the for-in loop runs as expected");
- assert.areEqual(undefined, say0, "slot array var declaration in for-of loop can be referenced before initialization");
- assert.areEqual(undefined, say1, "sanity check that the for-of loop runs as expected");
- var aox0 = null, aox1 = null;
- var aoy0 = null, aoy1 = null;
- function activationObjectVars() {
- for (var x in { a: aox0 = x }) {
- aox1 = eval("x");
- }
- for (var y of [ aoy0 = y ]) {
- aoy1 = eval("y");
- }
- }
- activationObjectVars();
- assert.areEqual(undefined, aox0, "slot array var declaration in for-in loop can be referenced before initialization");
- assert.areEqual('a', aox1, "sanity check that the for-in loop runs as expected");
- assert.areEqual(undefined, aoy0, "slot array var declaration in for-of loop can be referenced before initialization");
- assert.areEqual(undefined, aoy1, "sanity check that the for-of loop runs as expected");
- }
- },
- {
- 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",
- body: function () {
- var a = [], b = [];
- var i = 0, j = 0;
- for (let x in { a: a[i++] = () => x }) { b[j++] = () => x; }
- for (const x in { a: a[i++] = () => x }) { b[j++] = () => x; }
- for (let x of [ a[i++] = () => x ]) { b[j++] = () => x; }
- for (const x of [ a[i++] = () => x ]) { b[j++] = () => x; }
- for (let x in { a: a[i++] = () => eval("x") }) { b[j++] = () => eval("x"); }
- for (const x in { a: a[i++] = () => eval("x") }) { b[j++] = () => eval("x"); }
- for (let x of [ a[i++] = () => eval("x") ]) { b[j++] = () => eval("x"); }
- for (const x of [ a[i++] = () => eval("x") ]) { b[j++] = () => eval("x"); }
- assert.throws(a[0], ReferenceError, "for-let-in slot array capture use before declaration", "Use before declaration");
- assert.throws(a[1], ReferenceError, "for-const-in slot array capture use before declaration", "Use before declaration");
- assert.throws(a[2], ReferenceError, "for-let-of slot array capture use before declaration", "Use before declaration");
- assert.throws(a[3], ReferenceError, "for-const-of slot array capture use before declaration", "Use before declaration");
- assert.throws(a[4], ReferenceError, "for-let-in activation object capture use before declaration", "Use before declaration");
- assert.throws(a[5], ReferenceError, "for-const-in activation object capture use before declaration", "Use before declaration");
- assert.throws(a[6], ReferenceError, "for-let-of activation object capture use before declaration", "Use before declaration");
- assert.throws(a[7], ReferenceError, "for-const-of activation object capture use before declaration", "Use before declaration");
- assert.areEqual('a', b[0](), "sanity check for-let-in slot array capture body still initialized", "Use before declaration");
- assert.areEqual('a', b[1](), "sanity check for-const-in slot array capture body still initialized", "Use before declaration");
- assert.areEqual(a[2], b[2](), "sanity check for-let-of slot array capture body still initialized", "Use before declaration");
- assert.areEqual(a[3], b[3](), "sanity check for-const-of slot array capture body still initialized", "Use before declaration");
- assert.areEqual('a', b[4](), "sanity check for-let-in activation object capture body still initialized", "Use before declaration");
- assert.areEqual('a', b[5](), "sanity check for-const-in activation object capture body still initialized", "Use before declaration");
- assert.areEqual(a[6], b[6](), "sanity check for-let-of activation object capture body still initialized", "Use before declaration");
- assert.areEqual(a[7], b[7](), "sanity check for-const-of activation object capture body still initialized", "Use before declaration");
- }
- },
- {
- name: "[slot array] for loops have per iteration bindings for let variables",
- body: function () {
- var a = [];
- var i = 0;
- for (let x = 0; x < 3; x += 1) {
- a[i++] = function () { return x; };
- }
- assert.areEqual(0, a[0](), "first for-let function captures x when it is equal to 0");
- assert.areEqual(1, a[1](), "second for-let function captures x when it is equal to 1");
- assert.areEqual(2, a[2](), "third for-let function captures x when it is equal to 2");
- }
- },
- {
- name: "[activation object] for loops have per iteration bindings for let variables",
- body: function () {
- var a = [];
- var i = 0;
- for (let x = 0; x < 3; x += 1) {
- a[i++] = function () { return eval("x"); };
- }
- assert.areEqual(0, a[0](), "first for-let function captures x when it is equal to 0");
- assert.areEqual(1, a[1](), "second for-let function captures x when it is equal to 1");
- assert.areEqual(2, a[2](), "third for-let function captures x when it is equal to 2");
- }
- },
- {
- name: "for loops allow const loop variables but cannot assign to them anywhere including the increment expression", // so they're kinda useless
- body: function () {
- assert.throws(function () { for (const x = 0; x++ < 3; ) { } }, TypeError, "assignment to const known at parse time in the test expression", "Assignment to const");
- 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");
- 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");
- 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");
- 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");
- 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");
- 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");
- 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");
- 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");
- }
- },
- {
- name: "for loops have separate binding for the initializer expression for let variables",
- body: function () {
- var a = [];
- for (let x = (a[0] = () => x, 0); x < 1; x += 1) {
- x += 1;
- a[1] = () => x;
- }
- assert.areEqual(0, a[0](), "x captured in the initializer expression is a binding scoped only to the initializer expression");
- assert.areEqual(1, a[1](), "x captured in the initializer expression is a binding scoped only to the initializer expression");
- }
- },
- {
- name: "for loop per iteration binding includes the test and increment expressions and ends before the increment expression",
- body: function () {
- var t = [], ti = 0;
- var b = [], bi = 0;
- var i = [], ii = 0;
- var s = [], si = 0;
- for (let x = 0;
- // test expression
- t[ti++] = () => x,
- s[si++] = y => x = y,
- x < 2;
- // increment expression
- i[ii++] = () => x,
- x += 1) {
- // body
- b[bi++] = () => x;
- }
- assert.areEqual(0, t[0](), "Initially first test function captures first loop binding with value 0");
- assert.areEqual(0, b[0](), "Initially first body function captures first loop binding with value 0");
- assert.areEqual(1, i[0](), "Initially first increment function captures second loop binding with value 1");
- s[0]('a');
- assert.areEqual('a', t[0](), "Now first test function returns the new value of the first loop binding of x, 'a'");
- assert.areEqual('a', b[0](), "Now first body function returns the new value of the first loop binding of x, 'a'");
- assert.areEqual(1, i[0](), "But first increment function still returns 1 because it is a separate binding");
- assert.areEqual(1, t[1](), "Initially second test function captures second loop binding with value 1");
- assert.areEqual(1, b[1](), "Initially second body function captures second loop binding with value 1");
- assert.areEqual(2, i[1](), "Initially second increment function captures third loop binding with value 2");
- s[1]('b');
- assert.areEqual('b', i[0](), "And now first increment function returns 'b'");
- assert.areEqual('b', t[1](), "Now second test function returns the new value of the second loop binding of x, 'b'");
- assert.areEqual('b', b[1](), "Now second body function returns the new value of the second loop binding of x, 'b'");
- assert.areEqual(2, i[1](), "But second increment function still returns 2 because it is a separate binding");
- assert.areEqual(2, t[2](), "Initially third test function captures third loop binding with value 2");
- assert.areEqual(undefined, b[2], "There is no third body function because the loop terminated");
- assert.areEqual(undefined, i[2], "There is no third increment function because the loop terminated");
- s[2]('c');
- assert.areEqual('b', i[0](), "And now second increment function returns 'c'");
- assert.areEqual('b', t[1](), "Now third test function returns the new value of the third loop binding of x, 'c'");
- }
- },
- {
- name: "Destructuring adds the possibility of more than one loop variable -- quick smoke test",
- body: function () {
- var a = [];
- var i = 0;
- for (let [ x, y, z ] in { abc: 0, def: 1 }) {
- a[i++] = [ () => x, () => y, () => z ];
- }
- for (const { x, y, z } of [ { x: 1, y: 2, z: 3 }, { x: 4, y: 5, z: 6 } ]) {
- a[i++] = [ () => x, () => y, () => z ];
- }
- for (let [ x, y, z ] = [ 0, 1, 2 ];
- x < 2;
- [ x, y, z ] = [ x + 1, y + 2, z + 3 ]) {
- a[i++] = [ () => x, () => y, () => z ];
- }
- assert.areEqual('a', a[0][0](), "for-let-in array destructuring first x gets 'a' from 'abc'");
- assert.areEqual('b', a[0][1](), "for-let-in array destructuring first y gets 'b' from 'abc'");
- assert.areEqual('c', a[0][2](), "for-let-in array destructuring first z gets 'c' from 'abc'");
- assert.areEqual('d', a[1][0](), "for-let-in array destructuring second x gets 'd' from 'def'");
- assert.areEqual('e', a[1][1](), "for-let-in array destructuring second y gets 'e' from 'def'");
- assert.areEqual('f', a[1][2](), "for-let-in array destructuring second z gets 'f' from 'def'");
- assert.areEqual(1, a[2][0](), "for-const-of object destructuring first x gets 1 from x");
- assert.areEqual(2, a[2][1](), "for-const-of object destructuring first y gets 2 from y");
- assert.areEqual(3, a[2][2](), "for-const-of object destructuring first z gets 3 from z");
- assert.areEqual(4, a[3][0](), "for-const-of object destructuring second x gets 4 from x");
- assert.areEqual(5, a[3][1](), "for-const-of object destructuring second y gets 5 from y");
- assert.areEqual(6, a[3][2](), "for-const-of object destructuring second z gets 6 from z");
- assert.areEqual(0, a[4][0](), "for-let array destructuring first x gets 0 from [ 0, 1, 2 ]");
- assert.areEqual(1, a[4][1](), "for-let array destructuring first y gets 1 from [ 0, 1, 2 ]");
- assert.areEqual(2, a[4][2](), "for-let array destructuring first z gets 2 from [ 0, 1, 2 ]");
- assert.areEqual(1, a[5][0](), "for-let array destructuring second x gets 4 from [ x + 1, y + 2, z + 3 ]");
- assert.areEqual(3, a[5][1](), "for-let array destructuring second y gets 5 from [ x + 1, y + 2, z + 3 ]");
- assert.areEqual(5, a[5][2](), "for-let array destructuring second z gets 6 from [ x + 1, y + 2, z + 3 ]");
- }
- },
- ];
- testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });
|