| Index: src/builtins/builtins-promise.cc
|
| diff --git a/src/builtins/builtins-promise.cc b/src/builtins/builtins-promise.cc
|
| deleted file mode 100644
|
| index 285850c2bc8baba5acfa24ac0b5961777cd3c110..0000000000000000000000000000000000000000
|
| --- a/src/builtins/builtins-promise.cc
|
| +++ /dev/null
|
| @@ -1,1762 +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 {
|
| -
|
| -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));
|
| -}
|
| -
|
| -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));
|
| -
|
| - 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 =
|
| - GetProperty(context, object, isolate->factory()->constructor_string());
|
| -
|
| - // 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 =
|
| - GetProperty(context, constructor, isolate->factory()->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), &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 =
|
| - GetProperty(context, result, isolate->factory()->then_string());
|
| -
|
| - // 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);
|
| - {
|
| - // Don't cause a debug event as this case is forwarding a rejection.
|
| - InternalPromiseReject(context, promise, var_reason.value(), false);
|
| - 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);
|
| - {
|
| - Node* const then =
|
| - GetProperty(context, promise, isolate()->factory()->then_string());
|
| - 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");
|
| -
|
| - Node* const native_context = LoadNativeContext(context);
|
| - Node* const promise_fun =
|
| - LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
|
| -
|
| - 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.
|
| - 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 =
|
| - GetProperty(context, value, isolate->factory()->constructor_string());
|
| -
|
| - // 3.b If SameValue(xConstructor, C) is true, return x.
|
| - GotoIfNot(SameValue(constructor, receiver), &if_valueisnotpromise);
|
| -
|
| - Return(value);
|
| - }
|
| -
|
| - Bind(&if_valueisnotpromise);
|
| - {
|
| - Label if_nativepromise(this), if_notnativepromise(this);
|
| - Branch(WordEqual(promise_fun, receiver), &if_nativepromise,
|
| - &if_notnativepromise);
|
| -
|
| - // This adds a fast path for native promises that don't need to
|
| - // create NewPromiseCapability.
|
| - Bind(&if_nativepromise);
|
| - {
|
| - 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);
|
| - {
|
| - Node* const then =
|
| - GetProperty(context, promise, isolate()->factory()->then_string());
|
| - 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
|
|
|