"this" missing after await in async arrow function
authorgskachkov@gmail.com <gskachkov@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 19 Jan 2017 17:10:31 +0000 (17:10 +0000)
committergskachkov@gmail.com <gskachkov@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 19 Jan 2017 17:10:31 +0000 (17:10 +0000)
https://bugs.webkit.org/show_bug.cgi?id=166919

Reviewed by Saam Barati.

Source/JavaScriptCore:

This patch fixed issue in async arrow function. Issue appears because in arrow
function _this_ is loaded from arrow function virtual scope.
Async arrow function can be suspended and when resuming should be used _this_ from
virtual scope, to allow this we load _this_ from virtual scope before store it to
generator.generatorThis property

* bytecompiler/NodesCodegen.cpp:
(JSC::FunctionNode::emitBytecode):

JSTests:

* stress/async-arrow-functions-lexical-binding-in-class.js:
(ChildClass.prototype.asyncThisPropWithAwaitBody):
(ChildClass.prototype.asyncThisPropInEvalWithAwaitBody):
(ChildClass.prototype.asyncThisValueBodyWithAwait):
(ChildClass.prototype.asyncThisValueInEvalWithAwaitBody):
(ChildClass):
(ChildClass3):
(ChildClass3.prototype.classValue):
(ChildClass3.prototype.get classProperty):
* stress/async-arrow-functions-lexical-new.target-binding.js:
(C2WithAwait):

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

JSTests/ChangeLog
JSTests/stress/async-arrow-functions-lexical-binding-in-class.js
JSTests/stress/async-arrow-functions-lexical-new.target-binding.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp

index de9ae705549b7f5bb241c84df6161b7349e10712..9867bf87d6e53a1e73c1dcf8ac84127479b821a0 100644 (file)
@@ -1,3 +1,22 @@
+2017-01-19  Skachkov Oleksandr  <gskachkov@gmail.com>
+
+        "this" missing after await in async arrow function
+        https://bugs.webkit.org/show_bug.cgi?id=166919
+
+        Reviewed by Saam Barati.
+
+        * stress/async-arrow-functions-lexical-binding-in-class.js:
+        (ChildClass.prototype.asyncThisPropWithAwaitBody):
+        (ChildClass.prototype.asyncThisPropInEvalWithAwaitBody):
+        (ChildClass.prototype.asyncThisValueBodyWithAwait):
+        (ChildClass.prototype.asyncThisValueInEvalWithAwaitBody):
+        (ChildClass):
+        (ChildClass3):
+        (ChildClass3.prototype.classValue):
+        (ChildClass3.prototype.get classProperty):
+        * stress/async-arrow-functions-lexical-new.target-binding.js:
+        (C2WithAwait):
+
 2017-01-16  Filip Pizlo  <fpizlo@apple.com>
 
         Make opaque root scanning truly constraint-based
index 16fc0dfd5509e360d9fd1f39070f122548b11098..9bec9ace9679510160bcba7b8b220c487bc42d14 100644 (file)
@@ -20,6 +20,28 @@ function shouldBeAsync(expected, run, msg) {
     shouldBe(expected, actual, msg);
 }
 
