Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(597)

Unified Diff: src/builtins/builtins-promise.cc

Issue 2695753002: [ESnext] Implement Promise.prototype.finally (Closed)
Patch Set: Rename GotoUnless Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/builtins/builtins-promise.h ('k') | src/contexts.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « src/builtins/builtins-promise.h ('k') | src/contexts.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698