Index: src/builtins/builtins-promise.cc |
diff --git a/src/builtins/builtins-promise.cc b/src/builtins/builtins-promise.cc |
index 0f89d304e5cb37b505a00c9f0e05b32fa33cb443..aac79f1e3fe8f9c31261609e6e0ad866f05c1306 100644 |
--- a/src/builtins/builtins-promise.cc |
+++ b/src/builtins/builtins-promise.cc |
@@ -438,7 +438,6 @@ Node* PromiseBuiltinsAssembler::InternalPerformPromiseThen( |
Bind(&if_onresolvenotcallable); |
{ |
- Isolate* isolate = this->isolate(); |
Node* const default_resolve_handler_symbol = HeapConstant( |
isolate->factory()->promise_default_resolve_handler_symbol()); |
var_on_resolve.Bind(default_resolve_handler_symbol); |
@@ -1563,5 +1562,221 @@ TF_BUILTIN(InternalPromiseReject, PromiseBuiltinsAssembler) { |
Return(UndefinedConstant()); |
} |
+Node* PromiseBuiltinsAssembler::CreatePromiseFinallyContext( |
+ Node* on_finally, Node* native_context) { |
+ Node* const context = |
+ CreatePromiseContext(native_context, kOnFinallyContextLength); |
+ StoreContextElementNoWriteBarrier(context, kOnFinallySlot, on_finally); |
+ return context; |
+} |
+ |
+std::pair<Node*, Node*> PromiseBuiltinsAssembler::CreatePromiseFinallyFunctions( |
+ Node* on_finally, Node* native_context) { |
+ Node* const promise_context = |
+ CreatePromiseFinallyContext(on_finally, native_context); |
+ Node* const map = LoadContextElement( |
+ native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); |
+ Node* const then_finally_info = LoadContextElement( |
+ native_context, Context::PROMISE_THEN_FINALLY_SHARED_FUN); |
+ Node* const then_finally = AllocateFunctionWithMapAndContext( |
+ map, then_finally_info, promise_context); |
+ Node* const catch_finally_info = LoadContextElement( |
+ native_context, Context::PROMISE_CATCH_FINALLY_SHARED_FUN); |
+ Node* const catch_finally = AllocateFunctionWithMapAndContext( |
+ map, catch_finally_info, promise_context); |
+ return std::make_pair(then_finally, catch_finally); |
+} |
+ |
+TF_BUILTIN(PromiseValueThunkFinally, PromiseBuiltinsAssembler) { |
+ Node* const context = Parameter(3); |
+ |
+ Node* const value = LoadContextElement(context, kOnFinallySlot); |
+ Return(value); |
+} |
+ |
+Node* PromiseBuiltinsAssembler::CreateValueThunkFunctionContext( |
+ Node* value, Node* native_context) { |
+ Node* const context = |
+ CreatePromiseContext(native_context, kOnFinallyContextLength); |
+ StoreContextElementNoWriteBarrier(context, kOnFinallySlot, value); |
+ return context; |
+} |
+ |
+Node* PromiseBuiltinsAssembler::CreateValueThunkFunction(Node* value, |
+ Node* native_context) { |
+ Node* const value_thunk_context = |
+ CreateValueThunkFunctionContext(value, native_context); |
+ Node* const map = LoadContextElement( |
+ native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); |
+ Node* const value_thunk_info = LoadContextElement( |
+ native_context, Context::PROMISE_VALUE_THUNK_FINALLY_SHARED_FUN); |
+ Node* const value_thunk = AllocateFunctionWithMapAndContext( |
+ map, value_thunk_info, value_thunk_context); |
+ return value_thunk; |
+} |
+ |
+TF_BUILTIN(PromiseThenFinally, PromiseBuiltinsAssembler) { |
+ CSA_ASSERT_JS_ARGC_EQ(this, 1); |
+ |
+ Node* const value = Parameter(1); |
+ Node* const context = Parameter(4); |
+ |
+ Node* const on_finally = LoadContextElement(context, kOnFinallySlot); |
+ |
+ // 2.a Let result be ? Call(onFinally, undefined). |
+ Callable call_callable = CodeFactory::Call(isolate()); |
+ Node* result = |
+ CallJS(call_callable, context, on_finally, UndefinedConstant()); |
+ |
+ // 2.b Let promise be ! PromiseResolve( %Promise%, result). |
+ Node* const promise = AllocateAndInitJSPromise(context); |
+ InternalResolvePromise(context, promise, result); |
+ |
+ // 2.c Let valueThunk be equivalent to a function that returns value. |
+ Node* native_context = LoadNativeContext(context); |
+ Node* const value_thunk = CreateValueThunkFunction(value, native_context); |
+ |
+ // 2.d Let promiseCapability be ! NewPromiseCapability( %Promise%). |
+ Node* const promise_capability = AllocateAndInitJSPromise(context, promise); |
+ |
+ // 2.e Return PerformPromiseThen(promise, valueThunk, undefined, |
+ // promiseCapability). |
+ InternalPerformPromiseThen(context, promise, value_thunk, UndefinedConstant(), |
+ promise_capability, UndefinedConstant(), |
+ UndefinedConstant()); |
+ Return(promise_capability); |
+} |
+ |
+TF_BUILTIN(PromiseThrowerFinally, PromiseBuiltinsAssembler) { |
+ Node* const context = Parameter(3); |
+ |
+ Node* const reason = LoadContextElement(context, kOnFinallySlot); |
+ CallRuntime(Runtime::kThrow, context, reason); |
+ Return(UndefinedConstant()); |
+} |
+ |
+Node* PromiseBuiltinsAssembler::CreateThrowerFunctionContext( |
+ Node* reason, Node* native_context) { |
+ Node* const context = |
+ CreatePromiseContext(native_context, kOnFinallyContextLength); |
+ StoreContextElementNoWriteBarrier(context, kOnFinallySlot, reason); |
+ return context; |
+} |
+ |
+Node* PromiseBuiltinsAssembler::CreateThrowerFunction(Node* reason, |
+ Node* native_context) { |
+ Node* const thrower_context = |
+ CreateThrowerFunctionContext(reason, native_context); |
+ Node* const map = LoadContextElement( |
+ native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); |
+ Node* const thrower_info = LoadContextElement( |
+ native_context, Context::PROMISE_THROWER_FINALLY_SHARED_FUN); |
+ Node* const thrower = |
+ AllocateFunctionWithMapAndContext(map, thrower_info, thrower_context); |
+ return thrower; |
+} |
+ |
+TF_BUILTIN(PromiseCatchFinally, PromiseBuiltinsAssembler) { |
+ CSA_ASSERT_JS_ARGC_EQ(this, 1); |
+ |
+ Node* const reason = Parameter(1); |
+ Node* const context = Parameter(4); |
+ |
+ Node* const on_finally = LoadContextElement(context, kOnFinallySlot); |
+ |
+ // 2.a Let result be ? Call(onFinally, undefined). |
+ Callable call_callable = CodeFactory::Call(isolate()); |
+ Node* result = |
+ CallJS(call_callable, context, on_finally, UndefinedConstant()); |
+ |
+ // 2.b Let promise be ! PromiseResolve( %Promise%, result). |
+ Node* const promise = AllocateAndInitJSPromise(context); |
+ InternalResolvePromise(context, promise, result); |
+ |
+ // 2.c Let thrower be equivalent to a function that throws reason. |
+ Node* native_context = LoadNativeContext(context); |
+ Node* const thrower = CreateThrowerFunction(reason, native_context); |
+ |
+ // 2.d Let promiseCapability be ! NewPromiseCapability( %Promise%). |
+ Node* const promise_capability = AllocateAndInitJSPromise(context, promise); |
+ |
+ // 2.e Return PerformPromiseThen(promise, thrower, undefined, |
+ // promiseCapability). |
+ InternalPerformPromiseThen(context, promise, thrower, UndefinedConstant(), |
+ promise_capability, UndefinedConstant(), |
+ UndefinedConstant()); |
+ Return(promise_capability); |
+} |
+ |
+TF_BUILTIN(PromiseFinally, PromiseBuiltinsAssembler) { |
+ CSA_ASSERT_JS_ARGC_EQ(this, 1); |
+ |
+ // 1. Let promise be the this value. |
+ Node* const promise = Parameter(0); |
+ Node* const on_finally = Parameter(1); |
+ Node* const context = Parameter(4); |
+ |
+ // 2. If IsPromise(promise) is false, throw a TypeError exception. |
+ ThrowIfNotInstanceType(context, promise, JS_PROMISE_TYPE, |
+ "Promise.prototype.finally"); |
+ |
+ Variable var_then_finally(this, MachineRepresentation::kTagged), |
+ var_catch_finally(this, MachineRepresentation::kTagged); |
+ |
+ Label if_notcallable(this, Label::kDeferred), perform_finally(this); |
+ |
+ // 3. Let thenFinally be ! CreateThenFinally(onFinally). |
+ // 4. Let catchFinally be ! CreateCatchFinally(onFinally). |
+ GotoIf(TaggedIsSmi(on_finally), &if_notcallable); |
+ Node* const on_finally_map = LoadMap(on_finally); |
+ GotoIfNot(IsCallableMap(on_finally_map), &if_notcallable); |
+ |
+ Node* const native_context = LoadNativeContext(context); |
+ Node* then_finally = nullptr; |
+ Node* catch_finally = nullptr; |
+ std::tie(then_finally, catch_finally) = |
+ CreatePromiseFinallyFunctions(on_finally, native_context); |
+ var_then_finally.Bind(then_finally); |
+ var_catch_finally.Bind(catch_finally); |
+ Goto(&perform_finally); |
+ |
+ Bind(&if_notcallable); |
+ { |
+ var_then_finally.Bind(on_finally); |
+ var_catch_finally.Bind(on_finally); |
+ Goto(&perform_finally); |
+ } |
+ |
+ // 5. Return PerformPromiseThen(promise, valueThunk, undefined, |
+ // promiseCapability). |
+ Bind(&perform_finally); |
+ Label if_nativepromise(this), if_custompromise(this, Label::kDeferred); |
+ BranchIfFastPath(context, promise, &if_nativepromise, &if_custompromise); |
+ |
+ Bind(&if_nativepromise); |
+ { |
+ Node* deferred_promise = AllocateAndInitJSPromise(context, promise); |
+ InternalPerformPromiseThen(context, promise, var_then_finally.value(), |
+ var_catch_finally.value(), deferred_promise, |
+ UndefinedConstant(), UndefinedConstant()); |
+ Return(deferred_promise); |
+ } |
+ |
+ Bind(&if_custompromise); |
+ { |
+ Isolate* isolate = this->isolate(); |
+ Node* const then_str = HeapConstant(isolate->factory()->then_string()); |
+ Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
+ Node* const then = |
+ CallStub(getproperty_callable, context, promise, then_str); |
+ Callable call_callable = CodeFactory::Call(isolate); |
+ // 5. Return ? Invoke(promise, "then", « thenFinally, catchFinally »). |
+ Node* const result = |
+ CallJS(call_callable, context, then, promise, var_then_finally.value(), |
+ var_catch_finally.value()); |
+ Return(result); |
+ } |
+} |
+ |
} // namespace internal |
} // namespace v8 |