+class BaseWrongClassClass {
+    baseClassValue() {
+        return "wrong #1";
+    }
+    get property() {
+        return "wrong #2";
+    }
+}
+
+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);
+    const obj = { 
+        property : 'wrong value #1', 
+        baseClassValue : () => 'worng value #2'
+    };
+    shouldBeAsync(expected, run.bind(obj), msg);
+    shouldBeAsync(expected, run.bind(new BaseWrongClassClass()), msg);
+}
+
 class BaseClass {
     baseClassValue() {
         return "BaseClassValue";
@@ -52,46 +74,103 @@ class ChildClass extends BaseClass {
     asyncThisPropBody() {
         return async x => { return this.classProperty; };
     }
+    asyncThisPropWithAwaitBody() {
+        return async x => { 
+            var self = this.classProperty; 
+            self = await 'abc';  
+            return this.classProperty; 
+        };
+    }
     asyncThisPropInEvalExp() {
         return async x => eval('this.classProperty');
     }
     asyncThisPropInEvalBody() {
         return async x => { return eval('this.classProperty'); };
     }
+    asyncThisPropInEvalWithAwaitBody() {
+        return async x => { 
+            var self = eval('this.classProperty');
+            await 'abc';
+            return eval('this.classProperty'); 
+        };
+    }
     asyncThisValueExp() {
         return async x => this.classValue();
     }
     asyncThisValueBody() {
         return async x => { return this.classValue(); };
     }
+    asyncThisValueBodyWithAwait() {
+        return async x => { 
+            var self = this.classValue();
+            await 'self'; 
+            return this.classValue(); 
+        };
+    }
     asyncThisValueInEvalExp() {
         return async x => eval('this.classValue()');
     }
     asyncThisValueInEvalBody() {
         return async x => { return eval('this.classValue()'); };
     }
+    asyncThisValueInEvalWithAwaitBody() {
+        return async x => { 
+            var self = eval('this.classValue()');
+            await 'self'; 
+            return eval('this.classValue()'); 
+        };
+    }
 }
 
-shouldBeAsync("value", new ChildClass().asyncValueExp());
-shouldBeAsync("value", new ChildClass().asyncValueBody());
+shouldBeAsyncAndStoreBind("value", new ChildClass().asyncValueExp());
+shouldBeAsyncAndStoreBind("value", new ChildClass().asyncValueBody());
 
-shouldBeAsync("classProperty", new ChildClass().asyncThisPropExp());
-shouldBeAsync("classProperty", new ChildClass().asyncThisPropBody());
+shouldBeAsyncAndStoreBind("classProperty", new ChildClass().asyncThisPropExp());
+shouldBeAsyncAndStoreBind("classProperty", new ChildClass().asyncThisPropBody());
+shouldBeAsyncAndStoreBind("classProperty", new ChildClass().asyncThisPropWithAwaitBody());
+shouldBeAsyncAndStoreBind("classProperty", new ChildClass().asyncThisPropWithAwaitBody());
 
-shouldBeAsync("classProperty", new ChildClass().asyncThisPropInEvalExp());
-shouldBeAsync("classProperty", new ChildClass().asyncThisPropInEvalBody());
+shouldBeAsyncAndStoreBind("classProperty", new ChildClass().asyncThisPropInEvalExp());
+shouldBeAsyncAndStoreBind("classProperty", new ChildClass().asyncThisPropInEvalBody());
+shouldBeAsyncAndStoreBind("classProperty", new ChildClass().asyncThisPropInEvalWithAwaitBody());
 
-shouldBeAsync("classValue", new ChildClass().asyncThisValueExp());
-shouldBeAsync("classValue", new ChildClass().asyncThisValueBody());
+shouldBeAsyncAndStoreBind("classValue", new ChildClass().asyncThisValueExp());
+shouldBeAsyncAndStoreBind("classValue", new ChildClass().asyncThisValueBody());
+shouldBeAsyncAndStoreBind("classValue", new ChildClass().asyncThisValueBodyWithAwait());
 
-shouldBeAsync("classValue", new ChildClass().asyncThisValueInEvalExp());
-shouldBeAsync("classValue", new ChildClass().asyncThisValueInEvalBody());
+shouldBeAsyncAndStoreBind("classValue", new ChildClass().asyncThisValueInEvalExp());
+shouldBeAsyncAndStoreBind("classValue", new ChildClass().asyncThisValueInEvalBody());
+shouldBeAsyncAndStoreBind("classValue", new ChildClass().asyncThisValueInEvalWithAwaitBody());
 
 class ChildClass2 extends BaseClass {
     constructor() {
         super();
+        this.value = 'internalValue';
         return async () => this.classValue() + ' ' + this.classProperty;
     }
+    classStaticValue() {
+        return "classStaticValue";
+    }
+    classValue() {
+        return this.value;
+    }
+    get classProperty() {
+        return "classProperty";
+    }
+}
+
+shouldBeAsyncAndStoreBind("internalValue classProperty", new ChildClass2());
+
+class ChildClass3 extends BaseClass {
+    constructor() {
+        super();
+        this.internalValue = 'internalValue';
+        return async () => {
+            var self = this.classValue() + ' ' + this.classProperty;
+            await 'self';
+            return this.classValue() + ' ' + this.classProperty;
+        }
+    }
     classValue() {
         return "classValue";
     }
@@ -100,4 +179,4 @@ class ChildClass2 extends BaseClass {
     }
 }
 
-shouldBeAsync("classValue classProperty", new ChildClass2());
+shouldBeAsyncAndStoreBind("classValue classProperty", new ChildClass3());
index 36df0a31106e3bcbedbfaf58a1c3935312458c4d..e0e9d83c9f91dc9f7c195551178477192b6d8101 100644 (file)
@@ -43,10 +43,19 @@ function C2() {
     return async () => { return await new.target };
 }
 
+function C2WithAwait() {
+    return async () => { 
+        var self = new.target; await new.target;
+        return new.target;
+    }
+}
+
 shouldBeAsync(C1, new C1());
 shouldBeAsync(undefined, C1());
 shouldBeAsync(C2, new C2());
 shouldBeAsync(undefined, C2());
+shouldBeAsync(C2WithAwait, new C2WithAwait());
+shouldBeAsync(undefined, C2WithAwait());
 
 shouldThrowAsync(async () => await new.target, ReferenceError);
 shouldThrowAsync(async () => { return await new.target; }, ReferenceError);
index 1345c6afb485faa31b1ec4abcda010c97c4d613a..c87c07ab53a3ef7ebd17215ea57b0dad41762266 100644 (file)
@@ -1,3 +1,19 @@
+2017-01-19  Skachkov Oleksandr  <gskachkov@gmail.com>
+
+        "this" missing after await in async arrow function
+        https://bugs.webkit.org/show_bug.cgi?id=166919
+
+        Reviewed by NOBODY Saam Barati.
+
+        This patch fixed issue in async arrow function. Issue appears because in arrow
+        function _this_ is loaded from arrow function virtual scope. 
+        Async arrow function can be suspended and when resuming should be used _this_ from 
+        virtual scope, to allow this we load _this_ from virtual scope before store it to 
+        generator.generatorThis property 
+
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::FunctionNode::emitBytecode):
+
 2017-01-18  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [B3] B3 strength reduction could encounter Value without owner in PureCSE
index dfe5b91cc5a8a1b97520a9ba9750596252924c4d..efbded292fb62a819c30803a89788a920d30aad8 100644 (file)
@@ -3506,6 +3506,9 @@ void FunctionNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
             RefPtr<RegisterID> homeObject = emitHomeObjectForCallee(generator);
             emitPutHomeObject(generator, next.get(), homeObject.get());
         }
+        
+        if (generator.parseMode() == SourceParseMode::AsyncArrowFunctionMode && generator.isThisUsedInInnerArrowFunction())
+            generator.emitLoadThisFromArrowFunctionLexicalEnvironment();
 
         generator.emitPutGeneratorFields(next.get());