ASSERTION FAILED: generator.isConstructor() || generator.derivedContextType() ==...
authorgskachkov@gmail.com <gskachkov@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 30 May 2017 21:29:37 +0000 (21:29 +0000)
committergskachkov@gmail.com <gskachkov@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 30 May 2017 21:29:37 +0000 (21:29 +0000)
https://bugs.webkit.org/show_bug.cgi?id=171274

Reviewed by Saam Barati.

JSTests:

* stress/async-arrow-functions-lexical-binding-in-class.js:
(shouldBeAsync):
(shouldBeAsyncAndStoreBind):
(promise.new.Promise):
(ChildClass4):
(ChildClass4.prototype.classValue):
(ChildClass4.prototype.get classProperty):
* stress/async-arrow-functions-lexical-super-binding.js:
(A):
(const.childA1.new.prototype.var.f.async):
(const.childA1.new.var):
(const.childA1.new):
(const.childA2.new.prototype.var.f.async):
(const.childA2.new.var):
(const.childA2.new):
(const.childA3.new.prototype.var.f.async):
(const.childA3.new.var):
(const.childA3.new):
(try.childA4.new.prototype.var.f.async):
(try.childA4.new.var):
(try.childA4.new):
(catch):
(const.childA5.new.prototype.var.f.async):
(const.childA5.new.var):
(const.childA5.new):
(checkClass):
(checkClass.new.prototype.var.f.async):
(checkClass.new.var):
(checkClass.new):
(checkClass.new.prototype.method):
(checkClass.new.prototype.prop):

Source/JavaScriptCore:

Current patch allow to use async arrow function within constructor,
and allow to access to `this`. Current patch force load 'this' from
virtual scope each time as we access to `this` in async arrow function
within constructor it is neccessary because async function can be
suspended and `superCall` can be called and async function resumed.

* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitPutGeneratorFields):
(JSC::BytecodeGenerator::ensureThis):
* bytecompiler/BytecodeGenerator.h:
(JSC::BytecodeGenerator::makeFunction):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@217577 268f45cc-cd09-0410-ab3c-d52691b4dbfc

JSTests/ChangeLog
JSTests/stress/async-arrow-functions-lexical-binding-in-class.js
JSTests/stress/async-arrow-functions-lexical-super-binding.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h

index f641d57..5b19edd 100644 (file)
@@ -1,3 +1,42 @@
+2017-05-30  Oleksandr Skachkov  <gskachkov@gmail.com>
+
+        ASSERTION FAILED: generator.isConstructor() || generator.derivedContextType() == DerivedContextType::DerivedConstructorContext
+        https://bugs.webkit.org/show_bug.cgi?id=171274
+
+        Reviewed by Saam Barati.
+
+        * stress/async-arrow-functions-lexical-binding-in-class.js:
+        (shouldBeAsync):
+        (shouldBeAsyncAndStoreBind):
+        (promise.new.Promise):
+        (ChildClass4):
+        (ChildClass4.prototype.classValue):
+        (ChildClass4.prototype.get classProperty):
+        * stress/async-arrow-functions-lexical-super-binding.js:
+        (A):
+        (const.childA1.new.prototype.var.f.async):
+        (const.childA1.new.var):
+        (const.childA1.new):
+        (const.childA2.new.prototype.var.f.async):
+        (const.childA2.new.var):
+        (const.childA2.new):
+        (const.childA3.new.prototype.var.f.async):
+        (const.childA3.new.var):
+        (const.childA3.new):
+        (try.childA4.new.prototype.var.f.async):
+        (try.childA4.new.var):
+        (try.childA4.new):
+        (catch):
+        (const.childA5.new.prototype.var.f.async):
+        (const.childA5.new.var):
+        (const.childA5.new):
+        (checkClass):
+        (checkClass.new.prototype.var.f.async):
+        (checkClass.new.var):
+        (checkClass.new):
+        (checkClass.new.prototype.method):
+        (checkClass.new.prototype.prop):
+
 2017-05-27  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [JSC] Map and Set constructors should have fast path for cloning
index 9bec9ac..a4871d8 100644 (file)
@@ -7,7 +7,7 @@ function shouldBe(expected, actual, msg) {
         throw new Error("bad value" + msg + ": " + actual + ". Expected " + expected);
 }
 
-function shouldBeAsync(expected, run, msg) {
+function shouldBeAsync(expected, run) {
     let actual;
     var hadError = false;
     run().then(function(value) { actual = value; },
@@ -17,10 +17,10 @@ function shouldBeAsync(expected, run, msg) {
     if (hadError)
         throw actual;
 
-    shouldBe(expected, actual, msg);
+    shouldBe(expected, actual);
 }
 
-class BaseWrongClassClass {
+class BaseWrongClass {
     baseClassValue() {
         return "wrong #1";
     }
@@ -29,17 +29,17 @@ class BaseWrongClassClass {
     }
 }
 
-function shouldBeAsyncAndStoreBind(expected, run, msg) {
-    shouldBeAsync(expected, run, msg);
-    shouldBeAsync(expected, run.bind({}), msg);
-    shouldBeAsync(expected, run.bind(1), msg);
-    shouldBeAsync(expected, run.bind(undefined), msg);
+function shouldBeAsyncAndStoreBind(expected, run) {
+    shouldBeAsync(expected, run);
+    shouldBeAsync(expected, run.bind({}));
+    shouldBeAsync(expected, run.bind(1));
+    shouldBeAsync(expected, run.bind(undefined));
     const obj = { 
         property : 'wrong value #1', 
         baseClassValue : () => 'worng value #2'
     };
-    shouldBeAsync(expected, run.bind(obj), msg);
-    shouldBeAsync(expected, run.bind(new BaseWrongClassClass()), msg);
+    shouldBeAsync(expected, run.bind(obj));
+    shouldBeAsync(expected, run.bind(new BaseWrongClass()));
 }
 
 class BaseClass {
@@ -180,3 +180,85 @@ class ChildClass3 extends BaseClass {
 }
 
 shouldBeAsyncAndStoreBind("classValue classProperty", new ChildClass3());
+
+var promiseHolder = {};
+var promise = new Promise((resolve, reject) => {
+    promiseHolder.resolve = resolve;
+    promiseHolder.reject = reject;
+});
+
+class ChildClass4 extends BaseClass {
+    constructor() {
+        var arr = async () => {
+            var doSomeStaff = () => {};
+            doSomeStaff();
+            await promise;
+            return this.classValue() + ' ' + this.classProperty;
+        };
+        arr();
+        super();
+        this.internalValue = 'internalValue';
+        return async () => {
+            await 'await';
+            promiseHolder.resolve();
+            return this.classValue() + ' ' + this.classProperty;;
+        };
+    }
+    classValue() {
+        return "classValue";
+    }
+    get classProperty() {
+        return "classProperty";
+    }
+}
+
+shouldBeAsyncAndStoreBind("classValue classProperty", new ChildClass4());
+
+class ChildClass5 extends BaseClass {
+    constructor(result) {
+        const arr = async () => this.id;
+        arr().then(()=>{}, e => { result.error = e; });
+    }
+}
+
+class ChildClass6 extends BaseClass {
+    constructor(result) {
+        const arr = async () => { 
+            let z = this.id;
+        };
+        arr().then(()=>{}, e => { result.error = e; });
+        super();
+    }
+}
+
+class ChildClass7 extends BaseClass {
+    constructor(result) {
+        const arr = async () => this.id;
+        arr().then(()=>{}, e => { result.error = e; });
+        super();
+    }
+}
+
+class ChildClass8 extends BaseClass {
+    constructor(result) {
+        const arr = async () => { let i  = this.id; super(); };
+        arr().then(()=>{}, e => { result.error = e; });
+    }
+}
+
+function checkTDZDuringCreate(klass) {
+    let asyncError = {};
+    try {
+        var c = new klass(asyncError);
+    } catch(e) {
+        // We do not care about this error
+    }
+    drainMicrotasks();
+    const error = asyncError.error instanceof ReferenceError && asyncError.error.toString() === 'ReferenceError: Cannot access uninitialized variable.';
+    if (!error) throw new Error('TDZ error is expected, but appeared:' + asyncError.error);
+}
+
+checkTDZDuringCreate(ChildClass5);
+checkTDZDuringCreate(ChildClass6);
+checkTDZDuringCreate(ChildClass7);
+checkTDZDuringCreate(ChildClass8);
index a000463..abfde79 100644 (file)
@@ -48,3 +48,159 @@ class ChildClass2 extends BaseClass {
 }
 
 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; });
+    }
+}`);
\ No newline at end of file
index 4116933..78aed48 100644 (file)
@@ -1,3 +1,22 @@
+2017-05-30  Oleksandr Skachkov  <gskachkov@gmail.com>
+
+        ASSERTION FAILED: generator.isConstructor() || generator.derivedContextType() == DerivedContextType::DerivedConstructorContext
+        https://bugs.webkit.org/show_bug.cgi?id=171274
+
+        Reviewed by Saam Barati.
+
+        Current patch allow to use async arrow function within constructor,
+        and allow to access to `this`. Current patch force load 'this' from 
+        virtual scope each time as we access to `this` in async arrow function
+        within constructor it is neccessary because async function can be 
+        suspended and `superCall` can be called and async function resumed. 
+   
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::emitPutGeneratorFields):
+        (JSC::BytecodeGenerator::ensureThis):
+        * bytecompiler/BytecodeGenerator.h:
+        (JSC::BytecodeGenerator::makeFunction):
+
 2017-05-30  Ali Juma  <ajuma@chromium.org>
 
         [CredentialManagement] Incorporate IDL updates from latest spec
index d1fd681..e77031b 100644 (file)
@@ -2825,7 +2825,10 @@ void BytecodeGenerator::emitPutGeneratorFields(RegisterID* nextFunction)
 
     emitDirectPutById(m_generatorRegister, propertyNames().builtinNames().generatorNextPrivateName(), nextFunction, PropertyNode::KnownDirect);
 
-    emitDirectPutById(m_generatorRegister, propertyNames().builtinNames().generatorThisPrivateName(), &m_thisRegister, PropertyNode::KnownDirect);
+    // We do not store 'this' in arrow function within constructor,
+    // because it might be not initialized, if super is called later.
+    if (!(isDerivedConstructorContext() && m_codeBlock->parseMode() == SourceParseMode::AsyncArrowFunctionMode))
+        emitDirectPutById(m_generatorRegister, propertyNames().builtinNames().generatorThisPrivateName(), &m_thisRegister, PropertyNode::KnownDirect);
 
     emitDirectPutById(m_generatorRegister, propertyNames().builtinNames().generatorStatePrivateName(), emitLoad(nullptr, jsNumber(0)), PropertyNode::KnownDirect);
 
@@ -4591,11 +4594,12 @@ RegisterID* BytecodeGenerator::emitLoadDerivedConstructorFromArrowFunctionLexica
 
 RegisterID* BytecodeGenerator::ensureThis()
 {
-    if ((constructorKind() == ConstructorKind::Extends || isDerivedConstructorContext())  && needsToUpdateArrowFunctionContext() && isSuperCallUsedInInnerArrowFunction())
-        emitLoadThisFromArrowFunctionLexicalEnvironment();
+    if (constructorKind() == ConstructorKind::Extends || isDerivedConstructorContext()) {
+        if ((needsToUpdateArrowFunctionContext() && isSuperCallUsedInInnerArrowFunction()) || m_codeBlock->parseMode() == SourceParseMode::AsyncArrowFunctionBodyMode)
+            emitLoadThisFromArrowFunctionLexicalEnvironment();
 
-    if (constructorKind() == ConstructorKind::Extends || isDerivedConstructorContext())
         emitTDZCheck(thisRegister());
+    }
 
     return thisRegister();
 }
index 9ae8685..df9a93e 100644 (file)
@@ -1033,7 +1033,7 @@ namespace JSC {
         {
             DerivedContextType newDerivedContextType = DerivedContextType::None;
 
-            if (SourceParseModeSet(SourceParseMode::ArrowFunctionMode, SourceParseMode::AsyncArrowFunctionMode).contains(metadata->parseMode())) {
+            if (SourceParseModeSet(SourceParseMode::ArrowFunctionMode, SourceParseMode::AsyncArrowFunctionMode, SourceParseMode::AsyncArrowFunctionBodyMode).contains(metadata->parseMode())) {
                 if (constructorKind() == ConstructorKind::Extends || isDerivedConstructorContext())
                     newDerivedContextType = DerivedContextType::DerivedConstructorContext;
                 else if (m_codeBlock->isClassContext() || isDerivedClassContext())