Chromium Code Reviews| 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 |