Program: CWE-119
Project: CVE-2017-0152
Buggy source:
//-------------------------------------------------------------------------------------------------------
// 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 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 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" }); 


Fixed version (ground truth):
//-------------------------------------------------------------------------------------------------------
// 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");

        function f13() {
            var a = function jnvgfg(sfgnmj = function ccunlk() { jnvgfg(undefined, 1); }, b) {
                if (b) {
                    assert.areEqual(undefined, jnvgfg, "This refers to the instance in the body and the value of the function expression is not copied over");
                }
                var jnvgfg = 10;
                if (!b) {
                    sfgnmj();
                    return 100;
                }
            };
            assert.areEqual(100, a(), "After the recursion the right value is returned by the split scoped function");
        };
        f13();

    } 
 }, 
 { 
    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 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 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" });