Index: src/builtins/builtins-async-function.cc |
diff --git a/src/builtins/builtins-async-function.cc b/src/builtins/builtins-async-function.cc |
index a03c976ef0e5fc1fa40460ba85123672af7151f7..3339a25046e521c990739c7b61b81b64fa42d6f1 100644 |
--- a/src/builtins/builtins-async-function.cc |
+++ b/src/builtins/builtins-async-function.cc |
@@ -2,7 +2,7 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-#include "src/builtins/builtins-promise.h" |
+#include "src/builtins/builtins-async.h" |
#include "src/builtins/builtins-utils.h" |
#include "src/builtins/builtins.h" |
#include "src/code-stub-assembler.h" |
@@ -14,12 +14,151 @@ typedef compiler::Node Node; |
typedef CodeStubAssembler::ParameterMode ParameterMode; |
typedef compiler::CodeAssemblerState CodeAssemblerState; |
-class AsyncFunctionBuiltinsAssembler : public PromiseBuiltinsAssembler { |
+class AsyncFunctionBuiltinsAssembler : public AsyncBuiltinsAssembler { |
public: |
explicit AsyncFunctionBuiltinsAssembler(CodeAssemblerState* state) |
- : PromiseBuiltinsAssembler(state) {} |
+ : AsyncBuiltinsAssembler(state) {} |
+ |
+ protected: |
+ void AsyncFunctionAwait(Node* const context, Node* const generator, |
+ Node* const awaited, Node* const outer_promise, |
+ const bool is_predicted_as_caught); |
+ |
+ void AsyncFunctionAwaitResumeClosure( |
+ Node* const context, Node* const sent_value, |
+ JSGeneratorObject::ResumeMode resume_mode); |
+}; |
+ |
+namespace { |
+ |
+// Describe fields of Context associated with AsyncFunctionAwait resume |
+// closures. |
+// TODO(jgruber): Refactor to reuse code for upcoming async-generators. |
+class AwaitContext { |
+ public: |
+ enum Fields { kGeneratorSlot = Context::MIN_CONTEXT_SLOTS, kLength }; |
}; |
+} // anonymous namespace |
+ |
+void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwaitResumeClosure( |
+ Node* context, Node* sent_value, |
+ JSGeneratorObject::ResumeMode resume_mode) { |
+ DCHECK(resume_mode == JSGeneratorObject::kNext || |
+ resume_mode == JSGeneratorObject::kThrow); |
+ |
+ Node* const generator = |
+ LoadContextElement(context, AwaitContext::kGeneratorSlot); |
+ CSA_SLOW_ASSERT(this, HasInstanceType(generator, JS_GENERATOR_OBJECT_TYPE)); |
+ |
+ // Inline version of GeneratorPrototypeNext / GeneratorPrototypeReturn with |
+ // unnecessary runtime checks removed. |
+ // TODO(jgruber): Refactor to reuse code from builtins-generator.cc. |
+ |
+ // Ensure that the generator is neither closed nor running. |
+ CSA_SLOW_ASSERT( |
+ this, |
+ SmiGreaterThan( |
+ LoadObjectField(generator, JSGeneratorObject::kContinuationOffset), |
+ SmiConstant(JSGeneratorObject::kGeneratorClosed))); |
+ |
+ // Resume the {receiver} using our trampoline. |
+ Callable callable = CodeFactory::ResumeGenerator(isolate()); |
+ CallStub(callable, context, sent_value, generator, SmiConstant(resume_mode)); |
+ |
+ // The resulting Promise is a throwaway, so it doesn't matter what it |
+ // resolves to. What is important is that we don't end up keeping the |
+ // whole chain of intermediate Promises alive by returning the return value |
+ // of ResumeGenerator, as that would create a memory leak. |
+} |
+ |
+TF_BUILTIN(AsyncFunctionAwaitRejectClosure, AsyncFunctionBuiltinsAssembler) { |
+ CSA_ASSERT_JS_ARGC_EQ(this, 1); |
+ Node* const sentError = Parameter(1); |
+ Node* const context = Parameter(4); |
+ |
+ AsyncFunctionAwaitResumeClosure(context, sentError, |
+ JSGeneratorObject::kThrow); |
+ Return(UndefinedConstant()); |
+} |
+ |
+TF_BUILTIN(AsyncFunctionAwaitResolveClosure, AsyncFunctionBuiltinsAssembler) { |
+ CSA_ASSERT_JS_ARGC_EQ(this, 1); |
+ Node* const sentValue = Parameter(1); |
+ Node* const context = Parameter(4); |
+ |
+ AsyncFunctionAwaitResumeClosure(context, sentValue, JSGeneratorObject::kNext); |
+ Return(UndefinedConstant()); |
+} |
+ |
+// ES#abstract-ops-async-function-await |
+// AsyncFunctionAwait ( value ) |
+// Shared logic for the core of await. The parser desugars |
+// await awaited |
+// into |
+// yield AsyncFunctionAwait{Caught,Uncaught}(.generator, awaited, .promise) |
+// The 'awaited' parameter is the value; the generator stands in |
+// for the asyncContext, and .promise is the larger promise under |
+// construction by the enclosing async function. |
+void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwait( |
+ Node* const context, Node* const generator, Node* const awaited, |
+ Node* const outer_promise, const bool is_predicted_as_caught) { |
+ CSA_SLOW_ASSERT(this, HasInstanceType(generator, JS_GENERATOR_OBJECT_TYPE)); |
+ CSA_SLOW_ASSERT(this, HasInstanceType(outer_promise, JS_PROMISE_TYPE)); |
+ |
+ NodeGenerator1 create_closure_context = [&](Node* native_context) -> Node* { |
+ Node* const context = |
+ CreatePromiseContext(native_context, AwaitContext::kLength); |
+ StoreContextElementNoWriteBarrier(context, AwaitContext::kGeneratorSlot, |
+ generator); |
+ return context; |
+ }; |
+ |
+ // TODO(jgruber): AsyncBuiltinsAssembler::Await currently does not reuse |
+ // the awaited promise if it is already a promise. Reuse is non-spec compliant |
+ // but part of our old behavior gives us a couple of percent |
+ // performance boost. |
+ // TODO(jgruber): Use a faster specialized version of |
+ // InternalPerformPromiseThen. |
+ |
+ Node* const result = Await( |
+ context, generator, awaited, outer_promise, create_closure_context, |
+ Context::ASYNC_FUNCTION_AWAIT_RESOLVE_SHARED_FUN, |
+ Context::ASYNC_FUNCTION_AWAIT_REJECT_SHARED_FUN, is_predicted_as_caught); |
+ |
+ Return(result); |
+} |
+ |
+// Called by the parser from the desugaring of 'await' when catch |
+// prediction indicates that there is a locally surrounding catch block. |
+TF_BUILTIN(AsyncFunctionAwaitCaught, AsyncFunctionBuiltinsAssembler) { |
+ CSA_ASSERT_JS_ARGC_EQ(this, 3); |
+ Node* const generator = Parameter(1); |
+ Node* const awaited = Parameter(2); |
+ Node* const outer_promise = Parameter(3); |
+ Node* const context = Parameter(6); |
+ |
+ static const bool kIsPredictedAsCaught = true; |
+ |
+ AsyncFunctionAwait(context, generator, awaited, outer_promise, |
+ kIsPredictedAsCaught); |
+} |
+ |
+// Called by the parser from the desugaring of 'await' when catch |
+// prediction indicates no locally surrounding catch block. |
+TF_BUILTIN(AsyncFunctionAwaitUncaught, AsyncFunctionBuiltinsAssembler) { |
+ CSA_ASSERT_JS_ARGC_EQ(this, 3); |
+ Node* const generator = Parameter(1); |
+ Node* const awaited = Parameter(2); |
+ Node* const outer_promise = Parameter(3); |
+ Node* const context = Parameter(6); |
+ |
+ static const bool kIsPredictedAsCaught = false; |
+ |
+ AsyncFunctionAwait(context, generator, awaited, outer_promise, |
+ kIsPredictedAsCaught); |
+} |
+ |
TF_BUILTIN(AsyncFunctionPromiseCreate, AsyncFunctionBuiltinsAssembler) { |
CSA_ASSERT_JS_ARGC_EQ(this, 0); |
Node* const context = Parameter(3); |