Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| //------------------------------------------------------------------------------------------------------- | |
| // 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 overriding", | |
| 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 f(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 f({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 f({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"); | |
| assert.throws(function () { eval("function f(a = () => arguments) { }"); }, SyntaxError, "Arguments cannot be captured in the param 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 = () => arguments[0]) { }"); }, SyntaxError, "Arguments cannot be captured in the param 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 = 1, b = () => arguments[0]) { }"); }, SyntaxError, "Arguments cannot be captured in the param scope at any position", "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[0] + b, b = 10) { }"); }, SyntaxError, "Arguments cannot be captured in the param scope at any position", "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) { var arguments }"); }, SyntaxError, "Arguments cannot be captured in the param scope even when duplicate definition occurs in the body", "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) { function arguments() { } }"); }, SyntaxError, "Arguments cannot be captured in the param scope even when duplicate definition occurs in the body", "Use of 'arguments' in non-simple parameter list is not supported when one of the formals is captured"); | |
| assert.throws(function () { eval("function f(arguments, b = () => arguments) { }"); }, SyntaxError, "Arguments cannot be captured in the param scope even if it is a formal shadowing the actual arguments", "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) { }"); }, SyntaxError, "Arguments cannot be used as a formal name when one of the formal is captured", "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 = () => arguments}) { }"); }, SyntaxError, "Arguments cannot be used as a formal name when one of the formal is captured", "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"); | |
| function f18(a, b = function arguments(c) { | |
| if (!c) { | |
| return arguments.callee(a, 10, 20); | |
| } | |
| return arguments; | |
| }) { | |
| assert.areEqual(10, b()[1], "Function defined in the param scope can be called recursively"); | |
| assert.areEqual(1, arguments[0], "Arguments symbol is unaffected by the function expression"); | |
| } | |
| f18(1); | |
| function f19(a, b = arguments) { | |
| var c = function arguments(c) { | |
| if (!arguments.length) { | |
| return arguments.callee(a, 10, 20, 30); | |
| } | |
| return arguments; | |
| } | |
| assert.areEqual(30, c()[3], "In the function body the arguments function expression with name is not visible"); | |
| assert.areEqual(1, b[0], "In the param scope arguments symbol referes to the passed in values"); | |
| } | |
| f19(1, undefined, 2, 3, 4); | |
| function f20(a, b = function arguments(c) { | |
| if (!c) { | |
| return arguments.callee(a, 10, 20); | |
| } | |
| return eval("arguments"); | |
| }) { | |
| assert.areEqual(1, b()[0], "Function defined in the param scope can be called recursively when eval occurs in its body"); | |
| assert.areEqual(1, arguments[0], "Arguments symbol is unaffected by the function expression"); | |
| } | |
| f20(1); | |
| function f21(a, b = arguments) { | |
| var c = function arguments(c) { | |
| if (!arguments.length) { | |
| return arguments.callee(a, 10, 20, 30); | |
| } | |
| return arguments; | |
| } | |
| assert.areEqual(30, c()[3], "In the function body the arguments function expression with name is not visible when eval is there in the body"); | |
| assert.areEqual(3, eval("b[3]"), "In the param scope arguments symbol referes to the passed in values"); | |
| } | |
| f21(1, undefined, 2, 3, 4); | |
| function f22(a, b = () => a) { | |
| assert.areEqual(1, arguments[0], "Function in block causes a var declaration to be hoisted and the initial value should be same as the arguments symbol"); | |
| { | |
| { | |
| function arguments() { | |
| return 10; | |
| } | |
| } | |
| } | |
| assert.areEqual(1, b(), "Function defined in the param scope should be able to capture the formal even when arguments in overwritten the body"); | |
| assert.areEqual(10, arguments(), "Hoisted var binding is updated after the block is exected"); | |
| } | |
| f22(1); | |
| function f23(a, b = () => a) { | |
| function f16() { | |
| eval(""); | |
| this.arguments = 1; | |
| } | |
| a = 10; | |
| var obj = new f16(); | |
| function arguments() { | |
| return 10; | |
| } | |
| assert.areEqual(1, obj.arguments, "Inner function with eval should add the property named arguments when duplicate arguments definition occurs in the parent body"); | |
| assert.areEqual(1, b(), "Formal captured from the param scope should be constrained to the param scope"); | |
| }; | |
| f23(1); | |
| } | |
| }, | |
| { | |
| 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 f(h = () => eval("g()")) { // cannot combine scopes | |
| function g() { | |
| return 2 * 3; | |
| } | |
| return h(); // 9 | |
| } | |
| // TODO(tcare): Re-enable when split scope support is implemented | |
| //assert.areEqual(9, f(), "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"); | |
| } | |
| } | |
| }, | |
| { | |
| 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;")) { | |
| if (x === undefined) { | |
| // only a should be visible | |
| assert.areEqual(1, a1, "Var declarations leak out of eval into parameter scope"); | |
| } else { | |
| // 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(); | |
| test(123); | |
| // Redefining locals | |
| function foo(a = eval("var x = 1;")) { | |
| assert.areEqual(1, x, "Shadowed parameter scope var declaration retains its original value before the body declaration"); | |
| var x = 10; | |
| assert.areEqual(10, x, "Shadowed parameter scope var declaration uses its new value in the body declaration"); | |
| } | |
| assert.doesNotThrow(function() { foo(); }, "Redefining a local var with an eval var does not throw"); | |
| // TODO(aneeshdk): Will revisit this when enabling the eval cases | |
| // assert.throws(function() { return function(a = eval("var x = 1;")) { let x = 2; }; }, ReferenceError, "Redefining a local let declaration with a parameter scope eval var declaration leak throws", "Let/Const redeclaration"); | |
| // assert.throws(function() { return (function(a = eval("var x = 1;")) { const x = 2; })(); }, ReferenceError, "Redefining a local const declaration with a parameter scope eval var declaration leak throws", "Let/Const redeclaration"); | |
| // Function bodies defined in eval | |
| function funcArrow(a = eval("() => 1"), b = a()) { return [a(), b]; } | |
| assert.areEqual([1,1], funcArrow(), "Defining an arrow function body inside an eval works at default parameter scope"); | |
| function funcDecl(a = eval("function foo() { return 1; }"), b = foo()) { return [a, b, foo()]; } | |
| assert.areEqual([undefined, 1, 1], funcDecl(), "Declaring a function inside an eval works at default parameter scope"); | |
| function genFuncDecl(a = eval("function * foo() { return 1; }"), b = foo().next().value) { return [a, b, foo().next().value]; } | |
| assert.areEqual([undefined, 1, 1], genFuncDecl(), "Declaring a generator function inside an eval works at default parameter scope"); | |
| function funcExpr(a = eval("var f = function () { return 1; }"), b = f()) { return [a, b, f()]; } | |
| assert.areEqual([undefined, 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" }); |