Index: src/builtins/builtins-promise.cc |
diff --git a/src/builtins/builtins-promise.cc b/src/builtins/builtins-promise.cc |
index 345625d6d098d09eeb7686658803da7bdbcfa046..0945303bad428b34ea494a0866156bef76c7f480 100644 |
--- a/src/builtins/builtins-promise.cc |
+++ b/src/builtins/builtins-promise.cc |
@@ -605,6 +605,75 @@ Node* PromiseBuiltinsAssembler::InternalPerformPromiseThen( |
return deferred_promise; |
} |
+void PromiseBuiltinsAssembler::FastPerformPromiseThen(Node* context, |
+ Node* promise, |
+ Node* on_resolve, |
+ Node* on_reject, |
+ Node* result_promise) { |
+ CSA_SLOW_ASSERT(this, HasInstanceType(promise, JS_PROMISE_TYPE)); |
+ if (result_promise != nullptr) { |
+ CSA_SLOW_ASSERT(this, HasInstanceType(result_promise, JS_PROMISE_TYPE)); |
+ } |
+ |
+ if (on_resolve == nullptr) { |
+ on_resolve = LoadContextElement(LoadNativeContext(context), |
+ Context::PROMISE_ID_RESOLVE_HANDLER_INDEX); |
+ } else if (on_reject == nullptr) { |
+ on_reject = LoadContextElement(LoadNativeContext(context), |
+ Context::PROMISE_ID_REJECT_HANDLER_INDEX); |
+ } |
+ CSA_SLOW_ASSERT(this, IsJSFunction(on_resolve)); |
+ CSA_SLOW_ASSERT(this, IsJSFunction(on_reject)); |
+ |
+ Label if_ispending(this), if_isfulfilled(this), merge(this); |
+ |
+ Node* const status = LoadObjectField(promise, JSPromise::kStatusOffset); |
+ Branch(SmiEqual(status, SmiConstant(v8::Promise::kPending)), &if_ispending, |
+ &if_isfulfilled); |
+ |
+ Bind(&if_ispending); |
+ { |
+ // Assert: There are no callbacks attached. |
+ CSA_SLOW_ASSERT(this, IsUndefined(LoadObjectField( |
+ promise, JSPromise::kDeferredPromiseOffset))); |
+ |
+ // Store callbacks directly in the slots. |
+ StoreObjectField(promise, JSPromise::kDeferredPromiseOffset, |
+ result_promise ? result_promise : UndefinedConstant()); |
+ StoreObjectField(promise, JSPromise::kFulfillReactionsOffset, on_resolve); |
+ StoreObjectField(promise, JSPromise::kRejectReactionsOffset, on_reject); |
+ Goto(&merge); |
+ } |
+ |
+ Bind(&if_isfulfilled); |
+ { |
+ Node* const handler = |
+ Select(SmiEqual(status, SmiConstant(v8::Promise::kFulfilled)), |
+ [&]() -> Node* { return on_resolve; }, |
+ [&]() -> Node* { |
+ Label done(this); |
+ GotoIf(PromiseHasHandler(promise), &done); |
+ // TODO(gsathya): Fold these runtime calls and move to TF. |
+ CallRuntime(Runtime::kPromiseRevokeReject, context, promise); |
+ Goto(&done); |
+ Bind(&done); |
+ return on_reject; |
+ }, |
+ MachineRepresentation::kTagged); |
+ |
+ Node* const result = LoadObjectField(promise, JSPromise::kResultOffset); |
+ Node* info = AllocatePromiseReactionJobInfo( |
+ promise, result, handler, |
+ result_promise ? result_promise : UndefinedConstant(), |
+ UndefinedConstant(), UndefinedConstant(), context); |
+ // TODO(gsathya): Move this to TF |
+ CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, info, status); |
+ Goto(&merge); |
+ } |
+ |
+ Bind(&merge); |
+} |
+ |
// Promise fast path implementations rely on unmodified JSPromise instances. |
// We use a fairly coarse granularity for this and simply check whether both |
// the promise itself is unmodified (i.e. its map has not changed) and its |
@@ -1076,7 +1145,7 @@ TF_BUILTIN(PerformPromiseThen, PromiseBuiltinsAssembler) { |
Node* const context = Parameter(7); |
// No deferred_on_resolve/deferred_on_reject because this is just an |
- // internal promise created by async-await. |
+ // internal promise created by async-await / async-iteration. |
Node* const result = InternalPerformPromiseThen( |
context, promise, on_resolve, on_reject, deferred_promise, |
UndefinedConstant(), UndefinedConstant()); |