blob: 91c8dec301fe878dc37e9b9a0d226aee6432936c [file] [log] [blame]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
var tests = [
{
name: "Split parameter scope in function definition",
body: function () {
function f1(a = 10, b = function () { return a; }) {
assert.areEqual(10, a, "Initial value of parameter in the body scope should be the same as the one in param scope");
var a = 20;
assert.areEqual(20, a, "New assignment in the body scope updates the variable's value in body scope");
return b;
}
assert.areEqual(10, f1()(), "Function defined in the param scope captures the formals from the param scope not body scope");
function f2(a = 10, b = function () { return a; }, c = b() + a) {
assert.areEqual(10, a, "Initial value of parameter in the body scope should be the same as the one in param scope");
assert.areEqual(20, c, "Initial value of the third parameter in the body scope should be twice the value of the first parameter");
var a = 20;
assert.areEqual(20, a, "New assignment in the body scope updates the variable's value in body scope");
return b;
}
assert.areEqual(10, f2()(), "Function defined in the param scope captures the formals from the param scope not body scope");
function f3(a = 10, b = function () { return a; }) {
assert.areEqual(1, a, "Initial value of parameter in the body scope should be the same as the one passed in");
var a = 20;
assert.areEqual(20, a, "Assignment in the body scope updates the variable's value in body scope");
return b;
}
assert.areEqual(f3(1)(), 1, "Function defined in the param scope captures the formals from the param scope even when the default expression is not applied for that param");
(function (a = 10, b = a += 10, c = function () { return a + b; }) {
assert.areEqual(20, a, "Initial value of parameter in the body scope should be same as the corresponding symbol's final value in the param scope");
var a2 = 40;
(function () { assert.areEqual(40, a2, "Symbols defined in the body scope should be unaffected by the duplicate formal symbols"); })();
assert.areEqual(40, c(), "Function defined in param scope uses the formals from param scope even when executed inside the body");
})();
(function (a = 10, b = function () { assert.areEqual(10, a, "Function defined in the param scope captures the formals from the param scope when executed from the param scope"); }, c = b()) {
})();
function f4(a = 10, b = function () { return a; }) {
a = 20;
return b;
}
assert.areEqual(10, f4()(), "Even if the formals are not redeclared in the function body the symbol in the param scope and body scope are different");
function f5(a = 10, b = function () { return function () { return a; }; }) {
var a = 20;
return b;
}
assert.areEqual(10, f5()()(), "Parameter scope works fine with nested functions");
var a1 = 10;
function f6(a, b = function () { a; return a1; }) {
assert.areEqual(undefined, a1, "Inside the function body the assignment hasn't happened yet");
var a1 = 20;
assert.areEqual(20, a1, "Assignment to the symbol inside the function changes the value");
return b;
}
assert.areEqual(10, f6()(), "Function in the param scope correctly binds to the outer variable");
function f7(a = 10, b = { iFnc () { return a; } }) {
a = 20;
return b;
}
assert.areEqual(10, f7().iFnc(), "Function definition inside the object literal should capture the formal from the param scope");
var f8 = function (a, b = ((function() { assert.areEqual('string1', a, "First arguemnt receives the right value"); })(), 1), c) {
var d = 'string3';
(function () { assert.areEqual('string3', d, "Var declaration in the body is initialized properly"); })();
return c;
};
assert.areEqual('string2', f8('string1', undefined, 'string2'), "Function returns the third argument properly");
function f9() {
var f10 = function (a = function () { c; }, b, c) {
assert.areEqual(1, c, "Third argument is properly populated");
arguments;
function f11() {};
};
f10(undefined, undefined, 1);
}
f9();
f9();
function f12() {
var result = ((a = (w = a => a * a) => w) => a)()()(10);
assert.areEqual(100, result, "The inner lambda function properly maps to the right symbol for a");
};
f12();
}
},
{
name: "Split parameter scope and function expressions with name",
body: function () {
function f1(a = 10, b = function c() { return a; }) {
assert.areEqual(10, a, "Initial value of parameter in the body scope of the method should be the same as the one in param scope");
var a = 20;
assert.areEqual(20, a, "New assignment in the body scope of the method updates the variable's value in body scope");
return b;
}
assert.areEqual(10, f1()(), "Function expression defined in the param scope captures the formals from the param scope not body scope");
function f2(a = 10, b = function c(recurse = true) { return recurse ? c(false) : a; }) {
return b;
}
assert.areEqual(10, f2()(), "Recursive function expression defined in the param scope captures the formals from the param scope not body scope");
assert.areEqual(10, f2()(), "Recursive function expression defined in the param scope captures the formals from the param scope not body scope");
var f3 = function f4 (a = function ( ) { b; return f4(20); }, b) {
if (a == 20) {
return 10;
}
return a;
}
assert.areEqual(10, f3()(), "Recursive call to the function from the param scope returns the right value");
var f5 = function f6 (a = function ( ) { b; return f6; }, b) {
if (a == 20) {
return 10;
}
return a;
}
assert.areEqual(10, f5()()(20), "Recursive call to the function from the param scope returns the right value");
var f7 = function f8 (a = function ( ) { b; }, b) {
if (a == 20) {
return 10;
}
var a = function () { return f8(20); };
return a;
}
assert.areEqual(10, f7()(), "Recursive call to the function from the body scope returns the right value");
var f9 = function f10 (a = function ( ) { b; return f10(20); }, b) {
eval("");
if (a == 20) {
return 10;
}
return a;
}
assert.areEqual(10, f9()(), "Recursive call to the function from the param scope returns the right value when eval is there in the body");
var f11 = function f12 (a = function ( ) { b; }, b) {
eval("");
if (a == 20) {
return 10;
}
var a = function () { return f12(20); };
return a;
}
assert.areEqual(10, f11()(), "Recursive call to the function from the body scope returns the right value when eval is there in the body");
}
},
{
name: "Split parameter scope in member functions",
body: function () {
var o1 = {
f(a = 10, b = function () { return a; }) {
assert.areEqual(10, a, "Initial value of parameter in the body scope of the method should be the same as the one in param scope");
var a = 20;
assert.areEqual(20, a, "New assignment in the body scope of the method updates the variable's value in body scope");
return b;
}
}
assert.areEqual(o1.f()(), 10, "Function defined in the param scope of the object method captures the formals from the param scope not body scope");
var o2 = {
f1(a = 10, b = function () { return { f2 () { return a; } } }) {
var a = 20;
c = function () { return { f2 () { return a; } } };
return [b, c];
}
}
var result = o2.f1();
assert.areEqual(10, result[0]().f2(), "Short hand method defined in the param scope of the object method captures the formals from the param scope not body scope");
assert.areEqual(20, result[1]().f2(), "Short hand method defined in the param scope of the object method captures the formals from the param scope not body scope");
}
},
{
name: "Arrow functions in split param scope",
body: function () {
function f1(a = 10, b = () => { return a; }) {
assert.areEqual(10, a, "Initial value of parameter in the body scope should be the same as the one in param scope");
var a = 20;
assert.areEqual(20, a, "New assignment in the body scope updates the variable's value in body scope");
return b;
}
assert.areEqual(10, f1()(), "Arrow functions defined in the param scope captures the formals from the param scope not body scope");
function f2(a = 10, b = () => { return a; }) {
assert.areEqual(1, a, "Initial value of parameter in the body scope should be the same as the one in param scope");
var a = 20;
assert.areEqual(20, a, "New assignment in the body scope updates the variable's value in body scope");
return b;
}
assert.areEqual(1, f2(1)(), "Arrow functions defined in the param scope captures the formals from the param scope not body scope even when value is passed");
function f3(a = 10, b = () => a) {
assert.areEqual(10, a, "Initial value of parameter in the body scope should be the same as the one in param scope");
var a = 20;
assert.areEqual(20, a, "New assignment in the body scope updates the variable's value in body scope");
return b;
}
assert.areEqual(10, f3()(), "Arrow functions with concise body defined in the param scope captures the formals from the param scope not body scope");
((a = 10, b = a += 10, c = () => { assert.areEqual(20, a, "Value of the first formal inside the lambda should be same as the default value"); return a + b; }, d = c() * 10) => {
assert.areEqual(d, 400, "Initial value of the formal parameter inside the body should be the same as final value from the param scope");
})();
function f4(a = 10, b = () => { return () => a; }) {
a = 20;
return b;
}
assert.areEqual(10, f4()()(), "Nested lambda should capture the formal param value from the param scope");
assert.throws(function f4(a = () => x) { var x = 1; a(); }, ReferenceError, "Lambdas in param scope shouldn't be able to access the variables from body", "'x' is undefined");
assert.throws(function f5() { (function (a = () => x) { var x = 1; return a; })()(); }, ReferenceError, "Lambdas in param scope shouldn't be able to access the variables from body", "'x' is undefined");
assert.throws((a = () => 10, b = a() + c, c = 10) => {}, ReferenceError, "Formals defined to the right shouldn't be usable in lambdas", "Use before declaration");
}
},
{
name: "Split parameter scope with Rest",
body: function () {
var arr = [2, 3, 4];
function f1(a = 10, b = function () { return a; }, ...c) {
assert.areEqual(arr.length, c.length, "Rest parameter should contain the same number of elements as the spread arg");
for (i = 0; i < arr.length; i++) {
assert.areEqual(arr[i], c[i], "Elements in the rest and the spread should be in the same order");
}
return b;
}
assert.areEqual(f1(undefined, undefined, ...arr)(), 10, "Presence of rest parameter shouldn't affect the binding");
((a = 10, b = () => a, ...c) => {
assert.areEqual(arr.length, c.length, "Rest parameter should contain the same number of elements as the spread arg");
for (i = 0; i < arr.length; i++) {
assert.areEqual(arr[i], c[i], "Elements in the rest and the spread should be in the same order");
}
return b;
})(undefined, undefined, ...arr);
}
},
{
name: "Split parameter scope with this",
body: function () {
function f1(a = this.x, b = function() { assert.areEqual(100, this.x, "this object for the function in param scope is passed from the final call site"); return a; }) {
assert.areEqual(10, this.x, "this objects property retains the value from param scope");
a = 20;
return b;
}
assert.areEqual(10, f1.call({x : 10}).call({x : 100}), "Arrow functions defined in the param scope captures the formals from the param scope not body scope");
(function (a = this.x, b = function() {this.x = 20; return a;}) {
assert.areEqual(10, this.x, "this objects property retains the value in param scope before the inner function call");
b.call(this);
assert.areEqual(20, this.x, "Update to a this's property from the param scope is reflected in the body scope");
}).call({x : 10});
this.x = 10;
((a = this.x, b = function() { a; this.x = 20; }) => {
assert.areEqual(10, this.x, "this objects property retains the value in param scope before the inner function call in lambda");
b.call(this);
assert.areEqual(20, this.x, "Update to a this's property from the param scope of lambda function is reflected in the body scope");
})();
function f2(a = function() { return this.x; }, b = this.y, c = a.call({x : 20}) + b) {
assert.areEqual(undefined, this.x, "this object remains unaffected");
return c;
}
assert.areEqual(30, f2.call({y : 10}), "Properties are accessed from the right this object");
var thisObj = {x : 1, y : 20 };
function f3(a, b = () => { a; this.x = 10; return this.y; }) {
assert.areEqual(1, this.x, "Assignment from the param scope has not happened yet");
assert.areEqual(20, this.y, "y property of the this object is not affected");
return b;
}
assert.areEqual(20, f3.call(thisObj)(), "Lambda defined in the param scope returns the right property value from thisObj");
assert.areEqual(10, thisObj.x, "Assignment from the param scope method updates thisObj's property");
function f4(a, b = () => { a; return this; }) {
return b;
}
assert.areEqual(thisObj, f4.call(thisObj)(), "Lambda defined in the param scope returns the right this object");
var thisObj = { x : 1 };
function f5() {
return (a = this, b = function() { return a; }) => b;
}
assert.areEqual(thisObj, f5.call(thisObj)()(), "This object is returned properly from the inner lambda method's child function");
function f6(a, b = function () { return a; }) {
return (a = this, b = function() { return a; }) => b;
}
assert.areEqual(thisObj, f6.call(thisObj)()(), "This object is returned properly from the inner lambda defnied inside a split scoped function");
function f7(a, b = function () { return a; }) {
function f8() {
return (a = this, b = function() { return a; }) => b;
}
return f8.call(this);
}
assert.areEqual(thisObj, f7.call(thisObj)()(), "This object is returned properly from the inner lambda defnied inside a nested split scoped function");
function f9(a, b = function () { return a; }) {
function f10(c, d = function () { c; }) {
return (a = this, b = function() { return a; }) => b;
}
return f10.call(this);
}
assert.areEqual(thisObj, f9.call(thisObj)()(), "This object is returned properly from the inner lambda defnied inside a double nested split scoped function");
function f11(a = this.x * 10, b = () => { a; return this; }) {
assert.areEqual(10, a, "this should be accessible in the parameter scope");
assert.areEqual(thisObj, this, "Body scope should get the right value for this object");
assert.isTrue(eval("thisObj == this"), "Eval should be able to access the this object properly");
return b;
}
assert.areEqual(thisObj, f11.call(thisObj)(), "Lambda defined in the param scope returns the right this object");
function f12(a = this.x * 10, b = () => { a; return this; }) {
var c = 100;
assert.areEqual(10, a, "this should be accessible in the parameter scope");
assert.areEqual(thisObj, this, "Body scope should get the right value for this object");
assert.isTrue(eval("thisObj == this"), "Eval should be able to access the this object properly");
assert.areEqual(thisObj, (() => this)(), "Lambda should capture the this object from body properly");
assert.areEqual(100, c, "Body variable should be unaffected by the slot allocation of this object");
return b;
}
assert.areEqual(thisObj, f12.call(thisObj)(), "Lambda defined in the param scope returns the right this object");
function f13(a = 10, b = () => { a; return this; }) {
var c = 100;
assert.areEqual(thisObj, this, "Body scope should get the right value for this object");
var d = () => this;
this.x = 5;
assert.isTrue(eval("this.x == 5"), "Eval should be able to access the this object properly after the field is updated");
assert.isTrue(eval("d().x == 5"), "Lambda should capture the this symbol from the body properly");
assert.isTrue(eval("a == 10"), "Eval should be able to access the first parameter properly");
assert.isTrue(eval("b().x == 5"), "Lambda from the param scope should capture the this symbol properly");
assert.isTrue(eval("d().x == 5"), "Lambda should capture the this symbol from the body properly");
return b;
}
assert.areEqual(5, f13.call(thisObj)().x, "Lambda defined in the param scope returns the same this object as the one in body");
}
},
{
name: "Split parameter scope and class",
body: function () {
class c {
f(a = 10, d, b = function () { return a; }, c) {
assert.areEqual(10, a, "Initial value of parameter in the body scope in class method should be the same as the one in param scope");
var a = 20;
assert.areEqual(20, a, "Assignment in the class method body updates the value of the variable");
return b;
}
}
assert.areEqual(10, (new c()).f()(), "Method defined in the param scope of the class should capture the formal from the param scope itself");
function f1(a = 10, d, b = class { method1() { return a; } }, c) {
var a = 20;
assert.areEqual(10, (new b()).method1(), "Class method defined within the param scope should capture the formal from the param scope");
return b;
}
var result = f1();
assert.areEqual(10, (new result()).method1(), "Methods defined in a class defined in param scope should capture the formals form that param scope itself");
class c2 {
f1(a = 10, d, b = function () { a = this.f2(); return a; }, c) {
assert.areEqual(30, this.f2(), "this object in the body points to the right this object");
return b;
};
f2() {
return 30;
}
}
var f2Obj = new c2();
assert.areEqual(100, f2Obj.f1().call({f2() { return 100; }}), "Method defined in the param uses its own this object while updating the formal");
function f2(a = 10, d, b = class { method1() { return class { method2() { return a; }} } }, c) {
a = 20;
return b;
}
var obj1 = f2();
var obj2 = (new obj1()).method1();
assert.areEqual(10, (new obj2()).method2(), "Nested class definition in the param scope should capture the formals from the param scope");
var actualArray = [2, 3, 4];
class c3 {
f(a = 10, b = () => { return c; }, ...c) {
assert.areEqual(actualArray.length, c.length, "Rest param and the actual array should have the same length");
for (var i = 0; i < c.length; i++) {
assert.areEqual(actualArray[i], c[i], "Rest parameter should have the same value as the actual array");
}
c = [];
return b;
}
}
result = (new c3()).f(undefined, undefined, ...[2, 3, 4])();
assert.areEqual(actualArray.length, result.length, "The result and the actual array should have the same length");
for (var i = 0; i < result.length; i++) {
assert.areEqual(actualArray[i], result[i], "The result array should have the same value as the actual array");
}
class c4 {
f({x:x = 10, y:y = () => { return x; }}) {
assert.areEqual(10, x, "Initial value of destructure parameter in the body scope in class method should be the same as the one in param scope");
x = 20;
assert.areEqual(20, x, "Assignment in the class method body updates the value of the variable");
return y;
}
}
assert.areEqual(10, (new c4()).f({})(), "The method defined as the default destructured value of the parameter should capture the formal from the param scope");
function f3(a = 10, d, b = (function () { return a; }, class { method1() { return a; } }), c) {
var a = 20;
assert.areEqual(10, (new b()).method1(), "Class method defined within the param scope should capture the formal from the param scope");
return b;
}
result = f3();
assert.areEqual(10, (new result()).method1(), "Methods defined in a class defined, after another function definition, in the param scope should capture the formals form that param scope itself");
function f4(a = 10, d, b = (function () { return a; }, class {}, class { method1() { return a; } }), c) {
var a = 20;
return b;
}
result = f4();
assert.areEqual(10, (new result()).method1(), "Methods defined in a class defined, after another class definition, in the param scope should capture the formals form that param scope itself");
function f5(a = 10, d, b = (function () { return a; }, class {}, function () {}, class { method1() { return a; } }), c) {
var a = 20;
return b;
}
result = f5();
assert.areEqual(10, (new result()).method1(), "Methods defined in a class defined, after a function and class, in the param scope should capture the formals form that param scope itself");
function f6(a = 10, d, b = (function () { return a; }, class {}, function (a, b = () => a) {}, class { method1() { return a; } }), c) {
var a = 20;
return b;
}
result = f6();
assert.areEqual(10, (new result()).method1(), "Methods defined in a class defined, after a split scope function, in the param scope should capture the formals form that param scope itself");
function f7(a = 10, d, b = (function () { return a; }, class c1 { method1() { return a; } }), c) {
var a = 20;
assert.areEqual(10, (new b()).method1(), "Class method defined within the param scope should capture the formal from the param scope");
return b;
}
result = f7();
assert.areEqual(10, (new result()).method1(), "Methods defined in a class with name defined, after another function definition, in the param scope should capture the formals form that param scope itself");
function f8(a = 10, d, b = class c1 { method1() { return a; } }, c = (function () { return a; }, class c2 extends b { method2() { return a * a; } })) {
var a = 20;
assert.areEqual(10, (new b()).method1(), "Class method defined within the param scope should capture the formal from the param scope");
return c;
}
result = f8();
assert.areEqual(10, (new result()).method1(), "Methods defined in a class extending another class defined, after another function definition, in the param scope should capture the formals form that param scope itself");
assert.areEqual(100, (new result()).method2(), "Method in the derived class returns the right value");
}
},
{
name: "Split parameter scope in generator methods",
body: function () {
function *f1(a = 10, d, b = function () { return a; }, c) {
yield a;
var a = 20;
yield a;
yield b;
}
var f1Obj = f1();
assert.areEqual(10, f1Obj.next().value, "Initial value of the parameter in the body scope should be the same as the final value of the parameter in param scope");
assert.areEqual(20, f1Obj.next().value, "Assignment in the body scope updates the variable's value");
assert.areEqual(10, f1Obj.next().value(), "Function defined in the param scope captures the formal from the param scope itself");
function *f2(a = 10, d, b = function () { return a; }, c) {
yield a;
a = 20;
yield a;
yield b;
}
var f2Obj = f2();
assert.areEqual(10, f2Obj.next().value, "Initial value of the parameter in the body scope should be the same as the final value of the parameter in param scope");
assert.areEqual(20, f2Obj.next().value, "Assignment in the body scope updates the variable's value");
assert.areEqual(10, f2Obj.next().value(), "Function defined in the param scope captures the formal from the param scope itself even if it is not redeclared in the body");
function *f3(a = 10, d, b = function *() { yield a + c; }, c = 100) {
a = 20;
yield a;
yield b;
}
var f3Obj = f3();
assert.areEqual(20, f3Obj.next().value, "Assignment in the body scope updates the variable's value");
assert.areEqual(110, f3Obj.next().value().next().value, "Function defined in the param scope captures the formals from the param scope");
function *f4(a = 10, d, b = function *() { yield a; }, c) {
var a = 20;
yield function *() { yield a; };
yield b;
}
var f4Obj = f4();
assert.areEqual(20, f4Obj.next().value().next().value, "Generator defined inside the body captures the symbol from the body scope");
assert.areEqual(10, f4Obj.next().value().next().value, "Function defined in the param scope captures the formal from param scope even if it is captured in the body scope");
}
},
{
name: "Split parameter scope with destructuring",
body: function () {
function f1( {a:a1, b:b1}, c = function() { return a1 + b1; } ) {
assert.areEqual(10, a1, "Initial value of the first destructuring parameter in the body scope should be the same as the one in param scope");
assert.areEqual(20, b1, "Initial value of the second destructuring parameter in the body scope should be the same as the one in param scope");
a1 = 1;
b1 = 2;
assert.areEqual(1, a1, "New assignment in the body scope updates the first formal's value in body scope");
assert.areEqual(2, b1, "New assignment in the body scope updates the second formal's value in body scope");
assert.areEqual(30, c(), "The param scope method should return the sum of the destructured formals from the param scope");
return c;
}
assert.areEqual(30, f1({ a : 10, b : 20 })(), "Returned method should return the sum of the destructured formals from the param scope");
function f2({x:x = 10, y:y = function () { return x; }}) {
assert.areEqual(10, x, "Initial value of the first destructuring parameter in the body scope should be the same as the one in param scope");
x = 20;
assert.areEqual(20, x, "Assignment in the body updates the formal's value");
return y;
}
assert.areEqual(10, f2({ })(), "Returned method should return the value of the destructured formal from the param scope");
function f3({y:y = function () { return x; }, x:x = 10}) {
assert.areEqual(10, x, "Initial value of the first destructuring parameter in the body scope should be the same as the one in param scope");
x = 20;
assert.areEqual(20, x, "Assignment in the body updates the formal's value");
return y;
}
assert.areEqual(10, f3({ })(), "Returned method should return the value of the destructured formal from the param scope even if declared after");
(({x:x = 10, y:y = function () { return x; }}) => {
assert.areEqual(10, x, "Initial value of the first destructuring parameter in the body scope should be the same as the one in param scope");
x = 20;
assert.areEqual(10, y(), "Assignment in the body does not affect the formal captured from the param scope");
})({});
}
},
{
name: "Nested split scopes",
body: function () {
function f1(a = 10, b = function () { return a; }, c) {
function iFnc(d = 100, e = 200, pf1 = function () { return d + e; }) {
d = 1000;
e = 2000;
pf2 = function () { return d + e; };
return [pf1, pf2];
}
return [b].concat(iFnc());
}
var result = f1();
assert.areEqual(10, result[0](), "Function defined in the param scope of the outer function should capture the symbols from its own param scope");
assert.areEqual(300, result[1](), "Function defined in the param scope of the inner function should capture the symbols from its own param scope");
assert.areEqual(3000, result[2](), "Function defined in the body scope of the inner function should capture the symbols from its body scope");
function f2(a = 10, b = function () { return a; }, c) {
a = 1000;
c = 2000;
function iFnc(a = 100, b = function () { return a + c; }, c = 200) {
a = 1000;
c = 2000;
return b;
}
return [b, iFnc()];
}
result = f2();
assert.areEqual(10, result[0](), "Function defined in the param scope of the outer function should capture the symbols from its own param scope even if formals are with the same name in inner function");
assert.areEqual(300, result[1](), "Function defined in the param scope of the inner function should capture the symbols from its own param scope if formals are with the same name in the outer function");
function f3(a = 10, b = function () { return a; }, c) {
a = 1000;
c = 2000;
function iFnc(a = 100, b = function () { return a + c; }, c = 200) {
a = 1000;
c = 2000;
return b;
}
return [b, iFnc];
}
assert.areEqual(300, f3()[1]()(), "Function defined in the param scope of the inner function should capture the right formals even if the inner function is executed outside");
function f4(a = 10, b = function () { return a; }, c) {
a = 1000;
function iFnc(a = 100, b = function () { return a + c; }, c = 200) {
a = 1000;
c = 2000;
return b;
}
return [b, iFnc(undefined, b, c)];
}
result = f4(1, undefined, 3);
assert.areEqual(1, result[0](), "Function defined in the param scope of the outer function correctly captures the passed in value for the formal");
assert.areEqual(1, result[1](), "Function defined in the param scope of the inner function is replaced by the function definition from the param scope of the outer function");
function f5(a = 10, b = function () { return a; }, c) {
function iFnc(a = 100, b = function () { return a + c; }, c = 200) {
a = 1000;
c = 2000;
return b;
}
return [b, iFnc(a, undefined, c)];
}
result = f5(1, undefined, 3);
assert.areEqual(1, result[0](), "Function defined in the param scope of the outer function correctly captures the passed in value for the formal");
assert.areEqual(4, result[1](), "Function defined in the param scope of the inner function captures the passed values for the formals");
function f6(a , b, c) {
function iFnc(a = 1, b = function () { return a + c; }, c = 2) {
a = 10;
c = 20;
return b;
}
return iFnc;
}
assert.areEqual(3, f6()()(), "Function defined in the param scope captures the formals when defined inside another method without split scope");
function f7(a = 10 , b = 20, c = function () { return a + b; }) {
return (function () {
function iFnc(a = 100, b = function () { return a + c; }, c = 200) {
a = 1000;
c = 2000;
return b;
}
return [c, iFnc];
})();
}
result = f7();
assert.areEqual(30, result[0](), "Function defined in the param scope of the outer function should capture the symbols from its own param scope even in nested case");
assert.areEqual(300, result[1]()(), "Function defined in the param scope of the inner function should capture the symbols from its own param scope even when nested inside a normal method and a split scope");
function f8(a = 1, b = function (d = 10, e = function () { return a + d; }) { assert.areEqual(d, 10, "Split scope function defined in param scope should capture the right formal value"); d = 20; return e; }, c) {
a = 2;
return b;
}
assert.areEqual(11, f8()()(), "Split scope function defined within the param scope should capture the formals from the corresponding param scopes");
function f9(a = 1, b = function () { return function (d = 10, e = function () { return a + d; }) { d = 20; return e; } }, c) {
a = 2;
return b;
}
assert.areEqual(11, f9()()()(), "Split scope function defined within the param scope should capture the formals from the corresponding param scope in nested scope");
}
},
{
name: "Split scope with symbol shadowing",
body: function () {
function f1(a = 10, b = function () { return a; }) {
assert.areEqual(100, a(), "Function definition inside the body is hoisted");
function a () {
return 100;
}
return b;
}
assert.areEqual(10, f1()(), "Function definition in the param scope captures the symbol from the param scope");
function f2(a = 10, b = function () { return a; }, c = b) {
a = 20;
assert.areEqual(20, b(), "Function definition in the body scope captures the body symbol");
function b() {
return a;
}
return [c, b];
}
var result = f2();
assert.areEqual(10, result[0](), "Function definition in the param scope captures the param scope symbol");
assert.areEqual(20, result[1](), "Function definition in the body captures the body symbol");
var g = 1;
function f3(a = 10, b = function () { a; return g;}) {
assert.areEqual(10, g(), "Function definition inside the body is unaffected by the outer variable");
function g() {
return 10;
}
return b;
}
assert.areEqual(1, f3()(), "Function definition in the param scope captures the outer scoped var");
function f4(a = x1, b = function g() {
a;
return function h() {
assert.areEqual(10, x1, "x1 is captured from the outer scope");
};
}) {
var x1 = 100;
b()();
};
var x1 = 10;
f4();
var x2 = 1;
function f5(a = x2, b = function() { a; return x2; }) {
{
function x2() {
}
}
var x2 = 2;
return b;
}
assert.areEqual(1, f5()(), "Symbol capture at the param scope is unaffected by the inner definitions");
var x3 = 1;
function f6(a = x3, b = function(_x) { a; return x3; }) {
var x3 = 2;
return b;
}
assert.areEqual(1, f6()(), "Symbol capture at the param scope is unaffected by other references in the body and param");
}
},
{
name : "Split scope and arguments symbol",
body : function () {
assert.throws(function () { eval("function f(a = arguments, b = () => a) { }"); }, SyntaxError, "Use of arguments symbol is not allowed in non-simple parameter list with split scope", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured");
assert.throws(function () { eval("function f1() { function f2(a = arguments, b = () => a) { } }"); }, SyntaxError, "Use of arguments symbol is not allowed in non-simple parameter list with split scope inside another function", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured");
assert.throws(function () { eval("function f(a = arguments, b = () => a, c = eval('')) { }"); }, SyntaxError, "Use of arguments symbol is not allowed in non-simple parameter list with eval", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured");
assert.throws(function () { eval("function f(a = arguments = [1, 2], b = () => a) { }"); }, SyntaxError, "Use of arguments symbol is not allowed in non-simple parameter list with split scope", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured");
assert.throws(function () { eval("function f(a = 10, b = () => a, c = arguments) { }"); }, SyntaxError, "Use of arguments symbol is not allowed in non-simple parameter list with split scope", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured");
assert.throws(function () { eval("function f(a = 10, b = () => a, c = a = arguments) { }"); }, SyntaxError, "Use of arguments symbol is not allowed in non-simple parameter list with split scope", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured");
assert.throws(function () { eval("function f(a, b = () => { a; arguments}) { }"); }, SyntaxError, "Use of arguments symbol is not allowed in non-simple parameter list when captured in lambda method", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured");
assert.throws(function () { eval("function f(a = 10, b = (c = arguments) => a) { }"); }, SyntaxError, "Use of arguments symbol is not allowed in non-simple parameter list when captured in a lambda in split scope", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured");
assert.throws(function () { eval("function f(a, b = () => a, c = () => { return arguments; }) { }"); }, SyntaxError, "Use of arguments symbol is not allowed in non-simple parameter list in split scope when captured by a lambda method", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured");
assert.throws(function () { eval("function f(a = 10, b = () => a, c = () => () => arguments) { }"); }, SyntaxError, "Use of arguments symbol is not allowed in non-simple parameter list in split scope when captured by nested lambda", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured");
assert.throws(function () { eval("function f3(a, arguments = function () { return a; } ) { }"); }, SyntaxError, "Use of arguments as a parameter name is not allowed in non-simple parameter list in split scope when captured by nested lambda", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured");
assert.throws(function () { eval("function f3({a, arguments = function () { return a; }}) { }"); }, SyntaxError, "Use of arguments as a parameter name is not allowed in destructuring parameter list in split scope when captured by nested lambda", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured");
assert.throws(function () { eval("function f3({a = arguments}, b = function () { return a; } ) { }"); }, SyntaxError, "Use of arguments is not allowed in destructuring parameter list in split scope when captured by nested lambda", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured");
function f1(a, b = () => a) {
eval("");
b = () => { return arguments; };
assert.areEqual(1, arguments[0], "Arguments object receives the first parameter properly");
assert.areEqual(1, b()[0], "First argument receives the right value passed in");
assert.areEqual(undefined, b()[1], "Second argument receives the right value passed in");
assert.areEqual(2, arguments.length, "Arguments should have only two elements in it");
}
f1(1, undefined);
function f2(a, b = () => { return a; }) {
a = 10;
assert.areEqual(1, arguments[0], "First argument is properly received");
assert.areEqual(2, arguments[2], "Third argument is properly received");
assert.areEqual(3, arguments.length, "Only three arguments are passed in");
(() => { arguments = [3, 4]; a; })();
assert.areEqual(3, arguments[0], "Arguments symbol is updated with the new value when the lambda is executed");
assert.areEqual(4, arguments[1], "New array is properly assigned to arguments symbol");
assert.areEqual(2, arguments.length, "New array has only elements");
return b;
}
assert.areEqual(1, f2(1, undefined, 2)(), "Param scope method properly captures the first parameter");
function f3(a, b = () => { return a; }) {
eval("");
a = 10;
assert.areEqual(1, arguments[0], "First argument is properly received");
assert.areEqual(2, arguments[2], "Third argument is properly received");
assert.areEqual(3, arguments.length, "Only three arguments are passed in");
(() => { arguments = [3, 4]; a; })();
assert.areEqual(3, arguments[0], "Arguments symbol is updated with the new value when the lambda is executed");
assert.areEqual(4, arguments[1], "New array is properly assigned to arguments symbol");
assert.areEqual(2, arguments.length, "New array has only elements");
return b;
}
assert.areEqual(1, f3(1, undefined, 2)(), "Param scope method properly captures the first parameter, with eval in the body");
function f4(a, b = function () { a; } ) {
var c = 10;
assert.areEqual(1, arguments[0], "Arguments symbol properly receives the passed in values");
eval("");
}
f4(1);
function f5(a, b = function () { a; } ) {
var c = 10;
assert.areEqual(1, arguments[0], "Arguments symbol properly receives the passed in values");
arguments = 100;
assert.areEqual(100, arguments, "Arguments is updated after the assignment");
eval("");
}
f5(1);
function f6(a, b = function () { a; } ) {
assert.areEqual(1, arguments[0], "Arguments symbol properly receives the passed in values");
arguments = 100;
assert.areEqual(100, arguments, "Arguments is updated after the assignment");
}
f6(1);
function f7(a, b = function () { a; } ) {
assert.areEqual(5, arguments(), "Function definition is hoisted");
function arguments() { return 5; }
}
f7(1);
function f8(a, b = function () { a; } ) {
assert.areEqual(5, arguments(), "Function definition is hoisted");
function arguments() { return 5; }
eval("");
}
f8(1);
function f9(a, b = function () { a; } ) {
assert.areEqual(1, eval("a"), "Eval should be able to access the first argument properly");
assert.areEqual(1, eval("arguments[0]"), "Eval should be able to access the first argument properly from arguments object");
assert.areEqual(1, arguments[0], "Arguments symbol properly receives the passed in values");
arguments = 100;
assert.areEqual(100, arguments, "Arguments is updated after the assignment");
assert.areEqual(100, eval("arguments"), "Updated value of arguments is visible in eval");
assert.areEqual(1, eval("a"), "First argument remains unchanged after the arguments are updated");
}
f9(1);
function f10(a, b = function () { a; } ) {
assert.areEqual(1, arguments[0], "Arguments symbol properly receives the passed in values");
var arguments = 100;
assert.areEqual(100, arguments, "Arguments is updated after the assignment");
}
f10(1);
function f11(a, b = function () { a; } ) {
assert.areEqual(1, arguments[0], "Arguments symbol properly receives the passed in values");
var arguments = 100;
assert.areEqual(100, arguments, "Arguments is updated after the assignment");
eval("");
}
f11(1);
function f12(a, b = function () { a; } ) {
assert.areEqual(1, arguments[0], "Arguments symbol properly receives the passed in values");
b = () => arguments;
assert.areEqual(1, b()[0], "Lambda captures the right arguments symbol");
var arguments = 100;
assert.areEqual(100, arguments, "Arguments is updated after the assignment");
assert.areEqual(100, b(), "Lambda now gives the updated value");
eval("");
}
f12(1);
function f13(a, b = () => { return a; }) {
a = 10;
assert.areEqual(1, arguments[0], "First argument is properly received");
assert.areEqual(2, arguments[2], "Third argument is properly received");
assert.areEqual(3, arguments.length, "Only three arguments are passed in");
((c = arguments = [3, 4]) => { a; })();
assert.areEqual(3, arguments[0], "Arguments symbol is updated with the new value when the lambda is executed");
assert.areEqual(4, arguments[1], "New array is properly assigned to arguments symbol");
assert.areEqual(2, arguments.length, "New array has only elements");
return b;
}
assert.areEqual(1, f13(1, undefined, 2)(), "Param scope method properly captures the first parameter");
function f14(a, b = () => { return a; }) {
eval("");
a = 10;
assert.areEqual(1, arguments[0], "First argument is properly received");
assert.areEqual(2, arguments[2], "Third argument is properly received");
assert.areEqual(3, arguments.length, "Only three arguments are passed in");
((c = arguments = [3, 4]) => { a; })();
assert.areEqual(3, arguments[0], "Arguments symbol is updated with the new value when the lambda is executed");
assert.areEqual(4, arguments[1], "New array is properly assigned to arguments symbol");
assert.areEqual(2, arguments.length, "New array has only elements");
return b;
}
assert.areEqual(1, f14(1, undefined, 2)(), "Param scope method properly captures the first parameter, with eval in the body");
function f15(a, b = function () { a; }, ...c) {
assert.areEqual(1, arguments[0], "Checking first argument");
assert.areEqual(undefined, arguments[1], "Checking second argument");
assert.areEqual(2, arguments[2], "Checking third argument");
assert.areEqual(3, arguments[3], "Checking fourth argument");
assert.areEqual([2, 3], c, "Rest argument should get the trailing parameters properly");
var arguments = 100;
assert.areEqual(100, arguments, "Arguments is updated after the assignment");
assert.areEqual([2, 3], c, "Rest should remain unaffected when arguments is updated");
eval("");
}
f15(1, undefined, 2, 3);
var f16 = function f17(a, b = function () { a; }, ...c) {
if (a === 1) {
assert.areEqual(1, arguments[0], "Checking first argument");
assert.areEqual(undefined, arguments[1], "Checking second argument");
assert.areEqual(2, arguments[2], "Checking third argument");
assert.areEqual(3, arguments[3], "Checking fourth argument");
assert.areEqual([2, 3], c, "Rest argument should get the trailing parameters properly");
return f17(undefined, undefined, ...c);
} else {
assert.areEqual(undefined, arguments[0], "Checking first argument on the recursive call");
assert.areEqual(undefined, arguments[1], "Checking second argument on the recursive call");
assert.areEqual(2, arguments[2], "Checking third argument on the recursive call");
assert.areEqual(3, arguments[3], "Checking fourth argument on the recursive call");
assert.areEqual([2, 3], c, "Rest argument should get the trailing parameters properly");
var arguments = 100;
assert.areEqual(100, arguments, "Arguments is updated after the assignment");
assert.areEqual([2, 3], c, "Rest should remain unaffected when arguments is updated");
return eval("c");
}
}
assert.areEqual([2, 3], f16(1, undefined, 2, 3), "Rest should remain unaffected when arguments is updated");
}
},
{
name: "Split scope and super call",
body: function () {
class c1 {
constructor() {
return { x : 1 };
}
};
class c2 extends c1 {
constructor(a = 1, b = () => { assert.areEqual(1, super().x, "Super is accessible in the param scope"); return a; }) {
var c = 10;
a = 20;
(() => assert.areEqual(10, c, "Allocation of scope slot for super property shouldn't affect the body variables"))();
assert.areEqual(1, b(), "Function defined in the param scope should capture the formal");
return {};
}
}
new c2();
class c3 extends c1 {
constructor(a = 1, b = () => { return a; }) {
(() => assert.areEqual(1, super().x, "Lambda should be able to access the super method properly in the body"))();
a = 10;
assert.areEqual(1, b(), "Function defined in the param scope should capture the formal");
}
}
new c3();
class c4 extends c1 {
constructor(a = 1, b = () => { return a; }) {
var c = 10;
(() => assert.areEqual(10, c, "Allocation of scope slot for super property shouldn't affect the body variables"))();
assert.areEqual(1, b(), "Function defined in the param scope should capture the formal");
assert.areEqual(1, eval("super().x"), "Eval should be able to access the super property properly");
}
}
new c4();
class c5 extends c1 {
constructor(a = super().x, b = () => { return a; }) {
assert.areEqual(1, a, "First formal calls the super from the param scope");
var c = 10;
(() => assert.areEqual(10, c, "Allocation of scope slot for super property shouldn't affect the body variables"))();
assert.areEqual(1, b(), "Function defined in the param scope should capture the formal");
}
}
new c5();
}
},
{
name: "Split scope and super property",
body: function () {
class c1 {
foo () {
return 1;
}
};
class c2 extends c1 {
foo(a = 1, b = () => { assert.areEqual(1, super.foo(), "Super property access works fine from a lambda defined in the param scope"); return a; }) {
a = 20;
var c = 10;
(() => assert.areEqual(10, c, "Allocation of scope slot for super property shouldn't affect the body variables"))();
assert.areEqual(1, b(), "Function defined in the param scope should capture the formal");
}
}
(new c2()).foo();
class c3 extends c1 {
foo(a = 1, b = () => { return a; }) {
var c = 10;
a = 20;
(() => assert.areEqual(1, super.foo(), "Super property access works fine from a lambda defined in the body scope"))();
assert.areEqual(1, b(), "Function defined in the param scope should capture the formal");
}
}
(new c3()).foo();
class c4 extends c1 {
foo(a = 1, b = () => { return a; }) {
var c = 10;
a = 20;
(() => assert.areEqual(10, c, "Allocation of scope slot for super property shouldn't affect the body variables"))();
assert.areEqual(1, b(), "Function defined in the param scope should capture the formal");
assert.areEqual(1, eval("super.foo()"), "Eval should be able to access the super property properly from the body scope");
}
}
(new c4()).foo();
class c5 extends c1 {
foo(a = super.foo(), b = () => { return a; }) {
assert.areEqual(1, a, "First formal uses the super property from the param scope");
var c = 10;
(() => assert.areEqual(10, c, "Allocation of scope slot for super property shouldn't affect the body variables"))();
a = 20;
assert.areEqual(1, b(), "Function defined in the param scope should capture the formal");
}
}
(new c5()).foo();
}
},
{
name: "Split scope and new.target",
body: function () {
class c1 {
constructor(newTarget) {
assert.isTrue(newTarget == new.target, "Base class should receive the right value for new.target");
}
};
class c2 extends c1 {
constructor(a = 1, b = () => { assert.isTrue(new.target == c2, "new.target should have the derived class value in the param scope"); return a; }) {
super(c2);
var c = 10;
a = 20;
(() => assert.areEqual(10, c, "Allocation of scope slot for super property shouldn't affect the body variables"))();
assert.areEqual(1, b(), "Function defined in the param scope should capture the formal");
}
}
new c2();
class c3 extends c1 {
constructor(a = 1, b = () => { return a; }) {
super(c3);
var c = 10;
(() => assert.isTrue(new.target == c3, "new.target should be the derived class in the body scope when captured by lambda"))();
assert.isTrue(new.target == c3, "new.target should be the derived class in the body scope");
}
}
new c3();
class c4 extends c1 {
constructor(a = 1, b = () => { return a; }) {
super(c4);
assert.isTrue(eval("new.target == c4"), "new.target should be the derived class inside eval");
assert.isTrue(new.target == c4, "new.target should be the derived class in the body scope");
}
}
new c4();
class c5 extends c1 {
constructor(a = new.target, b = () => { return a; }) {
super(c5);
assert.isTrue(a == c5, "new.target accessed from the param scope should work fine");
}
}
new c5();
}
},
{
name: "Split parameter scope and eval",
body: function () {
function g() {
return 3 * 3;
}
function f1(h = () => eval("g()")) {
assert.areEqual(6, g(), "Right method is called in the body scope");
function g() {
return 2 * 3;
}
return h();
}
assert.areEqual(9, f1(), "Paramater scope remains split");
function f2(h = () => eval("g()")) {
assert.areEqual(6, eval("g()"), "Right method is called in the body scope");
function g() {
return 2 * 3;
}
return h();
}
assert.areEqual(9, f2(), "Paramater scope remains split");
}
},
{
name: "Split parameter scope with eval in body",
body: function () {
function f1(a = 10, b = function () { return a; }) {
assert.areEqual(10, a, "Initial value of parameter in the body scope should be the same as the one in param scope");
assert.areEqual(10, eval('a'), "Initial value of parameter in the body scope in eval should be the same as the one in param scope");
var a = 20;
assert.areEqual(20, a, "New assignment in the body scope updates the variable's value in body scope");
assert.areEqual(20, eval('a'), "New assignment in the body scope updates the variable's value when evaluated through eval in body scope");
return b;
}
assert.areEqual(10, f1()(), "Function defined in the param scope captures the formals from the param scope not body scope with eval");
function f2(a = 10, b = function () { return a; }) {
assert.areEqual(10, eval('b()'), "Eval of the function from param scope should return the right value for the formal");
var a = 20;
assert.areEqual(10, eval('b()'), "Eval of the function from param scope should return the right value for the formal even after assignment to the corresponding body symbol");
return b;
}
assert.areEqual(10, f2()(), "Function defined in the param scope captures the formals from the param scope not body scope with eval");
function f3(a = 10, b = function () { return a; }) {
assert.areEqual(100, eval('b()'), "Eval of the function from body scope should return the right value for the formal");
var a = 20;
function b () { return a * a; }
assert.areEqual(400, eval('b()'), "Eval of the function from body scope should return the right value after assignment to the corresponding body symbol");
return b;
}
assert.areEqual(400, f3()(), "Function defined in the body scope captures the symbol from the body scope with eval");
function f4 (a, b, c = function () { b; }, d = 1) {
var e = 10;
assert.areEqual(2, arguments[0], "Unmapped arguments value has the expected value in the body");
(function () {
eval('');
}());
};
f4.call(1, 2);
}
},
{
name: "Split scope and with",
body: function () {
function f1(a, b, c = function () { a; }) {
with ({}) {
var d = function () {
return 10;
};
assert.areEqual(10, d(), "With inside a split scope function should work fine");
}
}
f1();
function f2(a, b, c = function () { a; }) {
var d = function () {
return 10;
};
with ({}) {
assert.areEqual(10, d(), "With inside a split scope function should be able to access the function definition from the body");
}
}
f2();
function f3(a, b = function () { return 10; }, c = function () { a; }) {
with ({}) {
assert.areEqual(10, b(), "With inside a split scope function should be able to access the function definition from the param scope");
}
}
f3();
function f4(a, b = function () { return 10; }, c = function () { a; }) {
var d = {
e : function () { return 10; }
};
e = function () { return 100; };
with (d) {
assert.areEqual(10, e(), "With should use the function definition inside the object not the one from body");
}
}
f4();
function f5(a, b = { d : function () { return 10; } }, c = function () { a; }) {
var d = { };
with (b) {
assert.areEqual(10, d(), "With should use the function definition inside the object from the param scope not the one from body");
}
}
f5();
var v6 = 100
function f6(a, b, c = function () { a; }, e = function () { with({}) { assert.areEqual(100, v6, "With inside param scope should be able to access var from outside"); } }, f = e()) {
var v6 = { };
}
f6();
function f7(a, b, c = function () { a; }) {
with ({}) {
assert.areEqual(100, v6, "With inside body scope should be able to access var from outside");
}
}
f7();
function f8() {
function f9() {
return 1;
}
var v1 = 10;
function f10(a = 10, b = function f11() {
a;
assert.areEqual(10, v1, "Function in the param scope should be able to access the outside variable");
with ({}) {
assert.areEqual(1, f9(), "With construct inside a param scoped function should be able to execute functions from outside");
}
}) {
b();
};
f10();
}
f8();
f8();
function f12() {
function f13() {
return 1;
}
var v2 = 100;
function f14(a = 10, b = function () {
assert.areEqual(10, a, "Function in the param scope should be able to access the formal from parent");
return function () {
assert.areEqual(10, a, "Function nested in the param scope should be able to access the formal from the split scoped function");
assert.areEqual(100, v2, "Function in the param scope should be able to access the outside variable");
with ({}) {
assert.areEqual(1, f13(), "With construct inside a param scoped function should be able to execute functions from outside");
}
};
}) {
b()();
};
f14();
}
f12();
f12();
}
},
{
name: "Basic eval in parameter scope",
body: function () {
assert.areEqual(1,
function (a = eval("1")) { return a; }(),
"Eval with static constant works in parameter scope");
{
let b = 2;
assert.areEqual(2,
function (a = eval("b")) { return a; }(),
"Eval with parent var reference works in parameter scope");
}
assert.areEqual(1,
function (a, b = eval("arguments[0]")) { return b; }(1),
"Eval with arguments reference works in parameter scope");
function testSelf(a = eval("testSelf(1)")) {
return a;
}
assert.areEqual(1, testSelf(1), "Eval with reference to the current function works in parameter scope");
var testSelfExpr = function (a = eval("testSelfExpr(1)")) {
return a;
}
assert.areEqual(1, testSelfExpr(), "Eval with reference to the current function expression works in parameter scope");
{
let a = 1, b = 2, c = 3;
function testEvalRef(a = eval("a"), b = eval("b"), c = eval("c")) {
return [a, b, c];
}
assert.throws(function () { testEvalRef(); },
ReferenceError,
"Eval with reference to the current formal throws",
"Use before declaration");
function testEvalRef2(x = eval("a"), y = eval("b"), z = eval("c")) {
return [x, y, z];
}
assert.areEqual([1, 2, 3], testEvalRef2(), "Eval with references works in parameter scope");
}
function f1(a = 10, b = () => eval("a")) {
assert.areEqual(10, eval("a"), "In the body initial value of the symbol should be same as the final value from param scope");
a = 20;
assert.areEqual(20, eval("a"), "In the body after assignment the symbol value is updated");
assert.areEqual(10, b(), "Eval in the param scope captures the symbol from the param scope");
}
f1();
function f2(a = 10, b = () => eval("a")) {
a = 20;
assert.areEqual(10, b(), "Eval in the param scope captures the symbol from the param scope even when there is no eval in the body");
}
f2();
function f3(a = 10, b = function () { return eval("a"); }) {
a = 20;
assert.areEqual(10, b(), "Eval in the param scope captures the symbol from the param scope even when there is no eval in the body");
}
f3();
function f4(a = 10, b = () => eval("a"), c = a = 30) {
assert.areEqual(30, eval("a"), "In the body initial value of the symbol should be same as the final value from param scope");
a = 20;
assert.areEqual(20, eval("a"), "In the body after assignment the symbol value is updated");
assert.areEqual(30, b(), "Eval in the param scope captures the symbol from the param scope");
}
f4();
function f5(a = 10, b = () => eval("a")) {
assert.areEqual(30, eval("a"), "In the body initial value of the symbol should be same as the final value from param scope");
var a = 20;
assert.areEqual(20, eval("a"), "In the body after assignment the symbol value is updated");
assert.areEqual(30, b(), "Eval in the param scope captures the symbol from the param scope");
}
f5(30);
}
},
{
name: "Eval declarations in parameter scope",
body: function() {
// Redeclarations of formals - var
assert.throws(function () { return function (a = eval("var a = 2"), b = a) { return [a, b]; }() },
ReferenceError,
"Redeclaring the current formal using var inside an eval throws",
"Let/Const redeclaration");
assert.doesNotThrow(function () { "use strict"; return function (a = eval("var a = 2"), b = a) { return [a, b]; }() },
"Redeclaring the current formal using var inside a strict mode eval does not throw");
assert.doesNotThrow(function () { "use strict"; return function (a = eval("var a = 2"), b = a) { return [a, b]; }() },
"Redeclaring the current formal using var inside a strict mode function eval does not throw");
assert.throws(function () { function foo(a = eval("var b"), b, c = b) { return [a, b, c]; } foo(); },
ReferenceError,
"Redeclaring a future formal using var inside an eval throws",
"Let/Const redeclaration");
assert.throws(function () { function foo(a, b = eval("var a"), c = a) { return [a, b, c]; } foo(); },
ReferenceError,
"Redeclaring a previous formal using var inside an eval throws",
"Let/Const redeclaration");
// Let and const do not leak outside of an eval, so the test cases below should never throw.
// Redeclarations of formals - let
assert.doesNotThrow(function (a = eval("let a")) { return a; },
"Attempting to redeclare the current formal using let inside an eval does not leak");
assert.doesNotThrow(function (a = eval("let b"), b) { return [a, b]; },
"Attempting to redeclare a future formal using let inside an eval does not leak");
assert.doesNotThrow(function (a, b = eval("let a")) { return [a, b]; },
"Attempting to redeclare a previous formal using let inside an eval does not leak");
// Redeclarations of formals - const
assert.doesNotThrow(function (a = eval("const a = 1")) { return a; },
"Attempting to redeclare the current formal using const inside an eval does not leak");
assert.doesNotThrow(function (a = eval("const b = 1"), b) { return [a, b]; },
"Attempting to redeclare a future formal using const inside an eval does not leak");
assert.doesNotThrow(function (a, b = eval("const a = 1")) { return [a, b]; },
"Attempting to redeclare a previous formal using const inside an eval does not leak");
// Conditional declarations
function test(x = eval("var a1 = 1; let b1 = 2; const c1 = 3;")) {
// none should be visible
assert.throws(function () { a1 }, ReferenceError, "Ignoring the default value does not result in an eval declaration leaking", "'a1' is undefined");
assert.throws(function () { b1 }, ReferenceError, "Let declarations do not leak out of eval to parameter scope", "'b1' is undefined");
assert.throws(function () { c1 }, ReferenceError, "Const declarations do not leak out of eval to parameter scope when x is ", "'c1' is undefined");
}
test();
// Redefining locals
function foo(a = eval("var x = 1; assert.areEqual(1, x, 'Variable declared inside eval is accessible within eval');")) {
assert.areEqual(undefined, x, "Var declaration from eval is not visible in the body");
var x = 10;
assert.areEqual(10, x, "Var declaration from eval uses its new value in the body declaration");
}
assert.doesNotThrow(function() { foo(); }, "Redefining a local var with an eval var does not throw");
// Function bodies defined in eval
function funcArrow(a = eval("() => 1"), b = a) { function a() { return 10; }; return [a(), b()]; }
assert.areEqual([10,1], funcArrow(), "Defining an arrow function body inside an eval works at default parameter scope");
function funcDecl(a = eval("(function foo() { return 1; })"), b = a()) { return [a(), b]; }
assert.areEqual([1, 1], funcDecl(), "Defining a function inside an eval works at default parameter scope");
function funcDecl(a = eval("function foo() { return 1; }; foo"), b = a()) { return [a(), b]; }
assert.areEqual([1, 1], funcDecl(), "Defining a function inside an eval works at default parameter scope");
function genFuncDecl(a = eval("(function *foo() { yield 1; return 2; })"), b = a(), c = b.next()) { return [c, b.next()]; }
assert.areEqual([{value : 1, done : false}, {value : 2, done : true}], genFuncDecl(), "Declaring a generator function inside an eval works at default parameter scope");
function funcExpr(a = eval("f = function foo() { return 1; }"), b = f()) { return [a(), b, f()]; }
assert.areEqual([1, 1, 1], funcExpr(), "Declaring a function inside an eval works at default parameter scope");
assert.throws(function () { eval("function foo(a = eval('b'), b) {}; foo();"); }, ReferenceError, "Future default references using eval are not allowed", "Use before declaration");
}
},
];
testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });