Skip to content
Snippets Groups Projects
async-arrow-functions-lexical-super-binding.js 4.83 KiB
// Reviewed
function shouldBe(expected, actual, msg) {
    if (msg === void 0)
        msg = "";
    else
        msg = " for " + msg;
    if (actual !== expected)
        throw new Error("bad value" + msg + ": " + actual + ". Expected " + expected);
}

function shouldBeAsync(expected, run, msg) {
    let actual;
    var hadError = false;
    run().then(function(value) { actual = value; },
               function(error) { hadError = true; actual = error; });
    drainMicrotasks();

    if (hadError)
        throw actual;

    shouldBe(expected, actual, msg);
}

class BaseClass {
    baseClassValue() {
        return "BaseClassValue";
    }
    get property() {
        return "test!";
    }
}

class ChildClass extends BaseClass {
    asyncSuperProp() {
        return async x => super.baseClassValue();
    }
    asyncSuperProp2() {
        return async x => { return super.baseClassValue(); }
    }
}

shouldBeAsync("BaseClassValue", new ChildClass().asyncSuperProp());
shouldBeAsync("BaseClassValue", new ChildClass().asyncSuperProp2());

class ChildClass2 extends BaseClass {
    constructor() {
        return async (self = super()) => self.baseClassValue() + ' ' + super.property;
    }
}

shouldBeAsync("BaseClassValue test!", new ChildClass2());

var error = undefined;
var value = undefined;

class A {
    constructor() {
        this._id = 'class-id';
    }
}

const childA1 = new class extends A {
  constructor() {
    var f = async (a=super()) => { return 'abc'; }
    f().then( val => {value = val; }, err => { error = err;});
  }
}

drainMicrotasks();
shouldBe(childA1._id, 'class-id');
shouldBe(value, 'abc');
shouldBe(error, undefined);

value = undefined;
error = undefined;

const childA2 = new class extends A {
  constructor() {
    var f = async (a) => { super(); return 'abc'; }
    f().then( val => {value = val; }, err => { error = err;});
  }
}

drainMicrotasks();

shouldBe(childA2._id, 'class-id');
shouldBe(value, 'abc');
shouldBe(error, undefined);

value = undefined;
error = undefined;

const childA3 = new class extends A {
    constructor() {
        var f = async (a = super()) => { super(); return 'abc'; }
        f().then( val => {value = val; }, err => { error = err;});
    }
}

drainMicrotasks();

shouldBe(childA3._id, 'class-id');
shouldBe(value, undefined);
shouldBe(error.toString(), 'ReferenceError: \'super()\' can\'t be called more than once in a constructor.');


let childA4;
let catchError;
error = undefined; 
try {
    childA4 = new class extends A {
        constructor() {
            var f = async (a) => { await 'await value'; super(); return 'abc'; }
            f().then(val => { value = val; }, err => { error = err; });
        }
    }
} catch (err) {
    catchError = err;  
}

drainMicrotasks();

shouldBe(childA4, undefined);
shouldBe(value, 'abc');
shouldBe(error, undefined);
shouldBe(catchError.toString(), 'ReferenceError: Cannot access uninitialized variable.');

catchError = undefined;
error = undefined; 
value = undefined;

const childA5 = new class extends A {
    constructor() {
        var f = async (a) => { super(); await 'await value'; return 'abc'; }
        f().then(val => { value = val; }, err => { error = err; });
    }
}

drainMicrotasks();

shouldBe(childA5._id, 'class-id');
shouldBe(value, 'abc');
shouldBe(error, undefined);
shouldBe(catchError, undefined);

function checkClass(classSource) {
    let base1 = undefined;
    let error = undefined; 
    let value = undefined;
    let catchError = undefined;
    try {
        base1 = eval(classSource);

        drainMicrotasks();
    } catch (err) {
        catchError = err;  
    }

    shouldBe(base1, undefined);
    shouldBe(value, undefined);
    shouldBe(error, undefined);
    shouldBe(catchError.toString(), 'SyntaxError: super is not valid in this context.');
}

checkClass(`new class {
    constructor() {
        var f = async (a) => { super(); return 'abc'; }
        f().then(val => { value = val; }, err => { error = err; });
    }
}`);

checkClass(`new class {
    constructor() {
        var f = async (a) => { await 'p'; super(); return 'abc'; }
        f().then(val => { value = val; }, err => { error = err; });
    }
}`);

checkClass(`new class {
    constructor() {
        var f = async (a) => { super(); await 'p'; return 'abc'; }
        f().then(val => { value = val; }, err => { error = err; });
    }
}`);


checkClass(`new class extends A {
    method() {
        var f = async (a) => { super(); return 'abc'; }
        f().then(val => { value = val; }, err => { error = err; });
    }
}`);

checkClass(`new class extends A {
    get prop() {
        var f = async (a) => { super(); return 'abc'; }
        f().then(val => { value = val; }, err => { error = err; });
    }
}`);

checkClass(`new class extends A {
    set prop(_value) {
        var f = async (a) => { super(); return 'abc'; }
        f().then(val => { value = val; }, err => { error = err; });
    }
}`);