text:
// ES6 super chain tests

WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");

class SimpleParent {
    constructor() {
        this.foo = 'SimpleParent';
    }
}

let calls_to_ConstructorCountingParent = 0;
class ConstructorCountingParent {
    constructor() {
        calls_to_ConstructorCountingParent++;
    }
}

class UninitializedThisReturningArgumentConstructor extends SimpleParent {
    constructor(arg) {
        return arg;
    }
};

class InitializedThisReturningArgumentConstructor extends SimpleParent {
    constructor(arg) {
        super();
        return arg;
    }
};

var tests = [
    {
        name: "Simple derived class constructor using this",
        body: function () {
            class DerivedClassUsingThis extends SimpleParent {
                constructor() {
                    super();
                    this.bar = "DerivedClassUsingThis";
                }
            };

            let result = new DerivedClassUsingThis();

            assert.areEqual("DerivedClassUsingThis", result.bar, "This is initialized with the return value from super()");
            assert.areEqual("SimpleParent", result.foo, "Parent class returned the object from super to derived constructor");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof DerivedClassUsingThis, "Result object is instanceof derived class");
        }
    },
    {
        name: "Simple derived class constructor using this without calling super causing use before declaration error",
        body: function () {
            class DerivedClassUsingThisIllegally extends SimpleParent {
                constructor() {
                    this.bar = "DerivedClassUsingThisIllegally";
                }
            };

            assert.throws(function() { new DerivedClassUsingThisIllegally(); }, ReferenceError, "It's a ReferenceError to access 'this' without calling super", "Use before declaration");
        }
    },
    {
        name: "Simple derived class constructor using this before calling super causing use before declaration error",
        body: function () {
            class DerivedClassUsingThisIllegally extends SimpleParent {
                constructor() {
                    this.bar = "DerivedClassUsingThisIllegally";
                    super();
                }
            };

            assert.throws(function() { new DerivedClassUsingThisIllegally(); }, ReferenceError, "It's a ReferenceError to access 'this' without calling super", "Use before declaration");
        }
    },
    {
        name: "Simple derived class constructor using this via a lambda",
        body: function () {
            class DerivedClassUsingThisViaLambda extends SimpleParent {
                constructor() {
                    var arrow = () => { this.bar = 'DerivedClassUsingThisViaLambda'; };
                    super();
                    arrow();
                }
            };

            let result = new DerivedClassUsingThisViaLambda();

            assert.areEqual("DerivedClassUsingThisViaLambda", result.bar, "Arrow is defined using this (before super call) and called after super call in derived constructor");
            assert.areEqual("SimpleParent", result.foo, "Parent class returned the object from super to derived constructor");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof DerivedClassUsingThisViaLambda, "Result object is instanceof derived class");
        }
    },
    {
        name: "Simple derived class constructor using this without calling super via a lambda causing use before declaration error",
        body: function () {
            class DerivedClassUsingThisIllegallyViaLambda extends SimpleParent {
                constructor() {
                    var arrow = () => { this.bar = 'DerivedClassUsingThisIllegallyViaLambda'; };
                    arrow();
                }
            };

            assert.throws(function() { new DerivedClassUsingThisIllegallyViaLambda(); }, ReferenceError, "It's a ReferenceError to access 'this' without calling super", "Use before declaration");
        }
    },
    {
        name: "Simple derived class constructor using this before calling super via a lambda causing use before declaration error",
        body: function () {
            class DerivedClassUsingThisIllegallyViaLambda extends SimpleParent {
                constructor() {
                    var arrow = () => { this.bar = 'DerivedClassUsingThisIllegallyViaLambda'; };
                    arrow();
                    super();
                }
            };

            assert.throws(function() { new DerivedClassUsingThisIllegallyViaLambda(); }, ReferenceError, "It's a ReferenceError to access 'this' without calling super", "Use before declaration");
        }
    },
    {
        name: "Simple derived class constructor using this and super via a lambda",
        body: function () {
            class DerivedClassUsingThisAndSuperViaLambda extends SimpleParent {
                constructor() {
                    var this_arrow = () => { this.bar = 'DerivedClassUsingThisAndSuperViaLambda'; };
                    var super_arrow = () => { super(); }
                    super_arrow();
                    this_arrow();
                }
            };

            let result = new DerivedClassUsingThisAndSuperViaLambda();

            assert.areEqual("DerivedClassUsingThisAndSuperViaLambda", result.bar, "Arrow is defined using this (before super call) and called after super call in derived constructor");
            assert.areEqual("SimpleParent", result.foo, "Parent class returned the object from super to derived constructor");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof DerivedClassUsingThisAndSuperViaLambda, "Result object is instanceof derived class");
        }
    },
    {
        name: "Simple derived class constructor using this and super via a lambda (lambda with super call defined first)",
        body: function () {
            class DerivedClassUsingThisAndSuperViaLambda extends SimpleParent {
                constructor() {
                    var super_arrow = () => { super(); }
                    var this_arrow = () => { this.bar = 'DerivedClassUsingThisAndSuperViaLambda'; };
                    super_arrow();
                    this_arrow();
                }
            };

            let result = new DerivedClassUsingThisAndSuperViaLambda();

            assert.areEqual("DerivedClassUsingThisAndSuperViaLambda", result.bar, "Arrow is defined using this (before super call) and called after super call in derived constructor");
            assert.areEqual("SimpleParent", result.foo, "Parent class returned the object from super to derived constructor");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof DerivedClassUsingThisAndSuperViaLambda, "Result object is instanceof derived class");
        }
    },
    {
        name: "Derived class constructor throws ReferenceError accessing this with no super call",
        body: function () {
            class IllegalThisAccessConstructor extends SimpleParent {
                constructor() {
                    this.bar = 'something';
                }
            };

            assert.throws(function() { new IllegalThisAccessConstructor(); }, ReferenceError, "It's a ReferenceError to access 'this' without calling super", "Use before declaration");
        }
    },
    {
        name: "Derived class constructor throws ReferenceError accessing this before super call",
        body: function () {
            class IllegalThisBeforeSuperConstructor extends SimpleParent {
                constructor() {
                    this.bar = 'something';
                    super();
                }
            };

            assert.throws(function() { new IllegalThisBeforeSuperConstructor(); }, ReferenceError, "It's a ReferenceError to access 'this' before calling super", "Use before declaration");
        }
    },
    {
        name: "Derived class throws ReferenceError with empty constructor (implicit access of this)",
        body: function () {
            class EmptyConstructor extends SimpleParent {
                constructor() {
                    // Implicitly "return this;"
                }
            };

            assert.throws(function() { new EmptyConstructor(); }, ReferenceError, "It's a ReferenceError to implicit return 'this' from class constructor without calling super", "Use before declaration");
        }
    },
    {
        name: "Derived class with default constructor returns initialized this argument",
        body: function () {
            class DefaultConstructor extends SimpleParent {
                // Implicitly
                // constructor(...args) { super(...args); }
            };

            let obj = new DefaultConstructor();

            assert.areEqual('SimpleParent', obj.foo, "Object from base constructor should have been returned.");
            assert.isTrue(obj instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(obj instanceof DefaultConstructor, "Result object is instanceof derived class");

            class DefaultConstructorReturningArgumentViaBaseClass extends UninitializedThisReturningArgumentConstructor {
                // Implicitly
                // constructor(...args) { super(...args); }
            };

            obj = new DefaultConstructorReturningArgumentViaBaseClass({ bar: 'DefaultConstructorReturningArgumentViaBaseClass' });

            assert.areEqual('DefaultConstructorReturningArgumentViaBaseClass', obj.bar, "Object from base constructor should have been returned.");
            assert.isFalse(obj instanceof UninitializedThisReturningArgumentConstructor, "Result object is not instanceof base class");
            assert.isFalse(obj instanceof DefaultConstructorReturningArgumentViaBaseClass, "Result object is not instanceof derived class");
        }
    },
    {
        name: "Derived class throws TypeError when returning non-object if this is initialized",
        body: function () {
            assert.throws(function() { new InitializedThisReturningArgumentConstructor(null); }, TypeError, "Returning null from derived constructor should throw TypeError", "Derived class constructor can return only object or undefined");
            assert.throws(function() { new InitializedThisReturningArgumentConstructor('string'); }, TypeError, "Returning 'string' from derived constructor should throw TypeError", "Derived class constructor can return only object or undefined");
            assert.throws(function() { new InitializedThisReturningArgumentConstructor(5); }, TypeError, "Returning 5 from derived constructor should throw TypeError", "Derived class constructor can return only object or undefined");
        }
    },
    {
        name: "Derived class throws TypeError when returning non-object if this is uninitialized",
        body: function () {
            assert.throws(function() { new UninitializedThisReturningArgumentConstructor(null); }, TypeError, "Returning null from derived constructor should throw TypeError", "Derived class constructor can return only object or undefined");
            assert.throws(function() { new UninitializedThisReturningArgumentConstructor('string'); }, TypeError, "Returning 'string' from derived constructor should throw TypeError", "Derived class constructor can return only object or undefined");
            assert.throws(function() { new UninitializedThisReturningArgumentConstructor(5); }, TypeError, "Returning 5 from derived constructor should throw TypeError", "Derived class constructor can return only object or undefined");
        }
    },
    {
        name: "TypeError is thrown when trying to derive from a function which is not a constructor",
        body: function () {
            assert.throws(function () { class A extends JSON { }; }, TypeError, "JSON object does not have an internal Construct slot", "Function is not a constructor");
            assert.throws(function () { class A extends Math { }; }, TypeError, "Math object does not have an internal Construct slot", "Function is not a constructor");

            function* gf(a) { yield 1 + a + this.a; }
            assert.throws(function () { class A extends gf { } }, TypeError, "Class deriving from a generator function throws TypeError", "Function is not a constructor");

            var lambda = (a) => a;
            assert.throws(function () { class A extends lambda { } }, TypeError, "Class deriving from a lambda throws TypeError", "Function is not a constructor");
        }
    },
    {
        name: "Derived class returning undefined, returns 'this' instead",
        body: function () {
            class ImplicitReturnUndefinedNotInitializedThis extends SimpleParent {
                constructor() {
                    return;
                }
            }

            assert.throws(function() { new ImplicitReturnUndefinedNotInitializedThis(); }, ReferenceError, "Derived class constructor implicitly returning undefined with no super call throws ReferenceError", "Use before declaration");

            class ExplicitReturnUndefinedNotInitializedThis extends SimpleParent {
                constructor() {
                    return undefined;
                }
            }

            assert.throws(function() { new ExplicitReturnUndefinedNotInitializedThis(); }, ReferenceError, "Derived class constructor explicitly returning undefined with no super call throws ReferenceError", "Use before declaration");

            class ImplicitReturnUndefinedInitializedThis extends SimpleParent {
                constructor() {
                    super();
                    this.bar = 'ImplicitReturnUndefinedInitializedThis';
                    return;
                }
            }

            let result = new ImplicitReturnUndefinedInitializedThis();

            assert.areEqual('SimpleParent', result.foo, "Object from base constructor should have been returned.");
            assert.areEqual('ImplicitReturnUndefinedInitializedThis', result.bar, "'this' modified in derived constructor.");
            assert.isTrue(result instanceof ImplicitReturnUndefinedInitializedThis, "Result object is instanceof derived class");
            assert.isTrue(result instanceof SimpleParent, "Result object is not instanceof base class");

            class ExplicitReturnUndefinedInitializedThis extends SimpleParent {
                constructor() {
                    super();
                    this.bar = 'ExplicitReturnUndefinedInitializedThis';
                    return undefined;
                }
            }

            result = new ExplicitReturnUndefinedInitializedThis();

            assert.areEqual('SimpleParent', result.foo, "Object from base constructor should have been returned.");
            assert.areEqual('ExplicitReturnUndefinedInitializedThis', result.bar, "'this' modified in derived constructor.");
            assert.isTrue(result instanceof ExplicitReturnUndefinedInitializedThis, "Result object is instanceof derived class");
            assert.isTrue(result instanceof SimpleParent, "Result object is not instanceof base class");
        }
    },
    {
        name: "Derived class returns any object and avoids super call / this initialization",
        body: function () {
            let obj = new UninitializedThisReturningArgumentConstructor({ foo: 'value' });

            assert.areEqual('value', obj.foo, "We returned an object from the class constructor which didn't initialize 'this' so we should get that object back");
        }
    },
    {
        name: "Derived class throws ReferenceError with multiple calls to super ('this' is already initialized)",
        body: function () {
            // Reset call counter in parent
            calls_to_ConstructorCountingParent = 0;

            class IllegalSuperCallConstructor extends ConstructorCountingParent {
                constructor() {
                    super();
                    super();
                }
            };

            assert.throws(function() { new IllegalSuperCallConstructor(); }, ReferenceError, "It's a ReferenceError to call super multiple times in a derived class constructor", "Multiple calls to 'super' in a class constructor are not allowed");
            assert.areEqual(2, calls_to_ConstructorCountingParent, "We do call the super function body twice but we throw ReferenceError when trying to assign the 'this' value after the second super call");
        }
    },
    {
        name: "Derived class throws ReferenceError with multiple calls to super ('this' is already initialized) with one call in a lambda",
        body: function () {
            // Reset call counter in parent
            calls_to_ConstructorCountingParent = 0;

            class IllegalSuperCallConstructor extends ConstructorCountingParent {
                constructor() {
                    let arrow = () => { super(); };
                    super();
                    arrow();
                }
            };

            assert.throws(function() { new IllegalSuperCallConstructor(); }, ReferenceError, "It's a ReferenceError to call super multiple times in a derived class constructor", "Multiple calls to 'super' in a class constructor are not allowed");
            assert.areEqual(2, calls_to_ConstructorCountingParent, "We do call the super function body twice but we throw ReferenceError when trying to assign the 'this' value after the second super call");
        }
    },
    {
        name: "Derived class throws ReferenceError with multiple calls to super ('this' is already initialized) with multiple calls in lambdas",
        body: function () {
            // Reset call counter in parent
            calls_to_ConstructorCountingParent = 0;

            class IllegalSuperCallConstructor extends ConstructorCountingParent {
                constructor() {
                    let arrow = () => { super(); };
                    let arrow2 = () => { super(); };
                    arrow();
                    arrow2();
                }
            };

            assert.throws(function() { new IllegalSuperCallConstructor(); }, ReferenceError, "It's a ReferenceError to call super multiple times in a derived class constructor", "Multiple calls to 'super' in a class constructor are not allowed");
            assert.areEqual(2, calls_to_ConstructorCountingParent, "We do call the super function body twice but we throw ReferenceError when trying to assign the 'this' value after the second super call");
        }
    },
    {
        name: "Derived class throws ReferenceError with multiple calls to super ('this' is already initialized) with multiple calls in the same lambda",
        body: function () {
            // Reset call counter in parent
            calls_to_ConstructorCountingParent = 0;

            class IllegalSuperCallConstructor extends ConstructorCountingParent {
                constructor() {
                    let arrow = () => { super(); super(); };
                    arrow();
                }
            };

            assert.throws(function() { new IllegalSuperCallConstructor(); }, ReferenceError, "It's a ReferenceError to call super multiple times in a derived class constructor", "Multiple calls to 'super' in a class constructor are not allowed");
            assert.areEqual(2, calls_to_ConstructorCountingParent, "We do call the super function body twice but we throw ReferenceError when trying to assign the 'this' value after the second super call");
        }
    },
    {
        name: "Derived class constructor captures this and super in a lambda but doesn't call the lambda",
        body: function () {
            class DerivedClassCapturingThisAndSuper extends SimpleParent {
                constructor() {
                    let arrow = () => { this.bar = 'lambda'; super(); };
                    super();
                    this.bar = "DerivedClassCapturingThisAndSuper";
                }
            };

            let result = new DerivedClassCapturingThisAndSuper();

            assert.areEqual("DerivedClassCapturingThisAndSuper", result.bar, "This is initialized with the return value from super()");
            assert.areEqual("SimpleParent", result.foo, "Parent class returned the object from super to derived constructor");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof DerivedClassCapturingThisAndSuper, "Result object is instanceof derived class");
        }
    },
    {
        name: "Creating an instance of base class doesn't block new.target calculation for super chain",
        body: function () {
            let parent = new SimpleParent();
            class SimpleDerivedClass extends SimpleParent {
                constructor() {
                    super();
                    this.bar = "SimpleDerivedClass";
                }
            };

            let result = new SimpleDerivedClass();

            assert.isFalse(Object.hasOwnProperty(parent, 'bar'), "Parent object doesn't have derived class values");
            assert.areEqual("SimpleParent", parent.foo, "Parent class initialized the object");
            assert.isTrue(parent instanceof SimpleParent, "Parent object is instanceof base class");
            assert.isFalse(parent instanceof SimpleDerivedClass, "Parent object isn't instanceof derived class");

            assert.areEqual("SimpleDerivedClass", result.bar, "This is initialized with the return value from super()");
            assert.areEqual("SimpleParent", result.foo, "Parent class returned the object from super to derived constructor");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof SimpleDerivedClass, "Result object is instanceof derived class");
        }
    },
    {
        name: "Chain of multiple derived classes",
        body: function () {
            class MiddleDerivedClass extends SimpleParent {
                constructor() {
                    super();
                    this.bar = "MiddleDerivedClass";
                }
            };

            class BottomDerivedClass extends MiddleDerivedClass {
                constructor() {
                    super();
                    this.baz = "BottomDerivedClass";
                }
            };

            let result = new BottomDerivedClass();

            assert.areEqual("BottomDerivedClass", result.baz, "This is initialized with the return value from super()");
            assert.areEqual("MiddleDerivedClass", result.bar, "This is initialized with the return value from super()");
            assert.areEqual("SimpleParent", result.foo, "Parent class returned the object from super to derived constructor");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof MiddleDerivedClass, "Result object is instanceof derived class");
            assert.isTrue(result instanceof BottomDerivedClass, "Result object is instanceof derived class");
        }
    },
    {
        name: "Derived class constructor leaks lambda which performs super",
        body: function() {
            class A {
                constructor() {
                    this.a = 'A';
                }
            }

            class B extends A {
                constructor() {
                    super();
                    this.b = 'B';
                }
            }

            class C extends B {
                constructor() {
                    var s = () => { super(); this.c = 'C'; return this; };
                    return s;
                }
            }

            var maker = new C();
            var result = maker();

            assert.areEqual("C", result.c, "This is initialized with the return value from super()");
            assert.areEqual("B", result.b, "This is initialized with the return value from super()");
            assert.areEqual("A", result.a, "Parent class returned the object from super to derived constructor");
            assert.isTrue(result instanceof A, "Result object is instanceof base class");
            assert.isTrue(result instanceof B, "Result object is instanceof derived class");
            assert.isTrue(result instanceof C, "Result object is instanceof derived class");

            assert.throws(function() { var result2 = maker(); }, ReferenceError, "Calling the escaped lambda again will throw since 'this' of the parent is already initialized", "Multiple calls to 'super' in a class constructor are not allowed");

            // creating a new lambda should let us construct one more object
            maker = new C();
            result = maker();

            assert.areEqual("C", result.c, "This is initialized with the return value from super()");
            assert.areEqual("B", result.b, "This is initialized with the return value from super()");
            assert.areEqual("A", result.a, "Parent class returned the object from super to derived constructor");
            assert.isTrue(result instanceof A, "Result object is instanceof base class");
            assert.isTrue(result instanceof B, "Result object is instanceof derived class");
            assert.isTrue(result instanceof C, "Result object is instanceof derived class");

            assert.throws(function() { var result2 = maker(); }, ReferenceError, "Calling the escaped lambda again will throw since 'this' of the parent is already initialized", "Multiple calls to 'super' in a class constructor are not allowed");
        }
    },
    {
        name: "Derived class constructor leaks lambda which performs super (lambda comes from middle derived class)",
        body: function() {
            class A {
                constructor() {
                    this.a = 'A';
                }
            }

            class B extends A {
                constructor() {
                    var s = () => { super(); this.b = 'B'; return this; };
                    return s;
                }
            }

            class C extends B {
                constructor() {
                    super();
                }
            }

            var maker = new C();
            var result = maker();

            assert.areEqual("B", result.b, "This is initialized with the return value from super()");
            assert.areEqual("A", result.a, "Parent class returned the object from super to derived constructor");
            assert.isTrue(result instanceof A, "Result object is instanceof base class");
            assert.isTrue(result instanceof B, "Result object is instanceof derived class");
            assert.isTrue(result instanceof C, "Result object is instanceof derived class");

            assert.throws(function() { var result2 = maker(); }, ReferenceError, "Calling the escaped lambda again will throw since 'this' of the parent is already initialized", "Multiple calls to 'super' in a class constructor are not allowed");

            // creating a new lambda should let us construct one more object
            maker = new C();
            result = maker();

            assert.areEqual("B", result.b, "This is initialized with the return value from super()");
            assert.areEqual("A", result.a, "Parent class returned the object from super to derived constructor");
            assert.isTrue(result instanceof A, "Result object is instanceof base class");
            assert.isTrue(result instanceof B, "Result object is instanceof derived class");
            assert.isTrue(result instanceof C, "Result object is instanceof derived class");

            assert.throws(function() { var result2 = maker(); }, ReferenceError, "Calling the escaped lambda again will throw since 'this' of the parent is already initialized", "Multiple calls to 'super' in a class constructor are not allowed");
        }
    },
    {
        name: "Derived class constructor leaks lambda which references 'this' in TDZ",
        body: function() {
            class A {
                constructor() {
                    this.a = 'A';
                }
            }

            class B extends A {
                constructor() {
                    super();
                    this.b = 'B';
                }
            }

            class C extends B {
                constructor() {
                    var s = () => { this.c = 'C'; super(); return this; };
                    return s;
                }
            }

            var maker = new C();

            assert.throws(function() { var result = maker(); }, ReferenceError, "Calling the escaped lambda throws since 'this' is accessed before super call", "Use before declaration");
        }
    },
    {
        name: "Derived class constructor leaks lambda which references 'this' in TDZ (lambda comes from middle derived class)",
        body: function() {
            class A {
                constructor() {
                    this.a = 'A';
                }
            }

            class B extends A {
                constructor() {
                    var s = () => { this.b = 'B'; super(); return this; };
                    return s;
                }
            }

            class C extends B {
                constructor() {
                    super();
                }
            }

            var maker = new C();

            assert.throws(function() { var result = maker(); }, ReferenceError, "Calling the escaped lambda throws since 'this' is accessed before super call", "Use before declaration");
        }
    },
    {
        name: "Derived class with null extends expression cannot be new'd",
        body: function () {
            class NullExtendsExpression extends null {
            };

            assert.throws(function() { new NullExtendsExpression(); }, TypeError, "Class that extends null throws when we attempt to call super as [[construct]]");
        }
    },
    {
        name: "Derived class with null extends expression can be new'd if constructor returns object",
        body: function () {
            class NullExtendsExpressionWithConstructor extends null {
                constructor(arg) {
                    return arg;
                }
            };

            var result = new NullExtendsExpressionWithConstructor({foo:'value'});

            assert.areEqual('value', result.foo, "Class derived from null expression can return an object safely");
            assert.isFalse(result instanceof NullExtendsExpressionWithConstructor, "Result object is not instanceof class");
        }
    },
    {
        name: "Derived constructor with a super call that isn't executed",
        body: function () {
            let returnFalse = () => { return false; };
            class DerivedClassAccessThisImplicitReturn extends SimpleParent {
                constructor() {
                    if (returnFalse()) {
                        super();
                    }
                    this.bar = '';
                }
            };

            assert.throws(function() { new DerivedClassAccessThisImplicitReturn(); }, ReferenceError, "When super isn't called, this is undecl", "Use before declaration");

            class DerivedClassImplicitReturn extends SimpleParent {
                constructor() {
                    if (returnFalse()) {
                        super();
                    }
                }
            };

            assert.throws(function() { new DerivedClassImplicitReturn(); }, ReferenceError, "When super isn't called, this is undecl", "Use before declaration");

            class DerivedClassAccessThisExplicitReturn extends SimpleParent {
                constructor() {
                    if (returnFalse()) {
                        super();
                    }
                    this.bar = '';
                    return this;
                }
            };

            assert.throws(function() { new DerivedClassAccessThisExplicitReturn(); }, ReferenceError, "When super isn't called, this is undecl", "Use before declaration");

            class DerivedClassExplicitReturn extends SimpleParent {
                constructor() {
                    if (returnFalse()) {
                        super();
                    }
                    return this;
                }
            };

            assert.throws(function() { new DerivedClassExplicitReturn(); }, ReferenceError, "When super isn't called, this is undecl", "Use before declaration");

            class DerivedClassAccessThisViaLambdaImplicitReturn extends SimpleParent {
                constructor() {
                    let arrow = () => { this.foo = ''; }
                    if (returnFalse()) {
                        super();
                    }
                    arrow();
                }
            };

            assert.throws(function() { new DerivedClassAccessThisViaLambdaImplicitReturn(); }, ReferenceError, "When super isn't called, this is undecl", "Use before declaration");

            class DerivedClassAccessThisViaLambdaExplicitReturn extends SimpleParent {
                constructor() {
                    let arrow = () => { this.foo = ''; }
                    if (returnFalse()) {
                        super();
                    }
                    arrow();
                    return this;
                }
            };

            assert.throws(function() { new DerivedClassAccessThisViaLambdaExplicitReturn(); }, ReferenceError, "When super isn't called, this is undecl", "Use before declaration");

            class DerivedClassWithThisScopeCaptureNoAccessImplicitReturn extends SimpleParent {
                constructor() {
                    let arrow = () => { this.foo = ''; }
                    if (returnFalse()) {
                        super();
                    }
                }
            };

            assert.throws(function() { new DerivedClassWithThisScopeCaptureNoAccessImplicitReturn(); }, ReferenceError, "When super isn't called, this is undecl", "Use before declaration");

            class DerivedClassWithThisScopeCaptureNoAccessExplicitReturn extends SimpleParent {
                constructor() {
                    let arrow = () => { this.foo = ''; }
                    if (returnFalse()) {
                        super();
                    }
                    return this;
                }
            };

            assert.throws(function() { new DerivedClassWithThisScopeCaptureNoAccessExplicitReturn(); }, ReferenceError, "When super isn't called, this is undecl", "Use before declaration");
        }
    },
    {
        name: "Derived class with super call inside an eval",
        body: function () {
            class SuperCallInEvalClass extends SimpleParent {
                constructor() {
                    eval('super();');
                }
            };

            var result = new SuperCallInEvalClass();

            assert.areEqual('SimpleParent', result.foo, "Class derived from SimpleParent has foo field set to 'SimpleParent'");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof SuperCallInEvalClass, "Result object is instanceof derived class");
        }
    },
    {
        name: "Derived class with super call inside an eval which accesses this",
        body: function () {
            class SuperCallInEvalClass extends SimpleParent {
                constructor() {
                    eval('super(); this.bar = "SuperCallInEvalClass";');
                }
            };

            var result = new SuperCallInEvalClass();

            assert.areEqual('SimpleParent', result.foo, "Class derived from SimpleParent has foo field set to 'SimpleParent'");
            assert.areEqual('SuperCallInEvalClass', result.bar, "Class derived from SimpleParent can return an object safely");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof SuperCallInEvalClass, "Result object is instanceof derived class");
        }
    },
    {
        name: "Derived class with super call inside an eval which accesses new.target",
        body: function () {
            class SuperCallInEvalClass extends SimpleParent {
                constructor() {
                    eval('assert.areEqual(new.target, SuperCallInEvalClass, "new.target === SuperCallInEvalClass"); super();');
                }
            };

            var result = new SuperCallInEvalClass();

            assert.areEqual('SimpleParent', result.foo, "Class derived from SimpleParent has foo field set to 'SimpleParent'");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof SuperCallInEvalClass, "Result object is instanceof derived class");
        }
    },
    {
        name: "Derived class with super call inside an eval which accesses new.target and this",
        body: function () {
            class SuperCallInEvalClass extends SimpleParent {
                constructor() {
                    eval('assert.areEqual(new.target, SuperCallInEvalClass); super(); this.bar = "SuperCallInEvalClass";');
                }
            };

            var result = new SuperCallInEvalClass();

            assert.areEqual('SimpleParent', result.foo, "Class derived from SimpleParent has foo field set to 'SimpleParent'");
            assert.areEqual('SuperCallInEvalClass', result.bar, "Result object has derived field bar set to 'SuperCallInEvalClass'");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof SuperCallInEvalClass, "Result object is instanceof derived class");
        }
    },
    {
        name: "Derived class with super call inside an eval access to this inside eval and constructor",
        body: function () {
            class SuperCallInEvalClass extends SimpleParent {
                constructor() {
                    eval('super(); this.bar = "SuperCallInEvalClass";');
                    this.baz = "SuperCallInEvalClass_ctor";
                }
            };

            var result = new SuperCallInEvalClass();

            assert.areEqual('SimpleParent', result.foo, "Class derived from SimpleParent has foo field set to 'SimpleParent'");
            assert.areEqual('SuperCallInEvalClass', result.bar, "Result object has derived field bar set to 'SuperCallInEvalClass'");
            assert.areEqual('SuperCallInEvalClass_ctor', result.baz, "Result object has derived field baz set to 'SuperCallInEvalClass_ctor'");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof SuperCallInEvalClass, "Result object is instanceof derived class");
        }
    },
    {
        name: "Derived class 'this' access inside an eval",
        body: function () {
            class SuperCallInEvalClass extends SimpleParent {
                constructor() {
                    super();
                    eval('this.bar = "SuperCallInEvalClass";');
                }
            };

            var result = new SuperCallInEvalClass();

            assert.areEqual('SimpleParent', result.foo, "Class derived from SimpleParent has foo field set to 'SimpleParent'");
            assert.areEqual('SuperCallInEvalClass', result.bar, "Result object has derived field bar set to 'SuperCallInEvalClass'");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof SuperCallInEvalClass, "Result object is instanceof derived class");
        }
    },
    {
        name: "Derived class 'this' access inside and outside an eval",
        body: function () {
            class SuperCallInEvalClass extends SimpleParent {
                constructor() {
                    super();
                    eval('this.bar = "SuperCallInEvalClass";');
                    this.baz = "SuperCallInEvalClass_ctor";
                }
            };

            var result = new SuperCallInEvalClass();

            assert.areEqual('SimpleParent', result.foo, "Class derived from SimpleParent has foo field set to 'SimpleParent'");
            assert.areEqual('SuperCallInEvalClass', result.bar, "Result object has derived field bar set to 'SuperCallInEvalClass'");
            assert.areEqual('SuperCallInEvalClass_ctor', result.baz, "Result object has derived field baz set to 'SuperCallInEvalClass_ctor'");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof SuperCallInEvalClass, "Result object is instanceof derived class");
        }
    },
    {
        name: "Derived class super call inside an eval with an unused arrow super call",
        body: function () {
            class SuperCallInEvalClass extends SimpleParent {
                constructor() {
                    let arrow = () => { super(); }
                    eval('super(); this.bar = "SuperCallInEvalClass";');
                }
            };

            var result = new SuperCallInEvalClass();

            assert.areEqual('SimpleParent', result.foo, "Class derived from SimpleParent has foo field set to 'SimpleParent'");
            assert.areEqual('SuperCallInEvalClass', result.bar, "Result object has derived field bar set to 'SuperCallInEvalClass'");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof SuperCallInEvalClass, "Result object is instanceof derived class");
        }
    },
    {
        name: "Derived class super call inside an arrow with this access in an eval",
        body: function () {
            class SuperCallInEvalClass extends SimpleParent {
                constructor() {
                    let arrow = () => { super(); }
                    arrow();
                    eval('this.bar = "SuperCallInEvalClass";');
                }
            };

            var result = new SuperCallInEvalClass();

            assert.areEqual('SimpleParent', result.foo, "Class derived from SimpleParent has foo field set to 'SimpleParent'");
            assert.areEqual('SuperCallInEvalClass', result.bar, "Result object has derived field bar set to 'SuperCallInEvalClass'");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof SuperCallInEvalClass, "Result object is instanceof derived class");
        }
    },
    {
        name: "Derived class super call inside an arrow accessing new.target with this access in an eval",
        body: function () {
            class SuperCallInEvalClass extends SimpleParent {
                constructor() {
                    let arrow = () => {
                        assert.areEqual(SuperCallInEvalClass, new.target, "SuperCallInEvalClass === new.target");
                        super();
                    }
                    arrow();
                    eval('this.bar = "SuperCallInEvalClass";');
                }
            };

            var result = new SuperCallInEvalClass();

            assert.areEqual('SimpleParent', result.foo, "Class derived from SimpleParent has foo field set to 'SimpleParent'");
            assert.areEqual('SuperCallInEvalClass', result.bar, "Result object has derived field bar set to 'SuperCallInEvalClass'");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof SuperCallInEvalClass, "Result object is instanceof derived class");
        }
    },
    {
        name: "Derived class with arrow and eval accessing new.target, this - super in the arrow",
        body: function () {
            class SuperCallInEvalClass extends SimpleParent {
                constructor() {
                    let arrow = () => {
                        assert.areEqual(SuperCallInEvalClass, new.target, "SuperCallInEvalClass === new.target");
                        super();
                        this.baz = "SuperCallInEvalClass_arrow";
                    }
                    arrow();
                    eval('assert.areEqual(SuperCallInEvalClass, new.target, "SuperCallInEvalClass === new.target"); this.bar = "SuperCallInEvalClass";');
                }
            };

            var result = new SuperCallInEvalClass();

            assert.areEqual('SimpleParent', result.foo, "Class derived from SimpleParent has foo field set to 'SimpleParent'");
            assert.areEqual('SuperCallInEvalClass', result.bar, "Result object has derived field bar set to 'SuperCallInEvalClass'");
            assert.areEqual('SuperCallInEvalClass_arrow', result.baz, "Result object has derived field baz set to 'SuperCallInEvalClass_arrow'");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof SuperCallInEvalClass, "Result object is instanceof derived class");
        }
    },
    {
        name: "Derived class with arrow and eval accessing new.target, this - super in the eval",
        body: function () {
            class SuperCallInEvalClass extends SimpleParent {
                constructor() {
                    let arrow = () => {
                        assert.areEqual(SuperCallInEvalClass, new.target, "SuperCallInEvalClass === new.target");
                        this.baz = "SuperCallInEvalClass_arrow";
                    }
                    eval('assert.areEqual(SuperCallInEvalClass, new.target, "SuperCallInEvalClass === new.target"); super(); this.bar = "SuperCallInEvalClass";');
                    arrow();
                }
            };

            var result = new SuperCallInEvalClass();

            assert.areEqual('SimpleParent', result.foo, "Class derived from SimpleParent has foo field set to 'SimpleParent'");
            assert.areEqual('SuperCallInEvalClass', result.bar, "Result object has derived field bar set to 'SuperCallInEvalClass'");
            assert.areEqual('SuperCallInEvalClass_arrow', result.baz, "Result object has derived field baz set to 'SuperCallInEvalClass_arrow'");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof SuperCallInEvalClass, "Result object is instanceof derived class");
        }
    },
    {
        name: "Derived class with arrow and multiple evals accessing new.target, this - super in the eval",
        body: function () {
            class SuperCallInEvalClass extends SimpleParent {
                constructor() {
                    eval('assert.areEqual(SuperCallInEvalClass, new.target, "SuperCallInEvalClass === new.target");');
                    let arrow = () => {
                        assert.areEqual(SuperCallInEvalClass, new.target, "SuperCallInEvalClass === new.target");
                        this.baz = "SuperCallInEvalClass_arrow";
                    }
                    eval('super();');
                    arrow();
                    eval('this.bar = "SuperCallInEvalClass";');
                }
            };

            var result = new SuperCallInEvalClass();

            assert.areEqual('SimpleParent', result.foo, "Class derived from SimpleParent has foo field set to 'SimpleParent'");
            assert.areEqual('SuperCallInEvalClass', result.bar, "Result object has derived field bar set to 'SuperCallInEvalClass'");
            assert.areEqual('SuperCallInEvalClass_arrow', result.baz, "Result object has derived field baz set to 'SuperCallInEvalClass_arrow'");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof SuperCallInEvalClass, "Result object is instanceof derived class");
        }
    },
    {
        name: "Derived class testing all arrow/eval scenarios together",
        body: function () {
            class SuperCallInEvalClass extends SimpleParent {
                constructor(callSuperInCtor, callSuperInLambda, callSuperInEval) {
                    assert.areEqual(SuperCallInEvalClass, new.target, "SuperCallInEvalClass === new.target");
                    eval('assert.areEqual(SuperCallInEvalClass, new.target, "SuperCallInEvalClass === new.target");');

                    let arrow_pre = () => {
                        assert.areEqual(SuperCallInEvalClass, new.target, "SuperCallInEvalClass === new.target");
                    }
                    let arrow_post = () => {
                        this.baz = "SuperCallInEvalClass_arrow";
                    }
                    let arrow_super = () => {
                        super();
                    }

                    arrow_pre();
                    if (callSuperInCtor) {
                        super();
                    }
                    if (callSuperInLambda) {
                        arrow_super();
                    }
                    if (callSuperInEval) {
                        eval('super();');
                    }

                    arrow_post();
                    eval('this.bar = "SuperCallInEvalClass_eval";');
                    this.bot = 'SuperCallInEvalClass_ctor';
                }
            };

            function verifyObj(result) {
                assert.areEqual('SimpleParent', result.foo, "Class derived from SimpleParent has foo field set to 'SimpleParent'");
                assert.areEqual('SuperCallInEvalClass_eval', result.bar, "Result object has derived field bar set to 'SuperCallInEvalClass_eval'");
                assert.areEqual('SuperCallInEvalClass_arrow', result.baz, "Result object has derived field baz set to 'SuperCallInEvalClass_arrow'");
                assert.areEqual('SuperCallInEvalClass_ctor', result.bot, "Result object has derived field bot set to 'SuperCallInEvalClass_ctor'");
                assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
                assert.isTrue(result instanceof SuperCallInEvalClass, "Result object is instanceof derived class");
            }

            verifyObj(new SuperCallInEvalClass(true,false,false));
            verifyObj(new SuperCallInEvalClass(false,true,false));
            verifyObj(new SuperCallInEvalClass(false,false,true));

            assert.throws(function() { new SuperCallInEvalClass(false,false,false); }, ReferenceError, "Not calling super at all will cause a ReferenceError", "Use before declaration");
            assert.throws(function() { new SuperCallInEvalClass(true,true,false); }, ReferenceError, "Calling super in the ctor and a lambda throws a ReferenceError", "Multiple calls to 'super' in a class constructor are not allowed");
            assert.throws(function() { new SuperCallInEvalClass(true,false,true); }, ReferenceError, "Calling super in the ctor and eval throws a ReferenceError", "Multiple calls to 'super' in a class constructor are not allowed");
            assert.throws(function() { new SuperCallInEvalClass(false,true,true); }, ReferenceError, "Calling super in a lambda and eval throws a ReferenceError", "Multiple calls to 'super' in a class constructor are not allowed");
        }
    },
    {
        name: "Chained derived constructors performing super calls",
        body: function () {
            class DerivedClassUsingSuperInEval extends SimpleParent {
                constructor() {
                    eval('super();');
                }
            };
            class DerivedClassUsingSuperInArrow extends DerivedClassUsingSuperInEval {
                constructor() {
                    let arrow = () => { super(); }
                    arrow();
                }
            };
            class DerivedClassUsingDefaultConstructor extends DerivedClassUsingSuperInArrow {

            }
            class BottomLevelDerivedClass extends DerivedClassUsingDefaultConstructor {
                constructor() {
                    super();
                }
            };

            var result = new BottomLevelDerivedClass();

            assert.areEqual('SimpleParent', result.foo, "Class derived from SimpleParent has foo field set to 'SimpleParent'");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof DerivedClassUsingSuperInEval, "Result object is instanceof derived class");
            assert.isTrue(result instanceof DerivedClassUsingSuperInArrow, "Result object is instanceof derived class");
            assert.isTrue(result instanceof DerivedClassUsingDefaultConstructor, "Result object is instanceof derived class");
            assert.isTrue(result instanceof BottomLevelDerivedClass, "Result object is instanceof derived class");
        }
    },
    {
        name: "Derived class with nested evals performing super",
        body: function () {
            class SuperCallInEvalClass extends SimpleParent {
                constructor() {
                    var inner = "SuperCallInEvalClass_inner_eval";
                    var outer = "SuperCallInEvalClass_outer_eval";
                    eval('eval("super(); this.bar = inner;"); this.baz = outer;');
                }
            };

            var result = new SuperCallInEvalClass();

            assert.areEqual('SimpleParent', result.foo, "Class derived from SimpleParent has foo field set to 'SimpleParent'");
            assert.areEqual('SuperCallInEvalClass_inner_eval', result.bar, "Result object has derived field bar set to 'SuperCallInEvalClass_inner_eval'");
            assert.areEqual('SuperCallInEvalClass_outer_eval', result.baz, "Result object has derived field baz set to 'SuperCallInEvalClass_outer_eval'");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof SuperCallInEvalClass, "Result object is instanceof derived class");
        }
    },
    {
        name: "Derived class with eval in arrow performing super",
        body: function () {
            class SuperCallInEvalClass extends SimpleParent {
                constructor() {
                    var inner = "SuperCallInEvalClass_inner_eval";
                    var outer = "SuperCallInEvalClass_outer_eval";
                    var arrow = () => {
                        eval("super(); this.bar = inner;");
                        this.baz = outer;
                    };
                    arrow();
                }
            };

            var result = new SuperCallInEvalClass();

            assert.areEqual('SimpleParent', result.foo, "Class derived from SimpleParent has foo field set to 'SimpleParent'");
            assert.areEqual('SuperCallInEvalClass_inner_eval', result.bar, "Result object has derived field bar set to 'SuperCallInEvalClass_inner_eval'");
            assert.areEqual('SuperCallInEvalClass_outer_eval', result.baz, "Result object has derived field baz set to 'SuperCallInEvalClass_outer_eval'");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof SuperCallInEvalClass, "Result object is instanceof derived class");
        }
    },
    {
        name: "Derived class with nested eval in arrow performing super",
        body: function () {
            class SuperCallInEvalClass extends SimpleParent {
                constructor() {
                    var inner = "SuperCallInEvalClass_inner_eval";
                    var outer = "SuperCallInEvalClass_outer_eval";
                    var arrow = () => {
                        eval(`eval("super(); this.bar = inner;");`);
                        this.baz = outer;
                    };
                    arrow();
                }
            };

            var result = new SuperCallInEvalClass();

            assert.areEqual('SimpleParent', result.foo, "Class derived from SimpleParent has foo field set to 'SimpleParent'");
            assert.areEqual('SuperCallInEvalClass_inner_eval', result.bar, "Result object has derived field bar set to 'SuperCallInEvalClass_inner_eval'");
            assert.areEqual('SuperCallInEvalClass_outer_eval', result.baz, "Result object has derived field baz set to 'SuperCallInEvalClass_outer_eval'");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof SuperCallInEvalClass, "Result object is instanceof derived class");
        }
    },
    {
        name: "Derived class with nested eval nested in arrow performing super",
        body: function () {
            class SuperCallInEvalClass extends SimpleParent {
                constructor() {
                    var inner = "SuperCallInEvalClass_inner_eval";
                    var outer = "SuperCallInEvalClass_outer_eval";
                    var arrow = () => {
                        eval(`eval("super(); this.bar = inner;");`);
                        this.baz = outer;
                    };
                    var exec_arrow = () => {
                        arrow();
                    };
                    exec_arrow();
                }
            };

            var result = new SuperCallInEvalClass();

            assert.areEqual('SimpleParent', result.foo, "Class derived from SimpleParent has foo field set to 'SimpleParent'");
            assert.areEqual('SuperCallInEvalClass_inner_eval', result.bar, "Result object has derived field bar set to 'SuperCallInEvalClass_inner_eval'");
            assert.areEqual('SuperCallInEvalClass_outer_eval', result.baz, "Result object has derived field baz set to 'SuperCallInEvalClass_outer_eval'");
            assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
            assert.isTrue(result instanceof SuperCallInEvalClass, "Result object is instanceof derived class");
        }
    },
    {
        name: "Derived class with nested constructor calls and arrow performing super inside eval",
        body: function () {
            class B { }
            class A extends B
            {
                constructor()
                {
                    eval("(()=>{ super(); this.value = 1; class X extends Object { constructor() { eval(\"super(); this.value = 2; \"); } } var x = new X(); this.x = x;})()");
                }
            }
            var a=new A();

            assert.isTrue(a instanceof A, "object a should be instanceof derived class");
            assert.isTrue(a instanceof B, "object should be instanceof base class");
            assert.areEqual(1, a.value, "assignment to 'this' inside constructor eval nested arrow function");
            assert.areEqual(2, a.x.value, "assignment to 'this' inside nested constructor and eval");
        }
    },
    {
         name: "Derived class with arrow in eval performing super",
         body: function () {
             class SuperCallInEvalClass extends SimpleParent {
                 constructor() {
                     var inner = "SuperCallInEvalClass_inner_eval";
                     var outer = "SuperCallInEvalClass_outer_eval";
                     eval('(()=>{super(); this.bar = inner;})(); this.baz = outer;');
                 }
             };

             var result = new SuperCallInEvalClass();

             assert.areEqual('SimpleParent', result.foo, "Class derived from SimpleParent has foo field set to 'SimpleParent'");
             assert.areEqual('SuperCallInEvalClass_inner_eval', result.bar, "Result object has derived field bar set to 'SuperCallInEvalClass_inner_eval'");
             assert.areEqual('SuperCallInEvalClass_outer_eval', result.baz, "Result object has derived field baz set to 'SuperCallInEvalClass_outer_eval'");
             assert.isTrue(result instanceof SimpleParent, "Result object is instanceof base class");
             assert.isTrue(result instanceof SuperCallInEvalClass, "Result object is instanceof derived class");
         }
    },
    {
        name: "Eval class hierarchy with super call",
        body: function () {
            let result = eval(`
                class EvalSimpleParent {
                    constructor() {
                        this.foo = "EvalSimpleParent";
                    }
                };
                class EvalDerivedClass extends EvalSimpleParent {
                    constructor() {
                        super();
                        this.bar = "EvalDerivedClass";
                    }
                };
                var o = new EvalDerivedClass();

                assert.isTrue(o instanceof EvalSimpleParent, "Result object is instanceof base class");
                assert.isTrue(o instanceof EvalDerivedClass, "Result object is instanceof derived class");

                o;
            `);

            assert.areEqual('EvalSimpleParent', result.foo, "Class derived from EvalSimpleParent has foo field set to 'EvalSimpleParent'");
            assert.areEqual('EvalDerivedClass', result.bar, "Result object has derived field bar set to 'EvalDerivedClass'");
        }
    },
    {
        name: "Eval class hierarchy with super call inside arrow function",
        body: function () {
            let result = eval(`
                class EvalSimpleParent {
                    constructor() {
                        this.foo = "EvalSimpleParent";
                    }
                };
                class EvalDerivedClass extends EvalSimpleParent {
                    constructor() {
                        var inner = "EvalDerivedClass_inner";
                        var outer = "EvalDerivedClass_outer";
                        var arrow = () => {
                            super();
                            this.baz = inner;
                        };
                        arrow();
                        this.bar = outer;
                    }
                };
                var o = new EvalDerivedClass();

                assert.isTrue(o instanceof EvalSimpleParent, "Result object is instanceof base class");
                assert.isTrue(o instanceof EvalDerivedClass, "Result object is instanceof derived class");

                o;
            `);

            assert.areEqual('EvalSimpleParent', result.foo, "Class derived from EvalSimpleParent has foo field set to 'EvalSimpleParent'");
            assert.areEqual('EvalDerivedClass_outer', result.bar, "Result object has derived field bar set to 'EvalDerivedClass_outer'");
            assert.areEqual('EvalDerivedClass_inner', result.baz, "Result object has derived field baz set to 'EvalDerivedClass_inner'");
        }
    },
    {
        name: "Eval class hierarchy with super call inside nested eval",
        body: function () {
            let result = eval(`
                class EvalSimpleParent {
                    constructor() {
                        this.foo = "EvalSimpleParent";
                    }
                };
                class EvalDerivedClass extends EvalSimpleParent {
                    constructor() {
                        var inner = "EvalDerivedClass_inner";
                        var outer = "EvalDerivedClass_outer";
                        eval('super(); this.bar = inner;');
                        this.baz = outer;
                    }
                };
                var o = new EvalDerivedClass();

                assert.isTrue(o instanceof EvalSimpleParent, "Result object is instanceof base class");
                assert.isTrue(o instanceof EvalDerivedClass, "Result object is instanceof derived class");

                o;
            `);

            assert.areEqual('EvalSimpleParent', result.foo, "Class derived from EvalSimpleParent has foo field set to 'EvalSimpleParent'");
            assert.areEqual('EvalDerivedClass_inner', result.bar, "Result object has derived field bar set to 'EvalDerivedClass_inner'");
            assert.areEqual('EvalDerivedClass_outer', result.baz, "Result object has derived field baz set to 'EvalDerivedClass_outer'");
        }
    },
    {
        name: "Eval class hierarchy with super call inside eval inside arrow function",
        body: function () {
            let result = eval(`
                class EvalSimpleParent {
                    constructor() {
                        this.foo = "EvalSimpleParent";
                    }
                };
                class EvalDerivedClass extends EvalSimpleParent {
                    constructor() {
                        var moreinner = "EvalDerivedClass_moreinner";
                        var inner = "EvalDerivedClass_inner";
                        var outer = "EvalDerivedClass_outer";
                        var arrow = () => {
                            eval('super(); this.bat = moreinner;');
                            this.baz = inner;
                        };
                        arrow();
                        this.bar = outer;
                    }
                };
                var o = new EvalDerivedClass();

                assert.isTrue(o instanceof EvalSimpleParent, "Result object is instanceof base class");
                assert.isTrue(o instanceof EvalDerivedClass, "Result object is instanceof derived class");

                o;
            `);

            assert.areEqual('EvalSimpleParent', result.foo, "Class derived from EvalSimpleParent has foo field set to 'EvalSimpleParent'");
            assert.areEqual('EvalDerivedClass_outer', result.bar, "Result object has derived field bar set to 'EvalDerivedClass_outer'");
            assert.areEqual('EvalDerivedClass_inner', result.baz, "Result object has derived field baz set to 'EvalDerivedClass_inner'");
            assert.areEqual('EvalDerivedClass_moreinner', result.bat, "Result object has derived field bat set to 'EvalDerivedClass_moreinner'");
        }
    },
    {
        name: "ES5-style class as a base class - explicit return of this from base",
        body: function () {
            function Base() {
                assert.areEqual(new.target, Derived, "new.target === Derived");
                this.foo = "Base";
                return this;
            }
            Base.prototype = {
                constructor: Base
            }
            class Derived extends Base {
                constructor() {
                    super();
                    this.bar = "Derived";
                }
            }
            let result = new Derived();

            assert.isTrue(result instanceof Base, "Result object is instanceof base class");
            assert.isTrue(result instanceof Derived, "Result object is instanceof derived class");
            assert.areEqual('Base', result.foo, "Class derived from Base has foo field set to 'Base'");
            assert.areEqual('Derived', result.bar, "Result object has derived field bar set to 'Derived'");
        }
    },
    {
        name: "ES5-style class as a base class with the super call inside an eval - explicit return of this from base",
        body: function () {
            function Base() {
                assert.areEqual(new.target, Derived, "new.target === Derived");
                this.foo = "Base";
                return this;
            }
            Base.prototype = {
                constructor: Base
            }
            class Derived extends Base {
                constructor() {
                    eval('super();');
                    this.bar = "Derived";
                }
            }
            let result = new Derived();

            assert.isTrue(result instanceof Base, "Result object is instanceof base class");
            assert.isTrue(result instanceof Derived, "Result object is instanceof derived class");
            assert.areEqual('Base', result.foo, "Class derived from Base has foo field set to 'Base'");
            assert.areEqual('Derived', result.bar, "Result object has derived field bar set to 'Derived'");
        }
    },
    {
        name: "ES5-style class as a base class with the super call inside an arrow - explicit return of this from base",
        body: function () {
            function Base() {
                assert.areEqual(new.target, Derived, "new.target === Derived");
                this.foo = "Base";
                return this;
            }
            Base.prototype = {
                constructor: Base
            }
            class Derived extends Base {
                constructor() {
                    let arrow = () => { super(); };
                    arrow();
                    this.bar = "Derived";
                }
            }
            let result = new Derived();

            assert.isTrue(result instanceof Base, "Result object is instanceof base class");
            assert.isTrue(result instanceof Derived, "Result object is instanceof derived class");
            assert.areEqual('Base', result.foo, "Class derived from Base has foo field set to 'Base'");
            assert.areEqual('Derived', result.bar, "Result object has derived field bar set to 'Derived'");
        }
    },
    {
        name: "ES5-style class as a base class - implicit return of this from base",
        body: function () {
            function Base() {
                assert.areEqual(new.target, Derived, "new.target === Derived");
                this.foo = "Base";
            }
            Base.prototype = {
                constructor: Base
            }
            class Derived extends Base {
                constructor() {
                    super();
                    this.bar = "Derived";
                }
            }
            let result = new Derived();

            assert.isTrue(result instanceof Base, "Result object is instanceof base class");
            assert.isTrue(result instanceof Derived, "Result object is instanceof derived class");
            assert.areEqual('Base', result.foo, "Class derived from Base has foo field set to 'Base'");
            assert.areEqual('Derived', result.bar, "Result object has derived field bar set to 'Derived'");
        }
    },
    {
        name: "ES5-style class as a base class with the super call inside an eval - implicit return of this from base",
        body: function () {
            function Base() {
                assert.areEqual(new.target, Derived, "new.target === Derived");
                this.foo = "Base";
            }
            Base.prototype = {
                constructor: Base
            }
            class Derived extends Base {
                constructor() {
                    eval('super();');
                    this.bar = "Derived";
                }
            }
            let result = new Derived();

            assert.isTrue(result instanceof Base, "Result object is instanceof base class");
            assert.isTrue(result instanceof Derived, "Result object is instanceof derived class");
            assert.areEqual('Base', result.foo, "Class derived from Base has foo field set to 'Base'");
            assert.areEqual('Derived', result.bar, "Result object has derived field bar set to 'Derived'");
        }
    },
    {
        name: "ES5-style class as a base class with the super call inside an arrow - implicit return of this from base",
        body: function () {
            function Base() {
                assert.areEqual(new.target, Derived, "new.target === Derived");
                this.foo = "Base";
            }
            Base.prototype = {
                constructor: Base
            }
            class Derived extends Base {
                constructor() {
                    let arrow = () => { super(); };
                    arrow();
                    this.bar = "Derived";
                }
            }
            let result = new Derived();

            assert.isTrue(result instanceof Base, "Result object is instanceof base class");
            assert.isTrue(result instanceof Derived, "Result object is instanceof derived class");
            assert.areEqual('Base', result.foo, "Class derived from Base has foo field set to 'Base'");
            assert.areEqual('Derived', result.bar, "Result object has derived field bar set to 'Derived'");
        }
    },
    {
        name: "Function as a base class can't assign this to non-object via super call",
        body: function () {
            function BaseReturnsArgument(arg) {
                assert.areEqual(new.target, DerivedFromBase, "new.target === DerivedFromBase");
                this.foo = 'BaseReturnsArgument';
                return arg;
            }
            BaseReturnsArgument.prototype = {
                constructor: BaseReturnsArgument
            }
            class DerivedFromBase extends BaseReturnsArgument {
                constructor(arg) {
                    super(arg);
                    assert.isFalse(this === arg, "If base function returns non-object, 'this' is returned instead");
                    this.bar = 'DerivedFromBase';
                }
            }

            function testDerivedClass(arg) {
                let result = new DerivedFromBase(arg);

                assert.isTrue(result instanceof BaseReturnsArgument, "Result object is instanceof base class");
                assert.isTrue(result instanceof DerivedFromBase, "Result object is instanceof derived class");
                assert.areEqual('BaseReturnsArgument', result.foo, "Class derived from Base has foo field set to 'Base'");
                assert.areEqual('DerivedFromBase', result.bar, "Result object has derived field bar set to 'Derived'");
            }

            testDerivedClass();
            testDerivedClass(undefined);
            testDerivedClass(null);
            testDerivedClass('string');
            testDerivedClass(5);
            testDerivedClass(Symbol());
        }
    },
    {
        name: "Function as a base class can return an object to override this",
        body: function () {
            function Base() {
                assert.areEqual(new.target, Derived, "new.target === Derived");
                this.foo = 'bad';
                return { foo: 'Base' };
            }
            Base.prototype = {
                constructor: Base
            }
            class Derived extends Base {
                constructor() {
                    super();
                    this.bar = 'Derived';
                }
            }

            let result = new Derived();

            assert.isFalse(result instanceof Base, "Result object is instanceof base class");
            assert.isFalse(result instanceof Derived, "Result object is instanceof derived class");
            assert.areEqual('Base', result.foo, "Class derived from Base has foo field set to 'Base'");
            assert.areEqual('Derived', result.bar, "Result object has derived field bar set to 'Derived'");
        }
    },
    {
        name: "Super call with expressions in arguments",
        body: function () {
            function Base() {
                assert.areEqual(new.target, Derived, "new.target === Derived");
                assert.areEqual(3, arguments[0], 'arguments[0] === 3')
                assert.areEqual('str', arguments[1], 'arguments[1] === "str"')
                assert.areEqual(1, arguments[2], 'arguments[2] === 1')
                assert.areEqual(2, arguments[3], 'arguments[3] === 2')
                assert.areEqual(3, arguments[4], 'arguments[4] === 3')
                assert.areEqual(5, arguments.length, 'arguments.length === 5');
                this.foo = 'Base';
            }
            Base.prototype = {
                constructor: Base
            }
            function foo() { return 'str'; }
            class Derived extends Base {
                constructor() {
                    super(1+2,foo(),...[1,2,3]);
                    this.bar = 'Derived';
                }
            }

            let result = new Derived();

            assert.isTrue(result instanceof Base, "Result object is instanceof base class");
            assert.isTrue(result instanceof Derived, "Result object is instanceof derived class");
            assert.areEqual('Base', result.foo, "Class derived from Base has foo field set to 'Base'");
            assert.areEqual('Derived', result.bar, "Result object has derived field bar set to 'Derived'");
        }
    },
];

testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });


text:
FFI bindings to ehstorguids.

[Documentation](https://retep998.github.io/doc/ehstorguids-sys/)

redpajama_set_name: RedPajamaGithub


text:
namespace content {

class CONTENT_EXPORT PepperPrintingHost : public ppapi::host::ResourceHost {
 public:
  PepperPrintingHost(
      ppapi::host::PpapiHost* host,
      PP_Instance instance,
      PP_Resource resource,
      std::unique_ptr<PepperPrintSettingsManager> print_settings_manager);

  PepperPrintingHost(const PepperPrintingHost&) = delete;
  PepperPrintingHost& operator=(const PepperPrintingHost&) = delete;

  ~PepperPrintingHost() override;

  // ppapi::host::ResourceHost implementation.
  int32_t OnResourceMessageReceived(
      const IPC::Message& msg,
      ppapi::host::HostMessageContext* context) override;

 private:
  int32_t OnGetDefaultPrintSettings(ppapi::host::HostMessageContext* context);

  void PrintSettingsCallback(ppapi::host::ReplyMessageContext reply_context,
                             PepperPrintSettingsManager::Result result);

  std::unique_ptr<PepperPrintSettingsManager> print_settings_manager_;

  base::WeakPtrFactory<PepperPrintingHost> weak_factory_{this};
};

}  // namespace content

#endif  // CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_PRINTING_HOST_H_


text:
using System;
using System.Collections.Generic;
using Loupe.Extensibility;
using Loupe.Extensibility.Client;
using Loupe.Extensibility.Data;
using Loupe.Extensibility.Server;
using Loupe.Extension.FogBugz.Internal;

namespace Loupe.Extension.FogBugz
{
    /// <summary>
    /// Allows sessions to be scanned and errors reported to FogBugz
    /// </summary>
    public class SessionAnalyzer : ISessionAnalyzer, ISessionCommand
    {
        private const string LogCategory = RepositoryController.LogCategory +  ".Session Analyzer";

        private bool m_Initialized;
        private bool m_IsDisposed;
        private IRepositoryContext m_Context;
        private RepositoryController m_Controller;

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            if (m_Initialized)
                m_Initialized = false;

            if (m_IsDisposed)
                throw new InvalidOperationException("The add-in has already been disposed");

            m_IsDisposed = true;
        }

        /// <summary>
        /// Called to initialize the session analyzer.
        /// </summary>
        /// <param name="context">A standard interface to the hosting environment for the analyzer</param>
        /// <remarks>
        /// If any exception is thrown during this call this add in will not be loaded.
        ///             The analyzer can keep the context object for its entire lifespan
        /// </remarks>
        public void Initialize(IRepositoryContext context)
        {
            if (m_Initialized)
                throw new InvalidOperationException("The add-in has already been initialized and shouldn't be re-initialized");

            m_Context = context;
            m_Controller = (RepositoryController)m_Context.RepositoryController;

            //load up our baseline configuration
            ConfigurationChanged();

            m_Initialized = true;
        }

        /// <summary>
        /// Called by Loupe to indicate the configuration of the add in has changed at runtime
        /// </summary>
        public void ConfigurationChanged()
        {
        }

        /// <summary>
        /// Called to have the session command object register all commands that it supports.
        /// </summary>
        /// <param name="controller">The User Interface Controller for the current process</param>
        /// <remarks>
        /// The controller should not be persisted or accessed between calls, it may change and
        ///             the same object may get calls from multiple controllers.
        /// </remarks>
        public void RegisterCommands(IUserInterfaceContext controller)
        {
            controller.RegisterCommand("scan", false, "Analyze Session", "Reports errors to FogBugz");
        }

        /// <summary>
        /// Called just prior to displaying the list of commands to a user on a set of sessions.
        /// </summary>
        /// <param name="controller">The User Interface Controller for the current process</param><param name="sessionSummaries">Summaries of the selected sessions.</param>
        /// <remarks>
        /// <para>
        /// Use this method to change what commands are available or what their labels should be prior to being displayed.
        /// </para>
        /// <para>
        /// The controller should not be persisted or accessed between calls, it may change and
        ///             the same object may get calls from multiple controllers.
        /// </para>
        /// </remarks>
        public void BeforeCommandsDisplay(IUserInterfaceContext controller, IList<ISessionSummary> sessionSummaries)
        {
            controller.UpdateCommand("scan", "Analyze Session", "Reports errors to FogBugz", true);
        }

        /// <summary>
        /// Apply the specified command to the provided set of sessions
        /// </summary>
        /// <param name="controller">The User Interface Controller for the current process</param><param name="commandName">The command that was requested.</param><param name="sessionSummaries">Summaries of the selected sessions.</param>
        /// <remarks>
        /// <para>
        /// Headers of all of the currently selected sessions are provided.  If the command
        ///             was configured to provide its own user interface it will be called on the main UI thread and needs to perform
        ///             its own background processing to keep the user interface responsive.  If not, it will be called
        ///             from a background thread and the user interface will be kept responsive by the framework.
        /// </para>
        /// <para>
        /// The controller should not be persisted or accessed between calls, it may change and
        ///             the same object may get calls from multiple controllers.
        /// </para>
        /// </remarks>
        public void Process(IUserInterfaceContext controller, string commandName, IList<ISessionSummary> sessionSummaries)
        {
            switch (commandName)
            {
                case "scan":
                    foreach (ISessionSummary summary in sessionSummaries)
                    {
                        // Ignore sessions that do not contain any errors.  Checking the header first saves the time to load the session data
                        if (summary.ErrorCount == 0)
                        {
                            Log.Verbose(LogCategory, "Skipping session because there are no errors to inspect", null);
                        }
                        else
                        {
                            ActionProcessSession(summary.Session());
                        }
                    }
                    break;
            }
        }

        /// <summary>
        /// Analyze the provided session.
        /// </summary>
        /// <param name="session">The session to be analyzed</param>
        /// <remarks>
        /// <para>
        /// The FogBugz configuration file is read for each session so that
        /// changes to the file can be made without having to restart Loupe.
        /// </para>
        /// </remarks>
        public void Process(ISession session)
        {
            //if we aren't supposed to run on this tier, don't.
            if (m_Context.Environment == LoupeEnvironment.Server)
            {
                if (m_Controller.HubConfiguration.EnableAutomaticAnalysis == false)
                {
                    Log.Verbose(LogCategory, "Skipping session because automatic session analysis not enabled on the Hub", null);
                }
            }
            else
            {
                if (m_Controller.UserConfiguration.EnableAutomaticAnalysis == false)
                {
                    Log.Verbose(LogCategory, "Skipping session because automatic session analysis is not enabled", null);
                }
            }

            // Ignore sessions that do not contain any errors
            if (session.ErrorCount == 0)
            {
                Log.Verbose(LogCategory, "Skipping session because there are no errors to inspect", null);
                return;
            }

            // Only process this session if there is a matching target project in FogBugz
            Mapping target = m_Controller.FindTarget(session);
            if (target == null)
            {
                Log.Verbose(LogCategory, "Skipping session because it doesn't match any target in the configuration file", null);
                return; // this could be perfectly normal
            }

            // Throws an exception if we can't connect with FogBugz
            FBApi api;
            try
            {
                api = m_Controller.GetApi();
            }
            catch(Exception ex)
            {
                Log.Error(ex, LogCategory, "Unable to analyze session because FogBugz server is not available", "When connecting to the FogBugz API an exception was thrown.  We will assume it is not available.");
                throw; //we really do want to abort out to our caller who is designed to handle failures like this.
            }

            // Process each unique error (as defined in IssuesByClass)
            Dictionary<string, ErrorInfo> errorList = new Dictionary<string, ErrorInfo>();

            foreach (ILogMessage message in session.GetMessages())
            {
                //process errors
                if (message.Severity > LogMessageSeverity.Error)
                    continue;

                ErrorInfo currentError = new ErrorInfo(message);
                ErrorInfo previousError;

                // This error is either new or another instance of a previous error
                if (errorList.TryGetValue(currentError.Fingerprint, out previousError))
                    previousError.Messages.Add(message);
                else
                    errorList.Add(currentError.Fingerprint, currentError);
            }

            foreach (KeyValuePair<string, ErrorInfo> pair in errorList)
            {
                ErrorInfo error = pair.Value;
                FogBugzCaseWriter fogBugzCase = new FogBugzCaseWriter(Log, error);
                fogBugzCase.Submit(api, target);
            }
        }

        #region Private Properties and Methods

        /// <summary>
        /// Our log interface for recording diagnostic info
        /// </summary>
        private ILog Log { get { return m_Context.Log; } }

        /// <summary>
        /// Process the provided session
        /// </summary>
        /// <param name="session"></param>
        private void ActionProcessSession(ISession session)
        {
            // Ignore sessions that do not contain any errors
            if (session.ErrorCount == 0)
            {
                Log.Verbose(LogCategory, "Skipping session because there are no errors to inspect", null);
                return;
            }

            // Only process this session if there is a matching target project in FogBugz
            Mapping target = m_Controller.FindTarget(session);
            if (target == null)
            {
                Log.Verbose(LogCategory, "Skipping session because it doesn't match any target in the configuration file", null);
                return; // this could be perfectly normal
            }

            // Throws an exception if we can't connect with FogBugz
            FBApi api;
            try
            {
                api = m_Controller.GetApi();
            }
            catch(Exception ex)
            {
                Log.Error(ex, LogCategory, "Unable to analyze session because FogBugz server is not available", "When connecting to the FogBugz API an exception was thrown.  We will assume it is not available.");
                throw; //we really do want to abort out to our caller who is designed to handle failures like this.
            }

            // Process each unique error (as defined in IssuesByClass)
            Dictionary<string, ErrorInfo> errorList = new Dictionary<string, ErrorInfo>();

            foreach (ILogMessage message in session.GetMessages())
            {
                //process errors
                if (message.Severity > LogMessageSeverity.Error)
                    continue;

                ErrorInfo currentError = new ErrorInfo(message);
                ErrorInfo previousError;

                // This error is either new or another instance of a previous error
                if (errorList.TryGetValue(currentError.Fingerprint, out previousError))
                    previousError.Messages.Add(message);
                else
                    errorList.Add(currentError.Fingerprint, currentError);
            }

            foreach (KeyValuePair<string, ErrorInfo> pair in errorList)
            {
                ErrorInfo error = pair.Value;
                FogBugzCaseWriter fogBugzCase = new FogBugzCaseWriter(Log, error);
                fogBugzCase.Submit(api, target);
            }
        }

        #endregion
    }
}