Chromium Code Reviews| Index: src/builtins/builtins-promise.cc |
| diff --git a/src/builtins/builtins-promise.cc b/src/builtins/builtins-promise.cc |
| index 46f6043bd3c85350d81ea25da6940d0219a4b0ef..ad7c0ce8333439244a01be4450639adb305ef0dd 100644 |
| --- a/src/builtins/builtins-promise.cc |
| +++ b/src/builtins/builtins-promise.cc |
| @@ -16,6 +16,152 @@ typedef compiler::Node Node; |
| typedef CodeStubAssembler::ParameterMode ParameterMode; |
| typedef compiler::CodeAssemblerState CodeAssemblerState; |
| +Node* PromiseBuiltinsAssembler::CreatePromiseGetCapabilitiesExecutorContext( |
| + Node* promise_capability, Node* native_context) { |
| + Node* context = Allocate( |
| + FixedArray::SizeFor(GetPromiseCapabilityExecutor::kContextLength)); |
| + Node* map = HeapConstant(isolate()->factory()->function_context_map()); |
| + StoreMapNoWriteBarrier(context, map); |
| + StoreObjectFieldNoWriteBarrier( |
| + context, FixedArray::kLengthOffset, |
| + SmiConstant(GetPromiseCapabilityExecutor::kContextLength)); |
| + |
| + Node* script = LoadContextElement(native_context, Context::CLOSURE_INDEX); |
| + StoreContextElement(context, Context::CLOSURE_INDEX, script); |
| + StoreContextElement(context, Context::PREVIOUS_INDEX, UndefinedConstant()); |
| + StoreContextElement(context, Context::EXTENSION_INDEX, TheHoleConstant()); |
| + StoreContextElement(context, Context::NATIVE_CONTEXT_INDEX, native_context); |
|
gsathya
2016/12/21 01:02:33
You refactor this to reuse CreatePromiseResolvingF
|
| + StoreContextElement(context, GetPromiseCapabilityExecutor::kCapabilitySlot, |
|
gsathya
2016/12/21 01:02:33
StoreContextElementNoWriteBarrier
Igor Sheludko
2016/12/21 12:00:45
We can use it for all the stores because the objec
|
| + promise_capability); |
| + return context; |
| +} |
| + |
| +Node* PromiseBuiltinsAssembler::NewPromiseCapability(Node* context, |
| + Node* constructor, |
| + Node* debug_event) { |
| + if (debug_event == nullptr) { |
| + debug_event = TrueConstant(); |
| + } |
| + |
| + Node* native_context = LoadNativeContext(context); |
| + |
| + Node* capability = Allocate(JSPromiseCapability::kSize); |
| + StoreMapNoWriteBarrier(capability, Heap::kJSPromiseCapabilityMapRootIndex); |
|
gsathya
2016/12/21 01:02:33
Why not use AllocateJSObjectFromMap here?
caitp
2016/12/21 21:01:13
Done.
|
| + StoreObjectFieldNoWriteBarrier(capability, JSObject::kPropertiesOffset, |
| + EmptyFixedArrayConstant()); |
| + StoreObjectFieldNoWriteBarrier(capability, JSObject::kElementsOffset, |
| + EmptyFixedArrayConstant()); |
| + StoreObjectFieldNoWriteBarrier( |
| + capability, JSPromiseCapability::kPromiseOffset, UndefinedConstant()); |
| + StoreObjectFieldNoWriteBarrier( |
| + capability, JSPromiseCapability::kResolveOffset, UndefinedConstant()); |
| + StoreObjectFieldNoWriteBarrier(capability, JSPromiseCapability::kRejectOffset, |
| + UndefinedConstant()); |
| + |
| + Variable var_result(this, MachineRepresentation::kTagged); |
| + var_result.Bind(capability); |
| + |
| + Label if_builtin_promise(this), if_custom_promise(this), out(this); |
| + Branch(WordEqual(constructor, |
| + LoadContextElement(native_context, |
| + Context::PROMISE_FUNCTION_INDEX)), |
| + &if_builtin_promise, &if_custom_promise); |
| + |
| + Bind(&if_builtin_promise); |
| + { |
| + Node* promise = AllocateJSPromise(context); |
| + PromiseInit(promise); |
| + StoreObjectFieldNoWriteBarrier( |
| + capability, JSPromiseCapability::kPromiseOffset, promise); |
| + |
| + Node* resolve = nullptr; |
| + Node* reject = nullptr; |
| + |
| + std::tie(resolve, reject) = |
| + CreatePromiseResolvingFunctions(promise, debug_event, native_context); |
| + StoreObjectField(capability, JSPromiseCapability::kResolveOffset, resolve); |
| + StoreObjectField(capability, JSPromiseCapability::kRejectOffset, reject); |
| + |
| + GotoUnless(IsPromiseHookEnabled(), &out); |
| + CallRuntime(Runtime::kPromiseHookInit, context, promise, |
| + UndefinedConstant()); |
| + Goto(&out); |
| + } |
| + |
| + Bind(&if_custom_promise); |
| + { |
| + Label if_notcallable(this, Label::kDeferred); |
| + Node* executor_context = |
| + CreatePromiseGetCapabilitiesExecutorContext(capability, native_context); |
| + Node* executor_info = LoadContextElement( |
| + native_context, Context::PROMISE_GET_CAPABILITIES_EXECUTOR_SHARED_FUN); |
| + Node* function_map = LoadContextElement( |
| + native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); |
| + Node* executor = AllocateFunctionWithMapAndContext( |
| + function_map, executor_info, executor_context); |
| + |
| + Node* promise = ConstructJS(CodeFactory::Construct(isolate()), context, |
| + constructor, executor); |
| + |
| + Node* resolve = |
| + LoadObjectField(capability, JSPromiseCapability::kResolveOffset); |
| + GotoIf(TaggedIsSmi(resolve), &if_notcallable); |
| + GotoUnless(IsCallableMap(LoadMap(resolve)), &if_notcallable); |
| + |
| + Node* reject = |
| + LoadObjectField(capability, JSPromiseCapability::kRejectOffset); |
| + GotoIf(TaggedIsSmi(reject), &if_notcallable); |
| + GotoUnless(IsCallableMap(LoadMap(reject)), &if_notcallable); |
| + |
| + StoreObjectField(capability, JSPromiseCapability::kPromiseOffset, promise); |
| + |
| + Goto(&out); |
| + |
| + Bind(&if_notcallable); |
| + Node* message = SmiConstant(MessageTemplate::kPromiseNonCallable); |
| + StoreObjectField(capability, JSPromiseCapability::kPromiseOffset, |
| + UndefinedConstant()); |
| + StoreObjectField(capability, JSPromiseCapability::kResolveOffset, |
| + UndefinedConstant()); |
| + StoreObjectField(capability, JSPromiseCapability::kRejectOffset, |
| + UndefinedConstant()); |
| + CallRuntime(Runtime::kThrowTypeError, context, message); |
| + var_result.Bind(UndefinedConstant()); |
| + Goto(&out); |
| + } |
| + |
| + Bind(&out); |
| + return var_result.value(); |
| +} |
| + |
| +Node* PromiseBuiltinsAssembler::NewInternalPromiseCapability(Node* context, |
| + Node* parent) { |
| + Node* promise = AllocateJSPromise(context); |
| + PromiseInit(promise); |
| + |
| + Label out(this); |
| + |
| + Node* capability = Allocate(JSPromiseCapability::kSize); |
| + StoreMapNoWriteBarrier(capability, Heap::kJSPromiseCapabilityMapRootIndex); |
| + StoreObjectFieldNoWriteBarrier(capability, JSObject::kPropertiesOffset, |
| + EmptyFixedArrayConstant()); |
| + StoreObjectFieldNoWriteBarrier(capability, JSObject::kElementsOffset, |
| + EmptyFixedArrayConstant()); |
| + StoreObjectFieldNoWriteBarrier(capability, |
| + JSPromiseCapability::kPromiseOffset, promise); |
| + StoreObjectFieldNoWriteBarrier( |
| + capability, JSPromiseCapability::kResolveOffset, UndefinedConstant()); |
| + StoreObjectFieldNoWriteBarrier(capability, JSPromiseCapability::kRejectOffset, |
| + UndefinedConstant()); |
| + |
| + GotoUnless(IsPromiseHookEnabled(), &out); |
| + CallRuntime(Runtime::kPromiseHookInit, context, promise, parent); |
| + Goto(&out); |
| + |
| + Bind(&out); |
| + return capability; |
| +} |
| + |
| Node* PromiseBuiltinsAssembler::CreatePromiseResolvingFunctionsContext( |
| Node* promise, Node* debug_event, Node* native_context) { |
| Node* const context = |
| @@ -338,15 +484,10 @@ Node* PromiseBuiltinsAssembler::InternalPerformPromiseThen(Node* context, |
| Bind(&out); |
| PromiseSetHasHandler(promise); |
| - // TODO(gsathya): This call will be removed once we don't have to |
| - // deal with deferred objects. |
| - Isolate* isolate = this->isolate(); |
| - Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
| - Node* const key = |
| - HeapConstant(isolate->factory()->NewStringFromAsciiChecked("promise")); |
| - Node* const result = CallStub(getproperty_callable, context, deferred, key); |
| - |
| - return result; |
| + CSA_ASSERT(this, HasInstanceType(deferred, JS_PROMISE_CAPABILITY_TYPE)); |
| + Node* then_promise = |
| + LoadObjectField(deferred, JSPromiseCapability::kPromiseOffset); |
| + return then_promise; |
| } |
| // Promise fast path implementations rely on unmodified JSPromise instances. |
| @@ -795,24 +936,14 @@ TF_BUILTIN(PromiseThen, PromiseBuiltinsAssembler) { |
| // NewPromiseCapabability functions to TF. |
| Bind(&fast_promise_capability); |
| { |
| - // TODO(gsathya): Move this to TF. |
| - Node* const promise_internal_capability = LoadContextElement( |
| - native_context, Context::INTERNAL_PROMISE_CAPABILITY_INDEX); |
| - Node* const capability = |
| - CallJS(call_callable, context, promise_internal_capability, |
| - UndefinedConstant(), promise); |
| + Node* const capability = NewInternalPromiseCapability(context, promise); |
| var_deferred.Bind(capability); |
| Goto(&perform_promise_then); |
| } |
| Bind(&promise_capability); |
| { |
| - // TODO(gsathya): Move this to TF. |
| - Node* const new_promise_capability = LoadContextElement( |
| - native_context, Context::NEW_PROMISE_CAPABILITY_INDEX); |
| - Node* const capability = |
| - CallJS(call_callable, context, new_promise_capability, |
| - UndefinedConstant(), constructor); |
| + Node* const capability = NewPromiseCapability(context, constructor); |
| var_deferred.Bind(capability); |
| Goto(&perform_promise_then); |
| } |
| @@ -905,11 +1036,9 @@ TF_BUILTIN(PromiseHandle, PromiseBuiltinsAssembler) { |
| Isolate* isolate = this->isolate(); |
| // Get promise from deferred |
| - // TODO(gsathya): Remove this lookup by getting rid of the deferred object. |
| - Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
| - Node* const key = HeapConstant(isolate->factory()->promise_string()); |
| + CSA_ASSERT(this, HasInstanceType(deferred, JS_PROMISE_CAPABILITY_TYPE)); |
| Node* const deferred_promise = |
| - CallStub(getproperty_callable, context, deferred, key); |
| + LoadObjectField(deferred, JSPromiseCapability::kPromiseOffset); |
| Variable var_reason(this, MachineRepresentation::kTagged); |
| @@ -937,10 +1066,8 @@ TF_BUILTIN(PromiseHandle, PromiseBuiltinsAssembler) { |
| GotoIfException(result, &if_rejectpromise, &var_reason); |
| - // TODO(gsathya): Remove this lookup by getting rid of the deferred object. |
| - Node* const key = HeapConstant(isolate->factory()->resolve_string()); |
| Node* const on_resolve = |
| - CallStub(getproperty_callable, context, deferred, key); |
| + LoadObjectField(deferred, JSPromiseCapability::kResolveOffset); |
| Label if_internalhandler(this), if_customhandler(this, Label::kDeferred); |
| Branch(IsUndefined(on_resolve), &if_internalhandler, &if_customhandler); |
| @@ -960,10 +1087,8 @@ TF_BUILTIN(PromiseHandle, PromiseBuiltinsAssembler) { |
| Bind(&if_rejectpromise); |
| { |
| - // TODO(gsathya): Remove this lookup by getting rid of the deferred object. |
| - Node* const key = HeapConstant(isolate->factory()->reject_string()); |
| Node* const on_reject = |
| - CallStub(getproperty_callable, context, deferred, key); |
| + LoadObjectField(deferred, JSPromiseCapability::kRejectOffset); |
| Callable promise_handle_reject = CodeFactory::PromiseHandleReject(isolate); |
| CallStub(promise_handle_reject, context, deferred_promise, on_reject, |
| @@ -991,5 +1116,51 @@ TF_BUILTIN(PromiseHandle, PromiseBuiltinsAssembler) { |
| } |
| } |
| +TF_BUILTIN(PromiseGetCapabilitiesExecutor, PromiseBuiltinsAssembler) { |
| + Node* const resolve = Parameter(1); |
| + Node* const reject = Parameter(2); |
| + Node* const context = Parameter(5); |
| + |
| + Node* const capability = LoadContextElement( |
| + context, GetPromiseCapabilityExecutor::kCapabilitySlot); |
| + |
| + Label if_alreadyinvoked(this, Label::kDeferred); |
| + GotoIf(WordNotEqual( |
| + LoadObjectField(capability, JSPromiseCapability::kResolveOffset), |
| + UndefinedConstant()), |
| + &if_alreadyinvoked); |
| + GotoIf(WordNotEqual( |
| + LoadObjectField(capability, JSPromiseCapability::kRejectOffset), |
| + UndefinedConstant()), |
| + &if_alreadyinvoked); |
| + |
| + StoreObjectField(capability, JSPromiseCapability::kResolveOffset, resolve); |
| + StoreObjectField(capability, JSPromiseCapability::kRejectOffset, reject); |
| + |
| + Return(UndefinedConstant()); |
| + |
| + Bind(&if_alreadyinvoked); |
| + Node* message = SmiConstant(MessageTemplate::kPromiseExecutorAlreadyInvoked); |
| + Return(CallRuntime(Runtime::kThrowTypeError, context, message)); |
| +} |
| + |
| +TF_BUILTIN(NewPromiseCapability, PromiseBuiltinsAssembler) { |
| + Node* constructor = Parameter(1); |
| + Node* debug_event = Parameter(2); |
| + Node* context = Parameter(5); |
| + |
| + CSA_ASSERT_JS_ARGC_EQ(this, 2); |
| + |
| + Return(NewPromiseCapability(context, constructor, debug_event)); |
| +} |
| + |
| +TF_BUILTIN(NewInternalPromiseCapability, PromiseBuiltinsAssembler) { |
| + Node* parent = Parameter(1); |
| + Node* context = Parameter(4); |
| + CSA_ASSERT_JS_ARGC_EQ(this, 1); |
| + |
| + Return(NewInternalPromiseCapability(context, parent)); |
| +} |
| + |
| } // namespace internal |
| } // namespace v8 |