diff options
author | Michaël Zasso <targos@protonmail.com> | 2017-06-06 10:28:14 +0200 |
---|---|---|
committer | Michaël Zasso <targos@protonmail.com> | 2017-06-07 10:33:31 +0200 |
commit | 3dc8c3bed4cf3a77607edbb0b015e33f8b60fc09 (patch) | |
tree | 9dee56e142638b34f1eccbd0ad88c3bce5377c29 /deps/v8/src/builtins/builtins-promise.cc | |
parent | 91a1bbe3055a660194ca4d403795aa0c03e9d056 (diff) | |
download | android-node-v8-3dc8c3bed4cf3a77607edbb0b015e33f8b60fc09.tar.gz android-node-v8-3dc8c3bed4cf3a77607edbb0b015e33f8b60fc09.tar.bz2 android-node-v8-3dc8c3bed4cf3a77607edbb0b015e33f8b60fc09.zip |
deps: update V8 to 5.9.211.32
PR-URL: https://github.com/nodejs/node/pull/13263
Reviewed-By: Gibson Fahnestock <gibfahn@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
Reviewed-By: Myles Borins <myles.borins@gmail.com>
Diffstat (limited to 'deps/v8/src/builtins/builtins-promise.cc')
-rw-r--r-- | deps/v8/src/builtins/builtins-promise.cc | 1790 |
1 files changed, 0 insertions, 1790 deletions
diff --git a/deps/v8/src/builtins/builtins-promise.cc b/deps/v8/src/builtins/builtins-promise.cc deleted file mode 100644 index 6d9fed83a9..0000000000 --- a/deps/v8/src/builtins/builtins-promise.cc +++ /dev/null @@ -1,1790 +0,0 @@ -// Copyright 2016 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "src/builtins/builtins-promise.h" -#include "src/builtins/builtins-constructor.h" -#include "src/builtins/builtins-utils.h" -#include "src/builtins/builtins.h" -#include "src/code-factory.h" -#include "src/code-stub-assembler.h" -#include "src/objects-inl.h" - -namespace v8 { -namespace internal { - -typedef compiler::Node Node; -typedef CodeStubAssembler::ParameterMode ParameterMode; -typedef compiler::CodeAssemblerState CodeAssemblerState; - -Node* PromiseBuiltinsAssembler::AllocateJSPromise(Node* context) { - Node* const native_context = LoadNativeContext(context); - Node* const promise_fun = - LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); - Node* const initial_map = - LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset); - Node* const instance = AllocateJSObjectFromMap(initial_map); - return instance; -} - -void PromiseBuiltinsAssembler::PromiseInit(Node* promise) { - StoreObjectField(promise, JSPromise::kStatusOffset, - SmiConstant(v8::Promise::kPending)); - StoreObjectField(promise, JSPromise::kFlagsOffset, SmiConstant(0)); - for (int i = 0; i < v8::Promise::kEmbedderFieldCount; i++) { - int offset = JSPromise::kSize + i * kPointerSize; - StoreObjectFieldNoWriteBarrier(promise, offset, SmiConstant(Smi::kZero)); - } -} - -Node* PromiseBuiltinsAssembler::AllocateAndInitJSPromise(Node* context) { - return AllocateAndInitJSPromise(context, UndefinedConstant()); -} - -Node* PromiseBuiltinsAssembler::AllocateAndInitJSPromise(Node* context, - Node* parent) { - Node* const instance = AllocateJSPromise(context); - PromiseInit(instance); - - Label out(this); - GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &out); - CallRuntime(Runtime::kPromiseHookInit, context, instance, parent); - Goto(&out); - - Bind(&out); - return instance; -} - -Node* PromiseBuiltinsAssembler::AllocateAndSetJSPromise(Node* context, - Node* status, - Node* result) { - CSA_ASSERT(this, TaggedIsSmi(status)); - - Node* const instance = AllocateJSPromise(context); - - StoreObjectFieldNoWriteBarrier(instance, JSPromise::kStatusOffset, status); - StoreObjectFieldNoWriteBarrier(instance, JSPromise::kResultOffset, result); - StoreObjectFieldNoWriteBarrier(instance, JSPromise::kFlagsOffset, - SmiConstant(0)); - for (int i = 0; i < v8::Promise::kEmbedderFieldCount; i++) { - int offset = JSPromise::kSize + i * kPointerSize; - StoreObjectFieldNoWriteBarrier(instance, offset, SmiConstant(Smi::kZero)); - } - - Label out(this); - GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &out); - CallRuntime(Runtime::kPromiseHookInit, context, instance, - UndefinedConstant()); - Goto(&out); - - Bind(&out); - return instance; -} - -std::pair<Node*, Node*> -PromiseBuiltinsAssembler::CreatePromiseResolvingFunctions( - Node* promise, Node* debug_event, Node* native_context) { - Node* const promise_context = CreatePromiseResolvingFunctionsContext( - promise, debug_event, native_context); - Node* const map = LoadContextElement( - native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); - Node* const resolve_info = - LoadContextElement(native_context, Context::PROMISE_RESOLVE_SHARED_FUN); - Node* const resolve = - AllocateFunctionWithMapAndContext(map, resolve_info, promise_context); - Node* const reject_info = - LoadContextElement(native_context, Context::PROMISE_REJECT_SHARED_FUN); - Node* const reject = - AllocateFunctionWithMapAndContext(map, reject_info, promise_context); - return std::make_pair(resolve, reject); -} - -Node* PromiseBuiltinsAssembler::NewPromiseCapability(Node* context, - Node* constructor, - Node* debug_event) { - if (debug_event == nullptr) { - debug_event = TrueConstant(); - } - - Node* native_context = LoadNativeContext(context); - - Node* map = LoadRoot(Heap::kJSPromiseCapabilityMapRootIndex); - Node* capability = AllocateJSObjectFromMap(map); - - 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, Label::kDeferred), - 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); - - GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &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); - GotoIfNot(IsCallableMap(LoadMap(resolve)), &if_notcallable); - - Node* reject = - LoadObjectField(capability, JSPromiseCapability::kRejectOffset); - GotoIf(TaggedIsSmi(reject), &if_notcallable); - GotoIfNot(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); - Unreachable(); - } - - Bind(&out); - return var_result.value(); -} - -Node* PromiseBuiltinsAssembler::CreatePromiseContext(Node* native_context, - int slots) { - DCHECK_GE(slots, Context::MIN_CONTEXT_SLOTS); - - Node* const context = Allocate(FixedArray::SizeFor(slots)); - StoreMapNoWriteBarrier(context, Heap::kFunctionContextMapRootIndex); - StoreObjectFieldNoWriteBarrier(context, FixedArray::kLengthOffset, - SmiConstant(slots)); - - Node* const empty_fn = - LoadContextElement(native_context, Context::CLOSURE_INDEX); - StoreContextElementNoWriteBarrier(context, Context::CLOSURE_INDEX, empty_fn); - StoreContextElementNoWriteBarrier(context, Context::PREVIOUS_INDEX, - UndefinedConstant()); - StoreContextElementNoWriteBarrier(context, Context::EXTENSION_INDEX, - TheHoleConstant()); - StoreContextElementNoWriteBarrier(context, Context::NATIVE_CONTEXT_INDEX, - native_context); - return context; -} - -Node* PromiseBuiltinsAssembler::CreatePromiseResolvingFunctionsContext( - Node* promise, Node* debug_event, Node* native_context) { - Node* const context = - CreatePromiseContext(native_context, kPromiseContextLength); - StoreContextElementNoWriteBarrier(context, kAlreadyVisitedSlot, - SmiConstant(0)); - StoreContextElementNoWriteBarrier(context, kPromiseSlot, promise); - StoreContextElementNoWriteBarrier(context, kDebugEventSlot, debug_event); - return context; -} - -Node* PromiseBuiltinsAssembler::CreatePromiseGetCapabilitiesExecutorContext( - Node* promise_capability, Node* native_context) { - int kContextLength = kCapabilitiesContextLength; - Node* context = CreatePromiseContext(native_context, kContextLength); - StoreContextElementNoWriteBarrier(context, kCapabilitySlot, - promise_capability); - return context; -} - -Node* PromiseBuiltinsAssembler::ThrowIfNotJSReceiver( - Node* context, Node* value, MessageTemplate::Template msg_template, - const char* method_name) { - Label out(this), throw_exception(this, Label::kDeferred); - Variable var_value_map(this, MachineRepresentation::kTagged); - - GotoIf(TaggedIsSmi(value), &throw_exception); - - // Load the instance type of the {value}. - var_value_map.Bind(LoadMap(value)); - Node* const value_instance_type = LoadMapInstanceType(var_value_map.value()); - - Branch(IsJSReceiverInstanceType(value_instance_type), &out, &throw_exception); - - // The {value} is not a compatible receiver for this method. - Bind(&throw_exception); - { - Node* const method = - method_name == nullptr - ? UndefinedConstant() - : HeapConstant( - isolate()->factory()->NewStringFromAsciiChecked(method_name)); - Node* const message_id = SmiConstant(msg_template); - CallRuntime(Runtime::kThrowTypeError, context, message_id, method); - Unreachable(); - } - - Bind(&out); - return var_value_map.value(); -} - -Node* PromiseBuiltinsAssembler::PromiseHasHandler(Node* promise) { - Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset); - return IsSetWord(SmiUntag(flags), 1 << JSPromise::kHasHandlerBit); -} - -void PromiseBuiltinsAssembler::PromiseSetHasHandler(Node* promise) { - Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset); - Node* const new_flags = - SmiOr(flags, SmiConstant(1 << JSPromise::kHasHandlerBit)); - StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset, new_flags); -} - -void PromiseBuiltinsAssembler::PromiseSetHandledHint(Node* promise) { - Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset); - Node* const new_flags = - SmiOr(flags, SmiConstant(1 << JSPromise::kHandledHintBit)); - StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset, new_flags); -} - -Node* PromiseBuiltinsAssembler::SpeciesConstructor(Node* context, Node* object, - Node* default_constructor) { - Isolate* isolate = this->isolate(); - Variable var_result(this, MachineRepresentation::kTagged); - var_result.Bind(default_constructor); - - // 2. Let C be ? Get(O, "constructor"). - Node* const constructor_str = - HeapConstant(isolate->factory()->constructor_string()); - Callable getproperty_callable = CodeFactory::GetProperty(isolate); - Node* const constructor = - CallStub(getproperty_callable, context, object, constructor_str); - - // 3. If C is undefined, return defaultConstructor. - Label out(this); - GotoIf(IsUndefined(constructor), &out); - - // 4. If Type(C) is not Object, throw a TypeError exception. - ThrowIfNotJSReceiver(context, constructor, - MessageTemplate::kConstructorNotReceiver); - - // 5. Let S be ? Get(C, @@species). - Node* const species_symbol = - HeapConstant(isolate->factory()->species_symbol()); - Node* const species = - CallStub(getproperty_callable, context, constructor, species_symbol); - - // 6. If S is either undefined or null, return defaultConstructor. - GotoIf(IsUndefined(species), &out); - GotoIf(WordEqual(species, NullConstant()), &out); - - // 7. If IsConstructor(S) is true, return S. - Label throw_error(this); - Node* species_bitfield = LoadMapBitField(LoadMap(species)); - GotoIfNot(Word32Equal(Word32And(species_bitfield, - Int32Constant((1 << Map::kIsConstructor))), - Int32Constant(1 << Map::kIsConstructor)), - &throw_error); - var_result.Bind(species); - Goto(&out); - - // 8. Throw a TypeError exception. - Bind(&throw_error); - { - Node* const message_id = - SmiConstant(MessageTemplate::kSpeciesNotConstructor); - CallRuntime(Runtime::kThrowTypeError, context, message_id); - Unreachable(); - } - - Bind(&out); - return var_result.value(); -} - -void PromiseBuiltinsAssembler::AppendPromiseCallback(int offset, Node* promise, - Node* value) { - Node* elements = LoadObjectField(promise, offset); - Node* length = LoadFixedArrayBaseLength(elements); - CodeStubAssembler::ParameterMode mode = OptimalParameterMode(); - length = TaggedToParameter(length, mode); - - Node* delta = IntPtrOrSmiConstant(1, mode); - Node* new_capacity = IntPtrOrSmiAdd(length, delta, mode); - - const ElementsKind kind = FAST_ELEMENTS; - const WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER; - const CodeStubAssembler::AllocationFlags flags = - CodeStubAssembler::kAllowLargeObjectAllocation; - int additional_offset = 0; - - Node* new_elements = AllocateFixedArray(kind, new_capacity, mode, flags); - - CopyFixedArrayElements(kind, elements, new_elements, length, barrier_mode, - mode); - StoreFixedArrayElement(new_elements, length, value, barrier_mode, - additional_offset, mode); - - StoreObjectField(promise, offset, new_elements); -} - -Node* PromiseBuiltinsAssembler::InternalPromiseThen(Node* context, - Node* promise, - Node* on_resolve, - Node* on_reject) { - Isolate* isolate = this->isolate(); - - // 2. If IsPromise(promise) is false, throw a TypeError exception. - ThrowIfNotInstanceType(context, promise, JS_PROMISE_TYPE, - "Promise.prototype.then"); - - Node* const native_context = LoadNativeContext(context); - Node* const promise_fun = - LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); - - // 3. Let C be ? SpeciesConstructor(promise, %Promise%). - Node* constructor = SpeciesConstructor(context, promise, promise_fun); - - // 4. Let resultCapability be ? NewPromiseCapability(C). - Callable call_callable = CodeFactory::Call(isolate); - Label fast_promise_capability(this), promise_capability(this), - perform_promise_then(this); - Variable var_deferred_promise(this, MachineRepresentation::kTagged), - var_deferred_on_resolve(this, MachineRepresentation::kTagged), - var_deferred_on_reject(this, MachineRepresentation::kTagged); - - Branch(WordEqual(promise_fun, constructor), &fast_promise_capability, - &promise_capability); - - Bind(&fast_promise_capability); - { - Node* const deferred_promise = AllocateAndInitJSPromise(context, promise); - var_deferred_promise.Bind(deferred_promise); - var_deferred_on_resolve.Bind(UndefinedConstant()); - var_deferred_on_reject.Bind(UndefinedConstant()); - Goto(&perform_promise_then); - } - - Bind(&promise_capability); - { - Node* const capability = NewPromiseCapability(context, constructor); - var_deferred_promise.Bind( - LoadObjectField(capability, JSPromiseCapability::kPromiseOffset)); - var_deferred_on_resolve.Bind( - LoadObjectField(capability, JSPromiseCapability::kResolveOffset)); - var_deferred_on_reject.Bind( - LoadObjectField(capability, JSPromiseCapability::kRejectOffset)); - Goto(&perform_promise_then); - } - - // 5. Return PerformPromiseThen(promise, onFulfilled, onRejected, - // resultCapability). - Bind(&perform_promise_then); - Node* const result = InternalPerformPromiseThen( - context, promise, on_resolve, on_reject, var_deferred_promise.value(), - var_deferred_on_resolve.value(), var_deferred_on_reject.value()); - return result; -} - -Node* PromiseBuiltinsAssembler::InternalPerformPromiseThen( - Node* context, Node* promise, Node* on_resolve, Node* on_reject, - Node* deferred_promise, Node* deferred_on_resolve, - Node* deferred_on_reject) { - - Variable var_on_resolve(this, MachineRepresentation::kTagged), - var_on_reject(this, MachineRepresentation::kTagged); - - var_on_resolve.Bind(on_resolve); - var_on_reject.Bind(on_reject); - - Label out(this), if_onresolvenotcallable(this), onrejectcheck(this), - append_callbacks(this); - GotoIf(TaggedIsSmi(on_resolve), &if_onresolvenotcallable); - - Isolate* isolate = this->isolate(); - Node* const on_resolve_map = LoadMap(on_resolve); - Branch(IsCallableMap(on_resolve_map), &onrejectcheck, - &if_onresolvenotcallable); - - Bind(&if_onresolvenotcallable); - { - Node* const default_resolve_handler_symbol = HeapConstant( - isolate->factory()->promise_default_resolve_handler_symbol()); - var_on_resolve.Bind(default_resolve_handler_symbol); - Goto(&onrejectcheck); - } - - Bind(&onrejectcheck); - { - Label if_onrejectnotcallable(this); - GotoIf(TaggedIsSmi(on_reject), &if_onrejectnotcallable); - - Node* const on_reject_map = LoadMap(on_reject); - Branch(IsCallableMap(on_reject_map), &append_callbacks, - &if_onrejectnotcallable); - - Bind(&if_onrejectnotcallable); - { - Node* const default_reject_handler_symbol = HeapConstant( - isolate->factory()->promise_default_reject_handler_symbol()); - var_on_reject.Bind(default_reject_handler_symbol); - Goto(&append_callbacks); - } - } - - Bind(&append_callbacks); - { - Label fulfilled_check(this); - Node* const status = LoadObjectField(promise, JSPromise::kStatusOffset); - GotoIfNot(SmiEqual(status, SmiConstant(v8::Promise::kPending)), - &fulfilled_check); - - Node* const existing_deferred_promise = - LoadObjectField(promise, JSPromise::kDeferredPromiseOffset); - - Label if_noexistingcallbacks(this), if_existingcallbacks(this); - Branch(IsUndefined(existing_deferred_promise), &if_noexistingcallbacks, - &if_existingcallbacks); - - Bind(&if_noexistingcallbacks); - { - // Store callbacks directly in the slots. - StoreObjectField(promise, JSPromise::kDeferredPromiseOffset, - deferred_promise); - StoreObjectField(promise, JSPromise::kDeferredOnResolveOffset, - deferred_on_resolve); - StoreObjectField(promise, JSPromise::kDeferredOnRejectOffset, - deferred_on_reject); - StoreObjectField(promise, JSPromise::kFulfillReactionsOffset, - var_on_resolve.value()); - StoreObjectField(promise, JSPromise::kRejectReactionsOffset, - var_on_reject.value()); - Goto(&out); - } - - Bind(&if_existingcallbacks); - { - Label if_singlecallback(this), if_multiplecallbacks(this); - BranchIfJSObject(existing_deferred_promise, &if_singlecallback, - &if_multiplecallbacks); - - Bind(&if_singlecallback); - { - // Create new FixedArrays to store callbacks, and migrate - // existing callbacks. - Node* const deferred_promise_arr = - AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2)); - StoreFixedArrayElement(deferred_promise_arr, 0, - existing_deferred_promise); - StoreFixedArrayElement(deferred_promise_arr, 1, deferred_promise); - - Node* const deferred_on_resolve_arr = - AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2)); - StoreFixedArrayElement( - deferred_on_resolve_arr, 0, - LoadObjectField(promise, JSPromise::kDeferredOnResolveOffset)); - StoreFixedArrayElement(deferred_on_resolve_arr, 1, deferred_on_resolve); - - Node* const deferred_on_reject_arr = - AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2)); - StoreFixedArrayElement( - deferred_on_reject_arr, 0, - LoadObjectField(promise, JSPromise::kDeferredOnRejectOffset)); - StoreFixedArrayElement(deferred_on_reject_arr, 1, deferred_on_reject); - - Node* const fulfill_reactions = - AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2)); - StoreFixedArrayElement( - fulfill_reactions, 0, - LoadObjectField(promise, JSPromise::kFulfillReactionsOffset)); - StoreFixedArrayElement(fulfill_reactions, 1, var_on_resolve.value()); - - Node* const reject_reactions = - AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2)); - StoreFixedArrayElement( - reject_reactions, 0, - LoadObjectField(promise, JSPromise::kRejectReactionsOffset)); - StoreFixedArrayElement(reject_reactions, 1, var_on_reject.value()); - - // Store new FixedArrays in promise. - StoreObjectField(promise, JSPromise::kDeferredPromiseOffset, - deferred_promise_arr); - StoreObjectField(promise, JSPromise::kDeferredOnResolveOffset, - deferred_on_resolve_arr); - StoreObjectField(promise, JSPromise::kDeferredOnRejectOffset, - deferred_on_reject_arr); - StoreObjectField(promise, JSPromise::kFulfillReactionsOffset, - fulfill_reactions); - StoreObjectField(promise, JSPromise::kRejectReactionsOffset, - reject_reactions); - Goto(&out); - } - - Bind(&if_multiplecallbacks); - { - AppendPromiseCallback(JSPromise::kDeferredPromiseOffset, promise, - deferred_promise); - AppendPromiseCallback(JSPromise::kDeferredOnResolveOffset, promise, - deferred_on_resolve); - AppendPromiseCallback(JSPromise::kDeferredOnRejectOffset, promise, - deferred_on_reject); - AppendPromiseCallback(JSPromise::kFulfillReactionsOffset, promise, - var_on_resolve.value()); - AppendPromiseCallback(JSPromise::kRejectReactionsOffset, promise, - var_on_reject.value()); - Goto(&out); - } - } - - Bind(&fulfilled_check); - { - Label reject(this); - Node* const result = LoadObjectField(promise, JSPromise::kResultOffset); - GotoIfNot(WordEqual(status, SmiConstant(v8::Promise::kFulfilled)), - &reject); - - Node* info = AllocatePromiseReactionJobInfo( - result, var_on_resolve.value(), deferred_promise, deferred_on_resolve, - deferred_on_reject, context); - // TODO(gsathya): Move this to TF - CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, info); - Goto(&out); - - Bind(&reject); - { - Node* const has_handler = PromiseHasHandler(promise); - Label enqueue(this); - - // TODO(gsathya): Fold these runtime calls and move to TF. - GotoIf(has_handler, &enqueue); - CallRuntime(Runtime::kPromiseRevokeReject, context, promise); - Goto(&enqueue); - - Bind(&enqueue); - { - Node* info = AllocatePromiseReactionJobInfo( - result, var_on_reject.value(), deferred_promise, - deferred_on_resolve, deferred_on_reject, context); - // TODO(gsathya): Move this to TF - CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, info); - Goto(&out); - } - } - } - } - - Bind(&out); - PromiseSetHasHandler(promise); - return deferred_promise; -} - -// Promise fast path implementations rely on unmodified JSPromise instances. -// We use a fairly coarse granularity for this and simply check whether both -// the promise itself is unmodified (i.e. its map has not changed) and its -// prototype is unmodified. -// TODO(gsathya): Refactor this out to prevent code dupe with builtins-regexp -void PromiseBuiltinsAssembler::BranchIfFastPath(Node* context, Node* promise, - Label* if_isunmodified, - Label* if_ismodified) { - Node* const native_context = LoadNativeContext(context); - Node* const promise_fun = - LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); - BranchIfFastPath(native_context, promise_fun, promise, if_isunmodified, - if_ismodified); -} - -void PromiseBuiltinsAssembler::BranchIfFastPath(Node* native_context, - Node* promise_fun, - Node* promise, - Label* if_isunmodified, - Label* if_ismodified) { - CSA_ASSERT(this, IsNativeContext(native_context)); - CSA_ASSERT(this, - WordEqual(promise_fun, - LoadContextElement(native_context, - Context::PROMISE_FUNCTION_INDEX))); - - Node* const map = LoadMap(promise); - Node* const initial_map = - LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset); - Node* const has_initialmap = WordEqual(map, initial_map); - - GotoIfNot(has_initialmap, if_ismodified); - - Node* const initial_proto_initial_map = - LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_MAP_INDEX); - Node* const proto_map = LoadMap(LoadMapPrototype(map)); - Node* const proto_has_initialmap = - WordEqual(proto_map, initial_proto_initial_map); - - Branch(proto_has_initialmap, if_isunmodified, if_ismodified); -} - -Node* PromiseBuiltinsAssembler::AllocatePromiseResolveThenableJobInfo( - Node* thenable, Node* then, Node* resolve, Node* reject, Node* context) { - Node* const info = Allocate(PromiseResolveThenableJobInfo::kSize); - StoreMapNoWriteBarrier(info, - Heap::kPromiseResolveThenableJobInfoMapRootIndex); - StoreObjectFieldNoWriteBarrier( - info, PromiseResolveThenableJobInfo::kThenableOffset, thenable); - StoreObjectFieldNoWriteBarrier( - info, PromiseResolveThenableJobInfo::kThenOffset, then); - StoreObjectFieldNoWriteBarrier( - info, PromiseResolveThenableJobInfo::kResolveOffset, resolve); - StoreObjectFieldNoWriteBarrier( - info, PromiseResolveThenableJobInfo::kRejectOffset, reject); - StoreObjectFieldNoWriteBarrier( - info, PromiseResolveThenableJobInfo::kContextOffset, context); - return info; -} - -void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context, - Node* promise, - Node* result) { - Isolate* isolate = this->isolate(); - - Variable var_reason(this, MachineRepresentation::kTagged), - var_then(this, MachineRepresentation::kTagged); - - Label do_enqueue(this), fulfill(this), if_cycle(this, Label::kDeferred), - if_rejectpromise(this, Label::kDeferred), out(this); - - Label cycle_check(this); - GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &cycle_check); - CallRuntime(Runtime::kPromiseHookResolve, context, promise); - Goto(&cycle_check); - - Bind(&cycle_check); - // 6. If SameValue(resolution, promise) is true, then - GotoIf(SameValue(promise, result, context), &if_cycle); - - // 7. If Type(resolution) is not Object, then - GotoIf(TaggedIsSmi(result), &fulfill); - GotoIfNot(IsJSReceiver(result), &fulfill); - - Label if_nativepromise(this), if_notnativepromise(this, Label::kDeferred); - Node* const native_context = LoadNativeContext(context); - Node* const promise_fun = - LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); - BranchIfFastPath(native_context, promise_fun, result, &if_nativepromise, - &if_notnativepromise); - - // Resolution is a native promise and if it's already resolved or - // rejected, shortcircuit the resolution procedure by directly - // reusing the value from the promise. - Bind(&if_nativepromise); - { - Node* const thenable_status = - LoadObjectField(result, JSPromise::kStatusOffset); - Node* const thenable_value = - LoadObjectField(result, JSPromise::kResultOffset); - - Label if_isnotpending(this); - GotoIfNot(SmiEqual(SmiConstant(v8::Promise::kPending), thenable_status), - &if_isnotpending); - - // TODO(gsathya): Use a marker here instead of the actual then - // callback, and check for the marker in PromiseResolveThenableJob - // and perform PromiseThen. - Node* const then = - LoadContextElement(native_context, Context::PROMISE_THEN_INDEX); - var_then.Bind(then); - Goto(&do_enqueue); - - Bind(&if_isnotpending); - { - Label if_fulfilled(this), if_rejected(this); - Branch(SmiEqual(SmiConstant(v8::Promise::kFulfilled), thenable_status), - &if_fulfilled, &if_rejected); - - Bind(&if_fulfilled); - { - PromiseFulfill(context, promise, thenable_value, - v8::Promise::kFulfilled); - PromiseSetHasHandler(promise); - Goto(&out); - } - - Bind(&if_rejected); - { - Label reject(this); - Node* const has_handler = PromiseHasHandler(result); - - // Promise has already been rejected, but had no handler. - // Revoke previously triggered reject event. - GotoIf(has_handler, &reject); - CallRuntime(Runtime::kPromiseRevokeReject, context, result); - Goto(&reject); - - Bind(&reject); - // Don't cause a debug event as this case is forwarding a rejection - InternalPromiseReject(context, promise, thenable_value, false); - PromiseSetHasHandler(result); - Goto(&out); - } - } - } - - Bind(&if_notnativepromise); - { - // 8. Let then be Get(resolution, "then"). - Node* const then_str = HeapConstant(isolate->factory()->then_string()); - Callable getproperty_callable = CodeFactory::GetProperty(isolate); - Node* const then = - CallStub(getproperty_callable, context, result, then_str); - - // 9. If then is an abrupt completion, then - GotoIfException(then, &if_rejectpromise, &var_reason); - - // 11. If IsCallable(thenAction) is false, then - GotoIf(TaggedIsSmi(then), &fulfill); - Node* const then_map = LoadMap(then); - GotoIfNot(IsCallableMap(then_map), &fulfill); - var_then.Bind(then); - Goto(&do_enqueue); - } - - Bind(&do_enqueue); - { - // TODO(gsathya): Add fast path for native promises with unmodified - // PromiseThen (which don't need these resolving functions, but - // instead can just call resolve/reject directly). - Node* resolve = nullptr; - Node* reject = nullptr; - std::tie(resolve, reject) = CreatePromiseResolvingFunctions( - promise, FalseConstant(), native_context); - - Node* const info = AllocatePromiseResolveThenableJobInfo( - result, var_then.value(), resolve, reject, context); - - Label enqueue(this); - GotoIfNot(IsDebugActive(), &enqueue); - - GotoIf(TaggedIsSmi(result), &enqueue); - GotoIfNot(HasInstanceType(result, JS_PROMISE_TYPE), &enqueue); - - // Mark the dependency of the new promise on the resolution - Node* const key = - HeapConstant(isolate->factory()->promise_handled_by_symbol()); - CallRuntime(Runtime::kSetProperty, context, result, key, promise, - SmiConstant(STRICT)); - Goto(&enqueue); - - // 12. Perform EnqueueJob("PromiseJobs", - // PromiseResolveThenableJob, « promise, resolution, thenAction»). - Bind(&enqueue); - // TODO(gsathya): Move this to TF - CallRuntime(Runtime::kEnqueuePromiseResolveThenableJob, context, info); - Goto(&out); - } - - // 7.b Return FulfillPromise(promise, resolution). - Bind(&fulfill); - { - PromiseFulfill(context, promise, result, v8::Promise::kFulfilled); - Goto(&out); - } - - Bind(&if_cycle); - { - // 6.a Let selfResolutionError be a newly created TypeError object. - Node* const message_id = SmiConstant(MessageTemplate::kPromiseCyclic); - Node* const error = - CallRuntime(Runtime::kNewTypeError, context, message_id, result); - var_reason.Bind(error); - - // 6.b Return RejectPromise(promise, selfResolutionError). - Goto(&if_rejectpromise); - } - - // 9.a Return RejectPromise(promise, then.[[Value]]). - Bind(&if_rejectpromise); - { - InternalPromiseReject(context, promise, var_reason.value(), true); - Goto(&out); - } - - Bind(&out); -} - -void PromiseBuiltinsAssembler::PromiseFulfill( - Node* context, Node* promise, Node* result, - v8::Promise::PromiseState status) { - Label do_promisereset(this), debug_async_event_enqueue_recurring(this); - - Node* const status_smi = SmiConstant(static_cast<int>(status)); - Node* const deferred_promise = - LoadObjectField(promise, JSPromise::kDeferredPromiseOffset); - - GotoIf(IsUndefined(deferred_promise), &debug_async_event_enqueue_recurring); - - Node* const tasks = - status == v8::Promise::kFulfilled - ? LoadObjectField(promise, JSPromise::kFulfillReactionsOffset) - : LoadObjectField(promise, JSPromise::kRejectReactionsOffset); - - Node* const deferred_on_resolve = - LoadObjectField(promise, JSPromise::kDeferredOnResolveOffset); - Node* const deferred_on_reject = - LoadObjectField(promise, JSPromise::kDeferredOnRejectOffset); - - Node* const info = AllocatePromiseReactionJobInfo( - result, tasks, deferred_promise, deferred_on_resolve, deferred_on_reject, - context); - - CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, info); - Goto(&debug_async_event_enqueue_recurring); - - Bind(&debug_async_event_enqueue_recurring); - { - GotoIfNot(IsDebugActive(), &do_promisereset); - CallRuntime(Runtime::kDebugAsyncEventEnqueueRecurring, context, promise, - status_smi); - Goto(&do_promisereset); - } - - Bind(&do_promisereset); - { - StoreObjectField(promise, JSPromise::kStatusOffset, status_smi); - StoreObjectField(promise, JSPromise::kResultOffset, result); - StoreObjectFieldRoot(promise, JSPromise::kDeferredPromiseOffset, - Heap::kUndefinedValueRootIndex); - StoreObjectFieldRoot(promise, JSPromise::kDeferredOnResolveOffset, - Heap::kUndefinedValueRootIndex); - StoreObjectFieldRoot(promise, JSPromise::kDeferredOnRejectOffset, - Heap::kUndefinedValueRootIndex); - StoreObjectFieldRoot(promise, JSPromise::kFulfillReactionsOffset, - Heap::kUndefinedValueRootIndex); - StoreObjectFieldRoot(promise, JSPromise::kRejectReactionsOffset, - Heap::kUndefinedValueRootIndex); - } -} - -void PromiseBuiltinsAssembler::BranchIfAccessCheckFailed( - Node* context, Node* native_context, Node* promise_constructor, - Node* executor, Label* if_noaccess) { - Variable var_executor(this, MachineRepresentation::kTagged); - var_executor.Bind(executor); - Label has_access(this), call_runtime(this, Label::kDeferred); - - // If executor is a bound function, load the bound function until we've - // reached an actual function. - Label found_function(this), loop_over_bound_function(this, &var_executor); - Goto(&loop_over_bound_function); - Bind(&loop_over_bound_function); - { - Node* executor_type = LoadInstanceType(var_executor.value()); - GotoIf(InstanceTypeEqual(executor_type, JS_FUNCTION_TYPE), &found_function); - GotoIfNot(InstanceTypeEqual(executor_type, JS_BOUND_FUNCTION_TYPE), - &call_runtime); - var_executor.Bind(LoadObjectField( - var_executor.value(), JSBoundFunction::kBoundTargetFunctionOffset)); - Goto(&loop_over_bound_function); - } - - // Load the context from the function and compare it to the Promise - // constructor's context. If they match, everything is fine, otherwise, bail - // out to the runtime. - Bind(&found_function); - { - Node* function_context = - LoadObjectField(var_executor.value(), JSFunction::kContextOffset); - Node* native_function_context = LoadNativeContext(function_context); - Branch(WordEqual(native_context, native_function_context), &has_access, - &call_runtime); - } - - Bind(&call_runtime); - { - Branch(WordEqual(CallRuntime(Runtime::kAllowDynamicFunction, context, - promise_constructor), - BooleanConstant(true)), - &has_access, if_noaccess); - } - - Bind(&has_access); -} - -void PromiseBuiltinsAssembler::InternalPromiseReject(Node* context, - Node* promise, Node* value, - Node* debug_event) { - Label out(this); - GotoIfNot(IsDebugActive(), &out); - GotoIfNot(WordEqual(TrueConstant(), debug_event), &out); - CallRuntime(Runtime::kDebugPromiseReject, context, promise, value); - Goto(&out); - - Bind(&out); - InternalPromiseReject(context, promise, value, false); -} - -// This duplicates a lot of logic from PromiseRejectEvent in -// runtime-promise.cc -void PromiseBuiltinsAssembler::InternalPromiseReject(Node* context, - Node* promise, Node* value, - bool debug_event) { - Label fulfill(this), report_unhandledpromise(this), run_promise_hook(this); - - if (debug_event) { - GotoIfNot(IsDebugActive(), &run_promise_hook); - CallRuntime(Runtime::kDebugPromiseReject, context, promise, value); - Goto(&run_promise_hook); - } else { - Goto(&run_promise_hook); - } - - Bind(&run_promise_hook); - { - GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &report_unhandledpromise); - CallRuntime(Runtime::kPromiseHookResolve, context, promise); - Goto(&report_unhandledpromise); - } - - Bind(&report_unhandledpromise); - { - GotoIf(PromiseHasHandler(promise), &fulfill); - CallRuntime(Runtime::kReportPromiseReject, context, promise, value); - Goto(&fulfill); - } - - Bind(&fulfill); - PromiseFulfill(context, promise, value, v8::Promise::kRejected); -} - -// ES#sec-promise-reject-functions -// Promise Reject Functions -TF_BUILTIN(PromiseRejectClosure, PromiseBuiltinsAssembler) { - Node* const value = Parameter(1); - Node* const context = Parameter(4); - - Label out(this); - - // 3. Let alreadyResolved be F.[[AlreadyResolved]]. - int has_already_visited_slot = kAlreadyVisitedSlot; - - Node* const has_already_visited = - LoadContextElement(context, has_already_visited_slot); - - // 4. If alreadyResolved.[[Value]] is true, return undefined. - GotoIf(SmiEqual(has_already_visited, SmiConstant(1)), &out); - - // 5.Set alreadyResolved.[[Value]] to true. - StoreContextElementNoWriteBarrier(context, has_already_visited_slot, - SmiConstant(1)); - - // 2. Let promise be F.[[Promise]]. - Node* const promise = - LoadContextElement(context, IntPtrConstant(kPromiseSlot)); - Node* const debug_event = - LoadContextElement(context, IntPtrConstant(kDebugEventSlot)); - - InternalPromiseReject(context, promise, value, debug_event); - Return(UndefinedConstant()); - - Bind(&out); - Return(UndefinedConstant()); -} - -TF_BUILTIN(PromiseConstructor, PromiseBuiltinsAssembler) { - Node* const executor = Parameter(1); - Node* const new_target = Parameter(2); - Node* const context = Parameter(4); - Isolate* isolate = this->isolate(); - - Label if_targetisundefined(this, Label::kDeferred); - - GotoIf(IsUndefined(new_target), &if_targetisundefined); - - Label if_notcallable(this, Label::kDeferred); - - GotoIf(TaggedIsSmi(executor), &if_notcallable); - - Node* const executor_map = LoadMap(executor); - GotoIfNot(IsCallableMap(executor_map), &if_notcallable); - - Node* const native_context = LoadNativeContext(context); - Node* const promise_fun = - LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); - Node* const is_debug_active = IsDebugActive(); - Label if_targetisnotmodified(this), - if_targetismodified(this, Label::kDeferred), run_executor(this), - debug_push(this), if_noaccess(this, Label::kDeferred); - - BranchIfAccessCheckFailed(context, native_context, promise_fun, executor, - &if_noaccess); - - Branch(WordEqual(promise_fun, new_target), &if_targetisnotmodified, - &if_targetismodified); - - Variable var_result(this, MachineRepresentation::kTagged), - var_reject_call(this, MachineRepresentation::kTagged), - var_reason(this, MachineRepresentation::kTagged); - - Bind(&if_targetisnotmodified); - { - Node* const instance = AllocateAndInitJSPromise(context); - var_result.Bind(instance); - Goto(&debug_push); - } - - Bind(&if_targetismodified); - { - ConstructorBuiltinsAssembler constructor_assembler(this->state()); - Node* const instance = constructor_assembler.EmitFastNewObject( - context, promise_fun, new_target); - PromiseInit(instance); - var_result.Bind(instance); - - GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &debug_push); - CallRuntime(Runtime::kPromiseHookInit, context, instance, - UndefinedConstant()); - Goto(&debug_push); - } - - Bind(&debug_push); - { - GotoIfNot(is_debug_active, &run_executor); - CallRuntime(Runtime::kDebugPushPromise, context, var_result.value()); - Goto(&run_executor); - } - - Bind(&run_executor); - { - Label out(this), if_rejectpromise(this), debug_pop(this, Label::kDeferred); - - Node *resolve, *reject; - std::tie(resolve, reject) = CreatePromiseResolvingFunctions( - var_result.value(), TrueConstant(), native_context); - Callable call_callable = CodeFactory::Call(isolate); - - Node* const maybe_exception = CallJS(call_callable, context, executor, - UndefinedConstant(), resolve, reject); - - GotoIfException(maybe_exception, &if_rejectpromise, &var_reason); - Branch(is_debug_active, &debug_pop, &out); - - Bind(&if_rejectpromise); - { - Callable call_callable = CodeFactory::Call(isolate); - CallJS(call_callable, context, reject, UndefinedConstant(), - var_reason.value()); - Branch(is_debug_active, &debug_pop, &out); - } - - Bind(&debug_pop); - { - CallRuntime(Runtime::kDebugPopPromise, context); - Goto(&out); - } - Bind(&out); - Return(var_result.value()); - } - - // 1. If NewTarget is undefined, throw a TypeError exception. - Bind(&if_targetisundefined); - { - Node* const message_id = SmiConstant(MessageTemplate::kNotAPromise); - CallRuntime(Runtime::kThrowTypeError, context, message_id, new_target); - Unreachable(); - } - - // 2. If IsCallable(executor) is false, throw a TypeError exception. - Bind(&if_notcallable); - { - Node* const message_id = - SmiConstant(MessageTemplate::kResolverNotAFunction); - CallRuntime(Runtime::kThrowTypeError, context, message_id, executor); - Unreachable(); - } - - // Silently fail if the stack looks fishy. - Bind(&if_noaccess); - { - Node* const counter_id = - SmiConstant(v8::Isolate::kPromiseConstructorReturnedUndefined); - CallRuntime(Runtime::kIncrementUseCounter, context, counter_id); - Return(UndefinedConstant()); - } -} - -TF_BUILTIN(PromiseInternalConstructor, PromiseBuiltinsAssembler) { - Node* const parent = Parameter(1); - Node* const context = Parameter(4); - Return(AllocateAndInitJSPromise(context, parent)); -} - -TF_BUILTIN(IsPromise, PromiseBuiltinsAssembler) { - Node* const maybe_promise = Parameter(1); - Label if_notpromise(this, Label::kDeferred); - - GotoIf(TaggedIsSmi(maybe_promise), &if_notpromise); - - Node* const result = - SelectBooleanConstant(HasInstanceType(maybe_promise, JS_PROMISE_TYPE)); - Return(result); - - Bind(&if_notpromise); - Return(FalseConstant()); -} - -// ES#sec-promise.prototype.then -// Promise.prototype.catch ( onFulfilled, onRejected ) -TF_BUILTIN(PromiseThen, PromiseBuiltinsAssembler) { - // 1. Let promise be the this value. - Node* const promise = Parameter(0); - Node* const on_resolve = Parameter(1); - Node* const on_reject = Parameter(2); - Node* const context = Parameter(5); - - Node* const result = - InternalPromiseThen(context, promise, on_resolve, on_reject); - Return(result); -} - -// ES#sec-promise-resolve-functions -// Promise Resolve Functions -TF_BUILTIN(PromiseResolveClosure, PromiseBuiltinsAssembler) { - Node* const value = Parameter(1); - Node* const context = Parameter(4); - - Label out(this); - - // 3. Let alreadyResolved be F.[[AlreadyResolved]]. - int has_already_visited_slot = kAlreadyVisitedSlot; - - Node* const has_already_visited = - LoadContextElement(context, has_already_visited_slot); - - // 4. If alreadyResolved.[[Value]] is true, return undefined. - GotoIf(SmiEqual(has_already_visited, SmiConstant(1)), &out); - - // 5.Set alreadyResolved.[[Value]] to true. - StoreContextElementNoWriteBarrier(context, has_already_visited_slot, - SmiConstant(1)); - - // 2. Let promise be F.[[Promise]]. - Node* const promise = - LoadContextElement(context, IntPtrConstant(kPromiseSlot)); - - InternalResolvePromise(context, promise, value); - Return(UndefinedConstant()); - - Bind(&out); - Return(UndefinedConstant()); -} - -TF_BUILTIN(ResolvePromise, PromiseBuiltinsAssembler) { - Node* const promise = Parameter(1); - Node* const result = Parameter(2); - Node* const context = Parameter(5); - - InternalResolvePromise(context, promise, result); - Return(UndefinedConstant()); -} - -TF_BUILTIN(PromiseHandleReject, PromiseBuiltinsAssembler) { - typedef PromiseHandleRejectDescriptor Descriptor; - - Node* const promise = Parameter(Descriptor::kPromise); - Node* const on_reject = Parameter(Descriptor::kOnReject); - Node* const exception = Parameter(Descriptor::kException); - Node* const context = Parameter(Descriptor::kContext); - - Callable call_callable = CodeFactory::Call(isolate()); - Variable var_unused(this, MachineRepresentation::kTagged); - - Label if_internalhandler(this), if_customhandler(this, Label::kDeferred); - Branch(IsUndefined(on_reject), &if_internalhandler, &if_customhandler); - - Bind(&if_internalhandler); - { - InternalPromiseReject(context, promise, exception, false); - Return(UndefinedConstant()); - } - - Bind(&if_customhandler); - { - CallJS(call_callable, context, on_reject, UndefinedConstant(), exception); - Return(UndefinedConstant()); - } -} - -TF_BUILTIN(PromiseHandle, PromiseBuiltinsAssembler) { - Node* const value = Parameter(1); - Node* const handler = Parameter(2); - Node* const deferred_promise = Parameter(3); - Node* const deferred_on_resolve = Parameter(4); - Node* const deferred_on_reject = Parameter(5); - Node* const context = Parameter(8); - Isolate* isolate = this->isolate(); - - Variable var_reason(this, MachineRepresentation::kTagged); - - Node* const is_debug_active = IsDebugActive(); - Label run_handler(this), if_rejectpromise(this), promisehook_before(this), - promisehook_after(this), debug_pop(this); - - GotoIfNot(is_debug_active, &promisehook_before); - CallRuntime(Runtime::kDebugPushPromise, context, deferred_promise); - Goto(&promisehook_before); - - Bind(&promisehook_before); - { - GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &run_handler); - CallRuntime(Runtime::kPromiseHookBefore, context, deferred_promise); - Goto(&run_handler); - } - - Bind(&run_handler); - { - Label if_defaulthandler(this), if_callablehandler(this), - if_internalhandler(this), if_customhandler(this, Label::kDeferred); - Variable var_result(this, MachineRepresentation::kTagged); - - Branch(IsSymbol(handler), &if_defaulthandler, &if_callablehandler); - - Bind(&if_defaulthandler); - { - Label if_resolve(this), if_reject(this); - Node* const default_resolve_handler_symbol = HeapConstant( - isolate->factory()->promise_default_resolve_handler_symbol()); - Branch(WordEqual(default_resolve_handler_symbol, handler), &if_resolve, - &if_reject); - - Bind(&if_resolve); - { - var_result.Bind(value); - Branch(IsUndefined(deferred_on_resolve), &if_internalhandler, - &if_customhandler); - } - - Bind(&if_reject); - { - var_reason.Bind(value); - Goto(&if_rejectpromise); - } - } - - Bind(&if_callablehandler); - { - Callable call_callable = CodeFactory::Call(isolate); - Node* const result = - CallJS(call_callable, context, handler, UndefinedConstant(), value); - var_result.Bind(result); - GotoIfException(result, &if_rejectpromise, &var_reason); - Branch(IsUndefined(deferred_on_resolve), &if_internalhandler, - &if_customhandler); - } - - Bind(&if_internalhandler); - InternalResolvePromise(context, deferred_promise, var_result.value()); - Goto(&promisehook_after); - - Bind(&if_customhandler); - { - Callable call_callable = CodeFactory::Call(isolate); - Node* const maybe_exception = - CallJS(call_callable, context, deferred_on_resolve, - UndefinedConstant(), var_result.value()); - GotoIfException(maybe_exception, &if_rejectpromise, &var_reason); - Goto(&promisehook_after); - } - } - - Bind(&if_rejectpromise); - { - Callable promise_handle_reject = CodeFactory::PromiseHandleReject(isolate); - CallStub(promise_handle_reject, context, deferred_promise, - deferred_on_reject, var_reason.value()); - Goto(&promisehook_after); - } - - Bind(&promisehook_after); - { - GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &debug_pop); - CallRuntime(Runtime::kPromiseHookAfter, context, deferred_promise); - Goto(&debug_pop); - } - - Bind(&debug_pop); - { - Label out(this); - - GotoIfNot(is_debug_active, &out); - CallRuntime(Runtime::kDebugPopPromise, context); - Goto(&out); - - Bind(&out); - Return(UndefinedConstant()); - } -} - -// ES#sec-promise.prototype.catch -// Promise.prototype.catch ( onRejected ) -TF_BUILTIN(PromiseCatch, PromiseBuiltinsAssembler) { - // 1. Let promise be the this value. - Node* const promise = Parameter(0); - Node* const on_resolve = UndefinedConstant(); - Node* const on_reject = Parameter(1); - Node* const context = Parameter(4); - - Label if_internalthen(this), if_customthen(this, Label::kDeferred); - GotoIf(TaggedIsSmi(promise), &if_customthen); - BranchIfFastPath(context, promise, &if_internalthen, &if_customthen); - - Bind(&if_internalthen); - { - Node* const result = - InternalPromiseThen(context, promise, on_resolve, on_reject); - Return(result); - } - - Bind(&if_customthen); - { - 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); - Node* const result = - CallJS(call_callable, context, then, promise, on_resolve, on_reject); - Return(result); - } -} - -TF_BUILTIN(PromiseResolve, PromiseBuiltinsAssembler) { - // 1. Let C be the this value. - Node* receiver = Parameter(0); - Node* value = Parameter(1); - Node* context = Parameter(4); - Isolate* isolate = this->isolate(); - - // 2. If Type(C) is not Object, throw a TypeError exception. - ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject, - "PromiseResolve"); - - Label if_valueisnativepromise(this), if_valueisnotnativepromise(this), - if_valueisnotpromise(this); - - // 3.If IsPromise(x) is true, then - GotoIf(TaggedIsSmi(value), &if_valueisnotpromise); - - // This shortcircuits the constructor lookups. - GotoIfNot(HasInstanceType(value, JS_PROMISE_TYPE), &if_valueisnotpromise); - - // This adds a fast path as non-subclassed native promises don't have - // an observable constructor lookup. - Node* const native_context = LoadNativeContext(context); - Node* const promise_fun = - LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); - BranchIfFastPath(native_context, promise_fun, value, &if_valueisnativepromise, - &if_valueisnotnativepromise); - - Bind(&if_valueisnativepromise); - { - GotoIfNot(WordEqual(promise_fun, receiver), &if_valueisnotnativepromise); - Return(value); - } - - // At this point, value or/and receiver are not native promises, but - // they could be of the same subclass. - Bind(&if_valueisnotnativepromise); - { - // 3.a Let xConstructor be ? Get(x, "constructor"). - // The constructor lookup is observable. - Node* const constructor_str = - HeapConstant(isolate->factory()->constructor_string()); - Callable getproperty_callable = CodeFactory::GetProperty(isolate); - Node* const constructor = - CallStub(getproperty_callable, context, value, constructor_str); - - // 3.b If SameValue(xConstructor, C) is true, return x. - GotoIfNot(SameValue(constructor, receiver, context), &if_valueisnotpromise); - - Return(value); - } - - Bind(&if_valueisnotpromise); - { - Label if_nativepromise(this), if_notnativepromise(this); - BranchIfFastPath(context, receiver, &if_nativepromise, - &if_notnativepromise); - - // This adds a fast path for native promises that don't need to - // create NewPromiseCapability. - Bind(&if_nativepromise); - { - Label do_resolve(this); - - Node* const result = AllocateAndInitJSPromise(context); - InternalResolvePromise(context, result, value); - Return(result); - } - - Bind(&if_notnativepromise); - { - // 4. Let promiseCapability be ? NewPromiseCapability(C). - Node* const capability = NewPromiseCapability(context, receiver); - - // 5. Perform ? Call(promiseCapability.[[Resolve]], undefined, « x »). - Callable call_callable = CodeFactory::Call(isolate); - Node* const resolve = - LoadObjectField(capability, JSPromiseCapability::kResolveOffset); - CallJS(call_callable, context, resolve, UndefinedConstant(), value); - - // 6. Return promiseCapability.[[Promise]]. - Node* const result = - LoadObjectField(capability, JSPromiseCapability::kPromiseOffset); - Return(result); - } - } -} - -TF_BUILTIN(PromiseGetCapabilitiesExecutor, PromiseBuiltinsAssembler) { - Node* const resolve = Parameter(1); - Node* const reject = Parameter(2); - Node* const context = Parameter(5); - - Node* const capability = LoadContextElement(context, 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); - CallRuntime(Runtime::kThrowTypeError, context, message); - Unreachable(); -} - -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(PromiseReject, PromiseBuiltinsAssembler) { - // 1. Let C be the this value. - Node* const receiver = Parameter(0); - Node* const reason = Parameter(1); - Node* const context = Parameter(4); - - // 2. If Type(C) is not Object, throw a TypeError exception. - ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject, - "PromiseReject"); - - Label if_nativepromise(this), if_custompromise(this, Label::kDeferred); - Node* const native_context = LoadNativeContext(context); - Node* const promise_fun = - LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); - Branch(WordEqual(promise_fun, receiver), &if_nativepromise, - &if_custompromise); - - Bind(&if_nativepromise); - { - Node* const promise = AllocateAndSetJSPromise( - context, SmiConstant(v8::Promise::kRejected), reason); - CallRuntime(Runtime::kPromiseRejectEventFromStack, context, promise, - reason); - Return(promise); - } - - Bind(&if_custompromise); - { - // 3. Let promiseCapability be ? NewPromiseCapability(C). - Node* const capability = NewPromiseCapability(context, receiver); - - // 4. Perform ? Call(promiseCapability.[[Reject]], undefined, « r »). - Node* const reject = - LoadObjectField(capability, JSPromiseCapability::kRejectOffset); - Callable call_callable = CodeFactory::Call(isolate()); - CallJS(call_callable, context, reject, UndefinedConstant(), reason); - - // 5. Return promiseCapability.[[Promise]]. - Node* const promise = - LoadObjectField(capability, JSPromiseCapability::kPromiseOffset); - Return(promise); - } -} - -TF_BUILTIN(InternalPromiseReject, PromiseBuiltinsAssembler) { - Node* const promise = Parameter(1); - Node* const reason = Parameter(2); - Node* const debug_event = Parameter(3); - Node* const context = Parameter(6); - - InternalPromiseReject(context, promise, reason, debug_event); - 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); - Unreachable(); -} - -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 |