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

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

Issue 2695753002: [ESnext] Implement Promise.prototype.finally (Closed)
Patch Set: add comments 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
Index: src/builtins/builtins-promise.cc
diff --git a/src/builtins/builtins-promise.cc b/src/builtins/builtins-promise.cc
index 2805d611720e206cff4f031875cc2d6331672730..b0fa0d1f469fb84018e024e572056639b231fd9c 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);
@@ -1564,5 +1563,218 @@ 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,
neis 2017/02/15 12:40:39 nit: s/Function//
gsathya 2017/02/16 15:05:29 Leaving as such to be consistent with CreatePromis
+ 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) {
+ Node* const parent = Parameter(0);
neis 2017/02/15 12:40:39 This will typically be undefined.
gsathya 2017/02/16 15:05:29 Done.
+ Node* const value = Parameter(1);
+ Node* const context = Parameter(4);
neis 2017/02/15 12:40:39 Maybe use CSA_ASSERT_JS_ARGC_EQ here and elsewhere
gsathya 2017/02/16 15:05:30 Done.
+
+ 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, parent);
+ 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) {
neis 2017/02/15 12:40:39 nit: s/Function//
gsathya 2017/02/16 15:05:29 Same as CreateValueThunkFunction
+ 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) {
+ Node* const parent = Parameter(0);
+ Node* const value = Parameter(1);
neis 2017/02/15 12:40:39 nit: s/value/reason/
gsathya 2017/02/16 15:05:30 Done.
+ 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, parent);
+ 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(value, 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(result);
neis 2017/02/15 12:40:39 Ouch! Return(promise_capability) This suggests a
gsathya 2017/02/16 15:05:29 Done.
+}
+
+TF_BUILTIN(PromiseFinally, PromiseBuiltinsAssembler) {
+ // 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);
+
+ GotoIf(TaggedIsSmi(on_finally), &if_notcallable);
+ Node* const on_finally_map = LoadMap(on_finally);
+ GotoUnless(IsCallableMap(on_finally_map), &if_notcallable);
+
+ // 3. Let thenFinally be ! CreateThenFinally(onFinally).
neis 2017/02/15 12:40:39 The few lines above are already part of steps 3 an
gsathya 2017/02/16 15:05:29 Done.
+ // 4. Let catchFinally be ! CreateCatchFinally(onFinally).
+ 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);
+ {
+ // 1. If IsCallable(onFinally) is not true, return onFinally.
+ var_then_finally.Bind(on_finally);
+ // 1. If IsCallable(onFinally) is not true, return onFinally.
neis 2017/02/15 12:40:39 I find these comments here confusing, since they a
gsathya 2017/02/16 15:05:29 Yeah, that's fair. Removed
+ var_catch_finally.Bind(on_finally);
+ Goto(&perform_finally);
+ }
+
+ Bind(&perform_finally);
+ Label if_nativepromise(this), if_custompromise(this, Label::kDeferred);
+ GotoIf(TaggedIsSmi(promise), &if_custompromise);
neis 2017/02/15 12:40:39 We already know that promise is a JS_PROMISE objec
gsathya 2017/02/16 15:05:29 Done.
+ BranchIfFastPath(context, promise, &if_nativepromise, &if_custompromise);
+
neis 2017/02/15 12:40:39 The step 5 comment should appear here.
gsathya 2017/02/16 15:05:29 Done.
+ 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

Powered by Google App Engine
This is Rietveld 408576698