Move MicrotaskQueue stuff of InternalBuiltinsAssembler to a separate class
This CL moves EnqueueMicrotask and RunMicrotasks builtins out of InternalBuiltinAssembler to a separate class with no logic change. This is a trivial part of the following "Implement Faster MicrotaskQueue Step 2" https://crrev.com/c/1290751 in order to clean up the diff for easier review, and also for unblocking other CLs. Change-Id: I34dda8e599ffbf4db4bae07d3fd8ea3d6fadeb6b Reviewed-on: https://chromium-review.googlesource.com/c/1328548 Reviewed-by: Adam Klein <adamk@chromium.org> Commit-Queue: Taiju Tsuiki <tzik@chromium.org> Cr-Commit-Position: refs/heads/master@{#57411}
This commit is contained in:
parent
8a7f256796
commit
0d1ff784af
1
BUILD.gn
1
BUILD.gn
@ -1383,6 +1383,7 @@ v8_source_set("v8_initializers") {
|
||||
"src/builtins/builtins-lazy-gen.h",
|
||||
"src/builtins/builtins-math-gen.cc",
|
||||
"src/builtins/builtins-math-gen.h",
|
||||
"src/builtins/builtins-microtask-queue-gen.cc",
|
||||
"src/builtins/builtins-number-gen.cc",
|
||||
"src/builtins/builtins-object-gen.cc",
|
||||
"src/builtins/builtins-promise-gen.cc",
|
||||
|
@ -624,46 +624,6 @@ class InternalBuiltinsAssembler : public CodeStubAssembler {
|
||||
explicit InternalBuiltinsAssembler(compiler::CodeAssemblerState* state)
|
||||
: CodeStubAssembler(state) {}
|
||||
|
||||
TNode<MicrotaskQueue> GetDefaultMicrotaskQueue();
|
||||
TNode<IntPtrT> GetPendingMicrotaskCount(
|
||||
TNode<MicrotaskQueue> microtask_queue);
|
||||
void SetPendingMicrotaskCount(TNode<MicrotaskQueue> microtask_queue,
|
||||
TNode<IntPtrT> new_num_tasks);
|
||||
TNode<FixedArray> GetQueuedMicrotasks(TNode<MicrotaskQueue> microtask_queue);
|
||||
void SetQueuedMicrotasks(TNode<MicrotaskQueue> microtask_queue,
|
||||
TNode<FixedArray> new_queue);
|
||||
|
||||
TNode<Context> GetCurrentContext();
|
||||
void SetCurrentContext(TNode<Context> context);
|
||||
|
||||
void EnterMicrotaskContext(TNode<Context> context);
|
||||
void LeaveMicrotaskContext();
|
||||
|
||||
void RunPromiseHook(Runtime::FunctionId id, TNode<Context> context,
|
||||
SloppyTNode<HeapObject> promise_or_capability);
|
||||
|
||||
TNode<Object> GetPendingException() {
|
||||
auto ref = ExternalReference::Create(kPendingExceptionAddress, isolate());
|
||||
return TNode<Object>::UncheckedCast(
|
||||
Load(MachineType::AnyTagged(), ExternalConstant(ref)));
|
||||
}
|
||||
void ClearPendingException() {
|
||||
auto ref = ExternalReference::Create(kPendingExceptionAddress, isolate());
|
||||
StoreNoWriteBarrier(MachineRepresentation::kTagged, ExternalConstant(ref),
|
||||
TheHoleConstant());
|
||||
}
|
||||
|
||||
TNode<Object> GetScheduledException() {
|
||||
auto ref = ExternalReference::scheduled_exception_address(isolate());
|
||||
return TNode<Object>::UncheckedCast(
|
||||
Load(MachineType::AnyTagged(), ExternalConstant(ref)));
|
||||
}
|
||||
void ClearScheduledException() {
|
||||
auto ref = ExternalReference::scheduled_exception_address(isolate());
|
||||
StoreNoWriteBarrier(MachineRepresentation::kTagged, ExternalConstant(ref),
|
||||
TheHoleConstant());
|
||||
}
|
||||
|
||||
template <typename Descriptor>
|
||||
void GenerateAdaptorWithExitFrameType(
|
||||
Builtins::ExitFrameType exit_frame_type);
|
||||
@ -718,441 +678,6 @@ TF_BUILTIN(AdaptorWithBuiltinExitFrame, InternalBuiltinsAssembler) {
|
||||
GenerateAdaptorWithExitFrameType<Descriptor>(Builtins::BUILTIN_EXIT);
|
||||
}
|
||||
|
||||
TNode<MicrotaskQueue> InternalBuiltinsAssembler::GetDefaultMicrotaskQueue() {
|
||||
return TNode<MicrotaskQueue>::UncheckedCast(
|
||||
LoadRoot(RootIndex::kDefaultMicrotaskQueue));
|
||||
}
|
||||
|
||||
TNode<IntPtrT> InternalBuiltinsAssembler::GetPendingMicrotaskCount(
|
||||
TNode<MicrotaskQueue> microtask_queue) {
|
||||
TNode<IntPtrT> result = LoadAndUntagObjectField(
|
||||
microtask_queue, MicrotaskQueue::kPendingMicrotaskCountOffset);
|
||||
return result;
|
||||
}
|
||||
|
||||
void InternalBuiltinsAssembler::SetPendingMicrotaskCount(
|
||||
TNode<MicrotaskQueue> microtask_queue, TNode<IntPtrT> new_num_tasks) {
|
||||
StoreObjectField(microtask_queue,
|
||||
MicrotaskQueue::kPendingMicrotaskCountOffset,
|
||||
SmiFromIntPtr(new_num_tasks));
|
||||
}
|
||||
|
||||
TNode<FixedArray> InternalBuiltinsAssembler::GetQueuedMicrotasks(
|
||||
TNode<MicrotaskQueue> microtask_queue) {
|
||||
return LoadObjectField<FixedArray>(microtask_queue,
|
||||
MicrotaskQueue::kQueueOffset);
|
||||
}
|
||||
|
||||
void InternalBuiltinsAssembler::SetQueuedMicrotasks(
|
||||
TNode<MicrotaskQueue> microtask_queue, TNode<FixedArray> new_queue) {
|
||||
StoreObjectField(microtask_queue, MicrotaskQueue::kQueueOffset, new_queue);
|
||||
}
|
||||
|
||||
TNode<Context> InternalBuiltinsAssembler::GetCurrentContext() {
|
||||
auto ref = ExternalReference::Create(kContextAddress, isolate());
|
||||
return TNode<Context>::UncheckedCast(
|
||||
Load(MachineType::AnyTagged(), ExternalConstant(ref)));
|
||||
}
|
||||
|
||||
void InternalBuiltinsAssembler::SetCurrentContext(TNode<Context> context) {
|
||||
auto ref = ExternalReference::Create(kContextAddress, isolate());
|
||||
StoreNoWriteBarrier(MachineRepresentation::kTagged, ExternalConstant(ref),
|
||||
context);
|
||||
}
|
||||
|
||||
void InternalBuiltinsAssembler::EnterMicrotaskContext(
|
||||
TNode<Context> native_context) {
|
||||
CSA_ASSERT(this, IsNativeContext(native_context));
|
||||
|
||||
auto ref = ExternalReference::handle_scope_implementer_address(isolate());
|
||||
Node* const hsi = Load(MachineType::Pointer(), ExternalConstant(ref));
|
||||
StoreNoWriteBarrier(
|
||||
MachineType::PointerRepresentation(), hsi,
|
||||
IntPtrConstant(HandleScopeImplementerOffsets::kMicrotaskContext),
|
||||
BitcastTaggedToWord(native_context));
|
||||
|
||||
// Load mirrored std::vector length from
|
||||
// HandleScopeImplementer::entered_contexts_count_
|
||||
auto type = kSizetSize == 8 ? MachineType::Uint64() : MachineType::Uint32();
|
||||
Node* entered_contexts_length = Load(
|
||||
type, hsi,
|
||||
IntPtrConstant(HandleScopeImplementerOffsets::kEnteredContextsCount));
|
||||
|
||||
auto rep = kSizetSize == 8 ? MachineRepresentation::kWord64
|
||||
: MachineRepresentation::kWord32;
|
||||
|
||||
StoreNoWriteBarrier(
|
||||
rep, hsi,
|
||||
IntPtrConstant(
|
||||
HandleScopeImplementerOffsets::kEnteredContextCountDuringMicrotasks),
|
||||
entered_contexts_length);
|
||||
}
|
||||
|
||||
void InternalBuiltinsAssembler::LeaveMicrotaskContext() {
|
||||
auto ref = ExternalReference::handle_scope_implementer_address(isolate());
|
||||
|
||||
Node* const hsi = Load(MachineType::Pointer(), ExternalConstant(ref));
|
||||
StoreNoWriteBarrier(
|
||||
MachineType::PointerRepresentation(), hsi,
|
||||
IntPtrConstant(HandleScopeImplementerOffsets::kMicrotaskContext),
|
||||
IntPtrConstant(0));
|
||||
if (kSizetSize == 4) {
|
||||
StoreNoWriteBarrier(
|
||||
MachineRepresentation::kWord32, hsi,
|
||||
IntPtrConstant(HandleScopeImplementerOffsets::
|
||||
kEnteredContextCountDuringMicrotasks),
|
||||
Int32Constant(0));
|
||||
} else {
|
||||
StoreNoWriteBarrier(
|
||||
MachineRepresentation::kWord64, hsi,
|
||||
IntPtrConstant(HandleScopeImplementerOffsets::
|
||||
kEnteredContextCountDuringMicrotasks),
|
||||
Int64Constant(0));
|
||||
}
|
||||
}
|
||||
|
||||
void InternalBuiltinsAssembler::RunPromiseHook(
|
||||
Runtime::FunctionId id, TNode<Context> context,
|
||||
SloppyTNode<HeapObject> promise_or_capability) {
|
||||
Label hook(this, Label::kDeferred), done_hook(this);
|
||||
Branch(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(), &hook,
|
||||
&done_hook);
|
||||
BIND(&hook);
|
||||
{
|
||||
// Get to the underlying JSPromise instance.
|
||||
TNode<HeapObject> promise = Select<HeapObject>(
|
||||
IsPromiseCapability(promise_or_capability),
|
||||
[=] {
|
||||
return CAST(LoadObjectField(promise_or_capability,
|
||||
PromiseCapability::kPromiseOffset));
|
||||
},
|
||||
|
||||
[=] { return promise_or_capability; });
|
||||
GotoIf(IsUndefined(promise), &done_hook);
|
||||
CallRuntime(id, context, promise);
|
||||
Goto(&done_hook);
|
||||
}
|
||||
BIND(&done_hook);
|
||||
}
|
||||
|
||||
TF_BUILTIN(EnqueueMicrotask, InternalBuiltinsAssembler) {
|
||||
Node* microtask = Parameter(Descriptor::kMicrotask);
|
||||
|
||||
TNode<MicrotaskQueue> microtask_queue = GetDefaultMicrotaskQueue();
|
||||
TNode<IntPtrT> num_tasks = GetPendingMicrotaskCount(microtask_queue);
|
||||
TNode<IntPtrT> new_num_tasks = IntPtrAdd(num_tasks, IntPtrConstant(1));
|
||||
TNode<FixedArray> queue = GetQueuedMicrotasks(microtask_queue);
|
||||
TNode<IntPtrT> queue_length = LoadAndUntagFixedArrayBaseLength(queue);
|
||||
|
||||
Label if_append(this), if_grow(this), done(this);
|
||||
Branch(WordEqual(num_tasks, queue_length), &if_grow, &if_append);
|
||||
|
||||
BIND(&if_grow);
|
||||
{
|
||||
// Determine the new queue length and check if we need to allocate
|
||||
// in large object space (instead of just going to new space, where
|
||||
// we also know that we don't need any write barriers for setting
|
||||
// up the new queue object).
|
||||
Label if_newspace(this), if_lospace(this, Label::kDeferred);
|
||||
TNode<IntPtrT> new_queue_length =
|
||||
IntPtrMax(IntPtrConstant(8), IntPtrAdd(num_tasks, num_tasks));
|
||||
Branch(IntPtrLessThanOrEqual(new_queue_length,
|
||||
IntPtrConstant(FixedArray::kMaxRegularLength)),
|
||||
&if_newspace, &if_lospace);
|
||||
|
||||
BIND(&if_newspace);
|
||||
{
|
||||
// This is the likely case where the new queue fits into new space,
|
||||
// and thus we don't need any write barriers for initializing it.
|
||||
TNode<FixedArray> new_queue =
|
||||
CAST(AllocateFixedArray(PACKED_ELEMENTS, new_queue_length));
|
||||
CopyFixedArrayElements(PACKED_ELEMENTS, queue, new_queue, num_tasks,
|
||||
SKIP_WRITE_BARRIER);
|
||||
StoreFixedArrayElement(new_queue, num_tasks, microtask,
|
||||
SKIP_WRITE_BARRIER);
|
||||
FillFixedArrayWithValue(PACKED_ELEMENTS, new_queue, new_num_tasks,
|
||||
new_queue_length, RootIndex::kUndefinedValue);
|
||||
SetQueuedMicrotasks(microtask_queue, new_queue);
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&if_lospace);
|
||||
{
|
||||
// The fallback case where the new queue ends up in large object space.
|
||||
TNode<FixedArray> new_queue = CAST(AllocateFixedArray(
|
||||
PACKED_ELEMENTS, new_queue_length, INTPTR_PARAMETERS,
|
||||
AllocationFlag::kAllowLargeObjectAllocation));
|
||||
CopyFixedArrayElements(PACKED_ELEMENTS, queue, new_queue, num_tasks);
|
||||
StoreFixedArrayElement(new_queue, num_tasks, microtask);
|
||||
FillFixedArrayWithValue(PACKED_ELEMENTS, new_queue, new_num_tasks,
|
||||
new_queue_length, RootIndex::kUndefinedValue);
|
||||
SetQueuedMicrotasks(microtask_queue, new_queue);
|
||||
Goto(&done);
|
||||
}
|
||||
}
|
||||
|
||||
BIND(&if_append);
|
||||
{
|
||||
StoreFixedArrayElement(queue, num_tasks, microtask);
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&done);
|
||||
SetPendingMicrotaskCount(microtask_queue, new_num_tasks);
|
||||
Return(UndefinedConstant());
|
||||
}
|
||||
|
||||
TF_BUILTIN(RunMicrotasks, InternalBuiltinsAssembler) {
|
||||
// Load the current context from the isolate.
|
||||
TNode<Context> current_context = GetCurrentContext();
|
||||
TNode<MicrotaskQueue> microtask_queue = GetDefaultMicrotaskQueue();
|
||||
|
||||
Label init_queue_loop(this), done_init_queue_loop(this);
|
||||
Goto(&init_queue_loop);
|
||||
BIND(&init_queue_loop);
|
||||
{
|
||||
TVARIABLE(IntPtrT, index, IntPtrConstant(0));
|
||||
Label loop(this, &index), loop_next(this);
|
||||
|
||||
TNode<IntPtrT> num_tasks = GetPendingMicrotaskCount(microtask_queue);
|
||||
GotoIf(IntPtrEqual(num_tasks, IntPtrConstant(0)), &done_init_queue_loop);
|
||||
|
||||
TNode<FixedArray> queue = GetQueuedMicrotasks(microtask_queue);
|
||||
|
||||
CSA_ASSERT(this, IntPtrGreaterThanOrEqual(
|
||||
LoadAndUntagFixedArrayBaseLength(queue), num_tasks));
|
||||
CSA_ASSERT(this, IntPtrGreaterThan(num_tasks, IntPtrConstant(0)));
|
||||
|
||||
SetQueuedMicrotasks(microtask_queue, EmptyFixedArrayConstant());
|
||||
SetPendingMicrotaskCount(microtask_queue, IntPtrConstant(0));
|
||||
|
||||
Goto(&loop);
|
||||
BIND(&loop);
|
||||
{
|
||||
TNode<HeapObject> microtask =
|
||||
CAST(LoadFixedArrayElement(queue, index.value()));
|
||||
index = IntPtrAdd(index.value(), IntPtrConstant(1));
|
||||
|
||||
CSA_ASSERT(this, TaggedIsNotSmi(microtask));
|
||||
|
||||
StoreRoot(RootIndex::kCurrentMicrotask, microtask);
|
||||
TNode<Map> microtask_map = LoadMap(microtask);
|
||||
TNode<Int32T> microtask_type = LoadMapInstanceType(microtask_map);
|
||||
|
||||
VARIABLE(var_exception, MachineRepresentation::kTagged,
|
||||
TheHoleConstant());
|
||||
Label if_exception(this, Label::kDeferred);
|
||||
Label is_callable(this), is_callback(this),
|
||||
is_promise_fulfill_reaction_job(this),
|
||||
is_promise_reject_reaction_job(this),
|
||||
is_promise_resolve_thenable_job(this),
|
||||
is_weak_factory_cleanup_job(this),
|
||||
is_unreachable(this, Label::kDeferred);
|
||||
|
||||
int32_t case_values[] = {CALLABLE_TASK_TYPE,
|
||||
CALLBACK_TASK_TYPE,
|
||||
PROMISE_FULFILL_REACTION_JOB_TASK_TYPE,
|
||||
PROMISE_REJECT_REACTION_JOB_TASK_TYPE,
|
||||
PROMISE_RESOLVE_THENABLE_JOB_TASK_TYPE,
|
||||
WEAK_FACTORY_CLEANUP_JOB_TASK_TYPE};
|
||||
Label* case_labels[] = {&is_callable,
|
||||
&is_callback,
|
||||
&is_promise_fulfill_reaction_job,
|
||||
&is_promise_reject_reaction_job,
|
||||
&is_promise_resolve_thenable_job,
|
||||
&is_weak_factory_cleanup_job};
|
||||
static_assert(arraysize(case_values) == arraysize(case_labels), "");
|
||||
Switch(microtask_type, &is_unreachable, case_values, case_labels,
|
||||
arraysize(case_labels));
|
||||
|
||||
BIND(&is_callable);
|
||||
{
|
||||
// Enter the context of the {microtask}.
|
||||
TNode<Context> microtask_context =
|
||||
LoadObjectField<Context>(microtask, CallableTask::kContextOffset);
|
||||
TNode<Context> native_context = LoadNativeContext(microtask_context);
|
||||
|
||||
CSA_ASSERT(this, IsNativeContext(native_context));
|
||||
EnterMicrotaskContext(native_context);
|
||||
SetCurrentContext(native_context);
|
||||
|
||||
TNode<JSReceiver> callable = LoadObjectField<JSReceiver>(
|
||||
microtask, CallableTask::kCallableOffset);
|
||||
Node* const result = CallJS(
|
||||
CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
|
||||
microtask_context, callable, UndefinedConstant());
|
||||
GotoIfException(result, &if_exception, &var_exception);
|
||||
LeaveMicrotaskContext();
|
||||
SetCurrentContext(current_context);
|
||||
Goto(&loop_next);
|
||||
}
|
||||
|
||||
BIND(&is_callback);
|
||||
{
|
||||
Node* const microtask_callback =
|
||||
LoadObjectField(microtask, CallbackTask::kCallbackOffset);
|
||||
Node* const microtask_data =
|
||||
LoadObjectField(microtask, CallbackTask::kDataOffset);
|
||||
|
||||
// If this turns out to become a bottleneck because of the calls
|
||||
// to C++ via CEntry, we can choose to speed them up using a
|
||||
// similar mechanism that we use for the CallApiFunction stub,
|
||||
// except that calling the MicrotaskCallback is even easier, since
|
||||
// it doesn't accept any tagged parameters, doesn't return a value
|
||||
// and ignores exceptions.
|
||||
//
|
||||
// But from our current measurements it doesn't seem to be a
|
||||
// serious performance problem, even if the microtask is full
|
||||
// of CallHandlerTasks (which is not a realistic use case anyways).
|
||||
Node* const result =
|
||||
CallRuntime(Runtime::kRunMicrotaskCallback, current_context,
|
||||
microtask_callback, microtask_data);
|
||||
GotoIfException(result, &if_exception, &var_exception);
|
||||
Goto(&loop_next);
|
||||
}
|
||||
|
||||
BIND(&is_promise_resolve_thenable_job);
|
||||
{
|
||||
// Enter the context of the {microtask}.
|
||||
TNode<Context> microtask_context = LoadObjectField<Context>(
|
||||
microtask, PromiseResolveThenableJobTask::kContextOffset);
|
||||
TNode<Context> native_context = LoadNativeContext(microtask_context);
|
||||
CSA_ASSERT(this, IsNativeContext(native_context));
|
||||
EnterMicrotaskContext(native_context);
|
||||
SetCurrentContext(native_context);
|
||||
|
||||
Node* const promise_to_resolve = LoadObjectField(
|
||||
microtask, PromiseResolveThenableJobTask::kPromiseToResolveOffset);
|
||||
Node* const then = LoadObjectField(
|
||||
microtask, PromiseResolveThenableJobTask::kThenOffset);
|
||||
Node* const thenable = LoadObjectField(
|
||||
microtask, PromiseResolveThenableJobTask::kThenableOffset);
|
||||
|
||||
Node* const result =
|
||||
CallBuiltin(Builtins::kPromiseResolveThenableJob, native_context,
|
||||
promise_to_resolve, thenable, then);
|
||||
GotoIfException(result, &if_exception, &var_exception);
|
||||
LeaveMicrotaskContext();
|
||||
SetCurrentContext(current_context);
|
||||
Goto(&loop_next);
|
||||
}
|
||||
|
||||
BIND(&is_promise_fulfill_reaction_job);
|
||||
{
|
||||
// Enter the context of the {microtask}.
|
||||
TNode<Context> microtask_context = LoadObjectField<Context>(
|
||||
microtask, PromiseReactionJobTask::kContextOffset);
|
||||
TNode<Context> native_context = LoadNativeContext(microtask_context);
|
||||
CSA_ASSERT(this, IsNativeContext(native_context));
|
||||
EnterMicrotaskContext(native_context);
|
||||
SetCurrentContext(native_context);
|
||||
|
||||
Node* const argument =
|
||||
LoadObjectField(microtask, PromiseReactionJobTask::kArgumentOffset);
|
||||
Node* const handler =
|
||||
LoadObjectField(microtask, PromiseReactionJobTask::kHandlerOffset);
|
||||
Node* const promise_or_capability = LoadObjectField(
|
||||
microtask, PromiseReactionJobTask::kPromiseOrCapabilityOffset);
|
||||
|
||||
// Run the promise before/debug hook if enabled.
|
||||
RunPromiseHook(Runtime::kPromiseHookBefore, microtask_context,
|
||||
promise_or_capability);
|
||||
|
||||
Node* const result =
|
||||
CallBuiltin(Builtins::kPromiseFulfillReactionJob, microtask_context,
|
||||
argument, handler, promise_or_capability);
|
||||
GotoIfException(result, &if_exception, &var_exception);
|
||||
|
||||
// Run the promise after/debug hook if enabled.
|
||||
RunPromiseHook(Runtime::kPromiseHookAfter, microtask_context,
|
||||
promise_or_capability);
|
||||
|
||||
LeaveMicrotaskContext();
|
||||
SetCurrentContext(current_context);
|
||||
Goto(&loop_next);
|
||||
}
|
||||
|
||||
BIND(&is_promise_reject_reaction_job);
|
||||
{
|
||||
// Enter the context of the {microtask}.
|
||||
TNode<Context> microtask_context = LoadObjectField<Context>(
|
||||
microtask, PromiseReactionJobTask::kContextOffset);
|
||||
TNode<Context> native_context = LoadNativeContext(microtask_context);
|
||||
CSA_ASSERT(this, IsNativeContext(native_context));
|
||||
EnterMicrotaskContext(native_context);
|
||||
SetCurrentContext(native_context);
|
||||
|
||||
Node* const argument =
|
||||
LoadObjectField(microtask, PromiseReactionJobTask::kArgumentOffset);
|
||||
Node* const handler =
|
||||
LoadObjectField(microtask, PromiseReactionJobTask::kHandlerOffset);
|
||||
Node* const promise_or_capability = LoadObjectField(
|
||||
microtask, PromiseReactionJobTask::kPromiseOrCapabilityOffset);
|
||||
|
||||
// Run the promise before/debug hook if enabled.
|
||||
RunPromiseHook(Runtime::kPromiseHookBefore, microtask_context,
|
||||
promise_or_capability);
|
||||
|
||||
Node* const result =
|
||||
CallBuiltin(Builtins::kPromiseRejectReactionJob, microtask_context,
|
||||
argument, handler, promise_or_capability);
|
||||
GotoIfException(result, &if_exception, &var_exception);
|
||||
|
||||
// Run the promise after/debug hook if enabled.
|
||||
RunPromiseHook(Runtime::kPromiseHookAfter, microtask_context,
|
||||
promise_or_capability);
|
||||
|
||||
LeaveMicrotaskContext();
|
||||
SetCurrentContext(current_context);
|
||||
Goto(&loop_next);
|
||||
}
|
||||
|
||||
BIND(&is_weak_factory_cleanup_job);
|
||||
{
|
||||
// Enter the context of the {weak_factory}.
|
||||
TNode<JSWeakFactory> weak_factory = LoadObjectField<JSWeakFactory>(
|
||||
microtask, WeakFactoryCleanupJobTask::kFactoryOffset);
|
||||
TNode<Context> native_context = LoadObjectField<Context>(
|
||||
weak_factory, JSWeakFactory::kNativeContextOffset);
|
||||
CSA_ASSERT(this, IsNativeContext(native_context));
|
||||
EnterMicrotaskContext(native_context);
|
||||
SetCurrentContext(native_context);
|
||||
|
||||
Node* const result = CallRuntime(Runtime::kWeakFactoryCleanupJob,
|
||||
native_context, weak_factory);
|
||||
|
||||
GotoIfException(result, &if_exception, &var_exception);
|
||||
LeaveMicrotaskContext();
|
||||
SetCurrentContext(current_context);
|
||||
Goto(&loop_next);
|
||||
}
|
||||
|
||||
BIND(&is_unreachable);
|
||||
Unreachable();
|
||||
|
||||
BIND(&if_exception);
|
||||
{
|
||||
// Report unhandled exceptions from microtasks.
|
||||
CallRuntime(Runtime::kReportMessage, current_context,
|
||||
var_exception.value());
|
||||
LeaveMicrotaskContext();
|
||||
SetCurrentContext(current_context);
|
||||
Goto(&loop_next);
|
||||
}
|
||||
|
||||
BIND(&loop_next);
|
||||
Branch(IntPtrLessThan(index.value(), num_tasks), &loop, &init_queue_loop);
|
||||
}
|
||||
}
|
||||
|
||||
BIND(&done_init_queue_loop);
|
||||
{
|
||||
// Reset the "current microtask" on the isolate.
|
||||
StoreRoot(RootIndex::kCurrentMicrotask, UndefinedConstant());
|
||||
Return(UndefinedConstant());
|
||||
}
|
||||
}
|
||||
|
||||
TF_BUILTIN(AllocateInNewSpace, CodeStubAssembler) {
|
||||
TNode<IntPtrT> requested_size =
|
||||
UncheckedCast<IntPtrT>(Parameter(Descriptor::kRequestedSize));
|
||||
|
500
src/builtins/builtins-microtask-queue-gen.cc
Normal file
500
src/builtins/builtins-microtask-queue-gen.cc
Normal file
@ -0,0 +1,500 @@
|
||||
// Copyright 2018 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-utils-gen.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
#include "src/objects/microtask-inl.h"
|
||||
#include "src/objects/microtask-queue.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
template <typename T>
|
||||
using TNode = compiler::TNode<T>;
|
||||
|
||||
class MicrotaskQueueBuiltinsAssembler : public CodeStubAssembler {
|
||||
public:
|
||||
explicit MicrotaskQueueBuiltinsAssembler(compiler::CodeAssemblerState* state)
|
||||
: CodeStubAssembler(state) {}
|
||||
|
||||
TNode<MicrotaskQueue> GetDefaultMicrotaskQueue();
|
||||
TNode<IntPtrT> GetPendingMicrotaskCount(
|
||||
TNode<MicrotaskQueue> microtask_queue);
|
||||
void SetPendingMicrotaskCount(TNode<MicrotaskQueue> microtask_queue,
|
||||
TNode<IntPtrT> new_num_tasks);
|
||||
TNode<FixedArray> GetQueuedMicrotasks(TNode<MicrotaskQueue> microtask_queue);
|
||||
void SetQueuedMicrotasks(TNode<MicrotaskQueue> microtask_queue,
|
||||
TNode<FixedArray> new_queue);
|
||||
|
||||
TNode<Context> GetCurrentContext();
|
||||
void SetCurrentContext(TNode<Context> context);
|
||||
|
||||
void EnterMicrotaskContext(TNode<Context> context);
|
||||
void LeaveMicrotaskContext();
|
||||
|
||||
void RunPromiseHook(Runtime::FunctionId id, TNode<Context> context,
|
||||
SloppyTNode<HeapObject> promise_or_capability);
|
||||
|
||||
TNode<Object> GetPendingException() {
|
||||
auto ref = ExternalReference::Create(kPendingExceptionAddress, isolate());
|
||||
return TNode<Object>::UncheckedCast(
|
||||
Load(MachineType::AnyTagged(), ExternalConstant(ref)));
|
||||
}
|
||||
void ClearPendingException() {
|
||||
auto ref = ExternalReference::Create(kPendingExceptionAddress, isolate());
|
||||
StoreNoWriteBarrier(MachineRepresentation::kTagged, ExternalConstant(ref),
|
||||
TheHoleConstant());
|
||||
}
|
||||
|
||||
TNode<Object> GetScheduledException() {
|
||||
auto ref = ExternalReference::scheduled_exception_address(isolate());
|
||||
return TNode<Object>::UncheckedCast(
|
||||
Load(MachineType::AnyTagged(), ExternalConstant(ref)));
|
||||
}
|
||||
void ClearScheduledException() {
|
||||
auto ref = ExternalReference::scheduled_exception_address(isolate());
|
||||
StoreNoWriteBarrier(MachineRepresentation::kTagged, ExternalConstant(ref),
|
||||
TheHoleConstant());
|
||||
}
|
||||
};
|
||||
|
||||
TNode<MicrotaskQueue>
|
||||
MicrotaskQueueBuiltinsAssembler::GetDefaultMicrotaskQueue() {
|
||||
return TNode<MicrotaskQueue>::UncheckedCast(
|
||||
LoadRoot(RootIndex::kDefaultMicrotaskQueue));
|
||||
}
|
||||
|
||||
TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::GetPendingMicrotaskCount(
|
||||
TNode<MicrotaskQueue> microtask_queue) {
|
||||
TNode<IntPtrT> result = LoadAndUntagObjectField(
|
||||
microtask_queue, MicrotaskQueue::kPendingMicrotaskCountOffset);
|
||||
return result;
|
||||
}
|
||||
|
||||
void MicrotaskQueueBuiltinsAssembler::SetPendingMicrotaskCount(
|
||||
TNode<MicrotaskQueue> microtask_queue, TNode<IntPtrT> new_num_tasks) {
|
||||
StoreObjectField(microtask_queue,
|
||||
MicrotaskQueue::kPendingMicrotaskCountOffset,
|
||||
SmiFromIntPtr(new_num_tasks));
|
||||
}
|
||||
|
||||
TNode<FixedArray> MicrotaskQueueBuiltinsAssembler::GetQueuedMicrotasks(
|
||||
TNode<MicrotaskQueue> microtask_queue) {
|
||||
return LoadObjectField<FixedArray>(microtask_queue,
|
||||
MicrotaskQueue::kQueueOffset);
|
||||
}
|
||||
|
||||
void MicrotaskQueueBuiltinsAssembler::SetQueuedMicrotasks(
|
||||
TNode<MicrotaskQueue> microtask_queue, TNode<FixedArray> new_queue) {
|
||||
StoreObjectField(microtask_queue, MicrotaskQueue::kQueueOffset, new_queue);
|
||||
}
|
||||
|
||||
TNode<Context> MicrotaskQueueBuiltinsAssembler::GetCurrentContext() {
|
||||
auto ref = ExternalReference::Create(kContextAddress, isolate());
|
||||
return TNode<Context>::UncheckedCast(
|
||||
Load(MachineType::AnyTagged(), ExternalConstant(ref)));
|
||||
}
|
||||
|
||||
void MicrotaskQueueBuiltinsAssembler::SetCurrentContext(
|
||||
TNode<Context> context) {
|
||||
auto ref = ExternalReference::Create(kContextAddress, isolate());
|
||||
StoreNoWriteBarrier(MachineRepresentation::kTagged, ExternalConstant(ref),
|
||||
context);
|
||||
}
|
||||
|
||||
void MicrotaskQueueBuiltinsAssembler::EnterMicrotaskContext(
|
||||
TNode<Context> native_context) {
|
||||
CSA_ASSERT(this, IsNativeContext(native_context));
|
||||
|
||||
auto ref = ExternalReference::handle_scope_implementer_address(isolate());
|
||||
Node* const hsi = Load(MachineType::Pointer(), ExternalConstant(ref));
|
||||
StoreNoWriteBarrier(
|
||||
MachineType::PointerRepresentation(), hsi,
|
||||
IntPtrConstant(HandleScopeImplementerOffsets::kMicrotaskContext),
|
||||
BitcastTaggedToWord(native_context));
|
||||
|
||||
// Load mirrored std::vector length from
|
||||
// HandleScopeImplementer::entered_contexts_count_
|
||||
auto type = kSizetSize == 8 ? MachineType::Uint64() : MachineType::Uint32();
|
||||
Node* entered_contexts_length = Load(
|
||||
type, hsi,
|
||||
IntPtrConstant(HandleScopeImplementerOffsets::kEnteredContextsCount));
|
||||
|
||||
auto rep = kSizetSize == 8 ? MachineRepresentation::kWord64
|
||||
: MachineRepresentation::kWord32;
|
||||
|
||||
StoreNoWriteBarrier(
|
||||
rep, hsi,
|
||||
IntPtrConstant(
|
||||
HandleScopeImplementerOffsets::kEnteredContextCountDuringMicrotasks),
|
||||
entered_contexts_length);
|
||||
}
|
||||
|
||||
void MicrotaskQueueBuiltinsAssembler::LeaveMicrotaskContext() {
|
||||
auto ref = ExternalReference::handle_scope_implementer_address(isolate());
|
||||
|
||||
Node* const hsi = Load(MachineType::Pointer(), ExternalConstant(ref));
|
||||
StoreNoWriteBarrier(
|
||||
MachineType::PointerRepresentation(), hsi,
|
||||
IntPtrConstant(HandleScopeImplementerOffsets::kMicrotaskContext),
|
||||
IntPtrConstant(0));
|
||||
if (kSizetSize == 4) {
|
||||
StoreNoWriteBarrier(
|
||||
MachineRepresentation::kWord32, hsi,
|
||||
IntPtrConstant(HandleScopeImplementerOffsets::
|
||||
kEnteredContextCountDuringMicrotasks),
|
||||
Int32Constant(0));
|
||||
} else {
|
||||
StoreNoWriteBarrier(
|
||||
MachineRepresentation::kWord64, hsi,
|
||||
IntPtrConstant(HandleScopeImplementerOffsets::
|
||||
kEnteredContextCountDuringMicrotasks),
|
||||
Int64Constant(0));
|
||||
}
|
||||
}
|
||||
|
||||
void MicrotaskQueueBuiltinsAssembler::RunPromiseHook(
|
||||
Runtime::FunctionId id, TNode<Context> context,
|
||||
SloppyTNode<HeapObject> promise_or_capability) {
|
||||
Label hook(this, Label::kDeferred), done_hook(this);
|
||||
Branch(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(), &hook,
|
||||
&done_hook);
|
||||
BIND(&hook);
|
||||
{
|
||||
// Get to the underlying JSPromise instance.
|
||||
TNode<HeapObject> promise = Select<HeapObject>(
|
||||
IsPromiseCapability(promise_or_capability),
|
||||
[=] {
|
||||
return CAST(LoadObjectField(promise_or_capability,
|
||||
PromiseCapability::kPromiseOffset));
|
||||
},
|
||||
|
||||
[=] { return promise_or_capability; });
|
||||
GotoIf(IsUndefined(promise), &done_hook);
|
||||
CallRuntime(id, context, promise);
|
||||
Goto(&done_hook);
|
||||
}
|
||||
BIND(&done_hook);
|
||||
}
|
||||
|
||||
TF_BUILTIN(EnqueueMicrotask, MicrotaskQueueBuiltinsAssembler) {
|
||||
Node* microtask = Parameter(Descriptor::kMicrotask);
|
||||
|
||||
TNode<MicrotaskQueue> microtask_queue = GetDefaultMicrotaskQueue();
|
||||
TNode<IntPtrT> num_tasks = GetPendingMicrotaskCount(microtask_queue);
|
||||
TNode<IntPtrT> new_num_tasks = IntPtrAdd(num_tasks, IntPtrConstant(1));
|
||||
TNode<FixedArray> queue = GetQueuedMicrotasks(microtask_queue);
|
||||
TNode<IntPtrT> queue_length = LoadAndUntagFixedArrayBaseLength(queue);
|
||||
|
||||
Label if_append(this), if_grow(this), done(this);
|
||||
Branch(WordEqual(num_tasks, queue_length), &if_grow, &if_append);
|
||||
|
||||
BIND(&if_grow);
|
||||
{
|
||||
// Determine the new queue length and check if we need to allocate
|
||||
// in large object space (instead of just going to new space, where
|
||||
// we also know that we don't need any write barriers for setting
|
||||
// up the new queue object).
|
||||
Label if_newspace(this), if_lospace(this, Label::kDeferred);
|
||||
TNode<IntPtrT> new_queue_length =
|
||||
IntPtrMax(IntPtrConstant(8), IntPtrAdd(num_tasks, num_tasks));
|
||||
Branch(IntPtrLessThanOrEqual(new_queue_length,
|
||||
IntPtrConstant(FixedArray::kMaxRegularLength)),
|
||||
&if_newspace, &if_lospace);
|
||||
|
||||
BIND(&if_newspace);
|
||||
{
|
||||
// This is the likely case where the new queue fits into new space,
|
||||
// and thus we don't need any write barriers for initializing it.
|
||||
TNode<FixedArray> new_queue =
|
||||
CAST(AllocateFixedArray(PACKED_ELEMENTS, new_queue_length));
|
||||
CopyFixedArrayElements(PACKED_ELEMENTS, queue, new_queue, num_tasks,
|
||||
SKIP_WRITE_BARRIER);
|
||||
StoreFixedArrayElement(new_queue, num_tasks, microtask,
|
||||
SKIP_WRITE_BARRIER);
|
||||
FillFixedArrayWithValue(PACKED_ELEMENTS, new_queue, new_num_tasks,
|
||||
new_queue_length, RootIndex::kUndefinedValue);
|
||||
SetQueuedMicrotasks(microtask_queue, new_queue);
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&if_lospace);
|
||||
{
|
||||
// The fallback case where the new queue ends up in large object space.
|
||||
TNode<FixedArray> new_queue = CAST(AllocateFixedArray(
|
||||
PACKED_ELEMENTS, new_queue_length, INTPTR_PARAMETERS,
|
||||
AllocationFlag::kAllowLargeObjectAllocation));
|
||||
CopyFixedArrayElements(PACKED_ELEMENTS, queue, new_queue, num_tasks);
|
||||
StoreFixedArrayElement(new_queue, num_tasks, microtask);
|
||||
FillFixedArrayWithValue(PACKED_ELEMENTS, new_queue, new_num_tasks,
|
||||
new_queue_length, RootIndex::kUndefinedValue);
|
||||
SetQueuedMicrotasks(microtask_queue, new_queue);
|
||||
Goto(&done);
|
||||
}
|
||||
}
|
||||
|
||||
BIND(&if_append);
|
||||
{
|
||||
StoreFixedArrayElement(queue, num_tasks, microtask);
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&done);
|
||||
SetPendingMicrotaskCount(microtask_queue, new_num_tasks);
|
||||
Return(UndefinedConstant());
|
||||
}
|
||||
|
||||
TF_BUILTIN(RunMicrotasks, MicrotaskQueueBuiltinsAssembler) {
|
||||
// Load the current context from the isolate.
|
||||
TNode<Context> current_context = GetCurrentContext();
|
||||
TNode<MicrotaskQueue> microtask_queue = GetDefaultMicrotaskQueue();
|
||||
|
||||
Label init_queue_loop(this), done_init_queue_loop(this);
|
||||
Goto(&init_queue_loop);
|
||||
BIND(&init_queue_loop);
|
||||
{
|
||||
TVARIABLE(IntPtrT, index, IntPtrConstant(0));
|
||||
Label loop(this, &index), loop_next(this);
|
||||
|
||||
TNode<IntPtrT> num_tasks = GetPendingMicrotaskCount(microtask_queue);
|
||||
GotoIf(IntPtrEqual(num_tasks, IntPtrConstant(0)), &done_init_queue_loop);
|
||||
|
||||
TNode<FixedArray> queue = GetQueuedMicrotasks(microtask_queue);
|
||||
|
||||
CSA_ASSERT(this, IntPtrGreaterThanOrEqual(
|
||||
LoadAndUntagFixedArrayBaseLength(queue), num_tasks));
|
||||
CSA_ASSERT(this, IntPtrGreaterThan(num_tasks, IntPtrConstant(0)));
|
||||
|
||||
SetQueuedMicrotasks(microtask_queue, EmptyFixedArrayConstant());
|
||||
SetPendingMicrotaskCount(microtask_queue, IntPtrConstant(0));
|
||||
|
||||
Goto(&loop);
|
||||
BIND(&loop);
|
||||
{
|
||||
TNode<HeapObject> microtask =
|
||||
CAST(LoadFixedArrayElement(queue, index.value()));
|
||||
index = IntPtrAdd(index.value(), IntPtrConstant(1));
|
||||
|
||||
CSA_ASSERT(this, TaggedIsNotSmi(microtask));
|
||||
|
||||
StoreRoot(RootIndex::kCurrentMicrotask, microtask);
|
||||
TNode<Map> microtask_map = LoadMap(microtask);
|
||||
TNode<Int32T> microtask_type = LoadMapInstanceType(microtask_map);
|
||||
|
||||
VARIABLE(var_exception, MachineRepresentation::kTagged,
|
||||
TheHoleConstant());
|
||||
Label if_exception(this, Label::kDeferred);
|
||||
Label is_callable(this), is_callback(this),
|
||||
is_promise_fulfill_reaction_job(this),
|
||||
is_promise_reject_reaction_job(this),
|
||||
is_promise_resolve_thenable_job(this),
|
||||
is_weak_factory_cleanup_job(this),
|
||||
is_unreachable(this, Label::kDeferred);
|
||||
|
||||
int32_t case_values[] = {CALLABLE_TASK_TYPE,
|
||||
CALLBACK_TASK_TYPE,
|
||||
PROMISE_FULFILL_REACTION_JOB_TASK_TYPE,
|
||||
PROMISE_REJECT_REACTION_JOB_TASK_TYPE,
|
||||
PROMISE_RESOLVE_THENABLE_JOB_TASK_TYPE,
|
||||
WEAK_FACTORY_CLEANUP_JOB_TASK_TYPE};
|
||||
Label* case_labels[] = {&is_callable,
|
||||
&is_callback,
|
||||
&is_promise_fulfill_reaction_job,
|
||||
&is_promise_reject_reaction_job,
|
||||
&is_promise_resolve_thenable_job,
|
||||
&is_weak_factory_cleanup_job};
|
||||
static_assert(arraysize(case_values) == arraysize(case_labels), "");
|
||||
Switch(microtask_type, &is_unreachable, case_values, case_labels,
|
||||
arraysize(case_labels));
|
||||
|
||||
BIND(&is_callable);
|
||||
{
|
||||
// Enter the context of the {microtask}.
|
||||
TNode<Context> microtask_context =
|
||||
LoadObjectField<Context>(microtask, CallableTask::kContextOffset);
|
||||
TNode<Context> native_context = LoadNativeContext(microtask_context);
|
||||
|
||||
CSA_ASSERT(this, IsNativeContext(native_context));
|
||||
EnterMicrotaskContext(native_context);
|
||||
SetCurrentContext(native_context);
|
||||
|
||||
TNode<JSReceiver> callable = LoadObjectField<JSReceiver>(
|
||||
microtask, CallableTask::kCallableOffset);
|
||||
Node* const result = CallJS(
|
||||
CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
|
||||
microtask_context, callable, UndefinedConstant());
|
||||
GotoIfException(result, &if_exception, &var_exception);
|
||||
LeaveMicrotaskContext();
|
||||
SetCurrentContext(current_context);
|
||||
Goto(&loop_next);
|
||||
}
|
||||
|
||||
BIND(&is_callback);
|
||||
{
|
||||
Node* const microtask_callback =
|
||||
LoadObjectField(microtask, CallbackTask::kCallbackOffset);
|
||||
Node* const microtask_data =
|
||||
LoadObjectField(microtask, CallbackTask::kDataOffset);
|
||||
|
||||
// If this turns out to become a bottleneck because of the calls
|
||||
// to C++ via CEntry, we can choose to speed them up using a
|
||||
// similar mechanism that we use for the CallApiFunction stub,
|
||||
// except that calling the MicrotaskCallback is even easier, since
|
||||
// it doesn't accept any tagged parameters, doesn't return a value
|
||||
// and ignores exceptions.
|
||||
//
|
||||
// But from our current measurements it doesn't seem to be a
|
||||
// serious performance problem, even if the microtask is full
|
||||
// of CallHandlerTasks (which is not a realistic use case anyways).
|
||||
Node* const result =
|
||||
CallRuntime(Runtime::kRunMicrotaskCallback, current_context,
|
||||
microtask_callback, microtask_data);
|
||||
GotoIfException(result, &if_exception, &var_exception);
|
||||
Goto(&loop_next);
|
||||
}
|
||||
|
||||
BIND(&is_promise_resolve_thenable_job);
|
||||
{
|
||||
// Enter the context of the {microtask}.
|
||||
TNode<Context> microtask_context = LoadObjectField<Context>(
|
||||
microtask, PromiseResolveThenableJobTask::kContextOffset);
|
||||
TNode<Context> native_context = LoadNativeContext(microtask_context);
|
||||
CSA_ASSERT(this, IsNativeContext(native_context));
|
||||
EnterMicrotaskContext(native_context);
|
||||
SetCurrentContext(native_context);
|
||||
|
||||
Node* const promise_to_resolve = LoadObjectField(
|
||||
microtask, PromiseResolveThenableJobTask::kPromiseToResolveOffset);
|
||||
Node* const then = LoadObjectField(
|
||||
microtask, PromiseResolveThenableJobTask::kThenOffset);
|
||||
Node* const thenable = LoadObjectField(
|
||||
microtask, PromiseResolveThenableJobTask::kThenableOffset);
|
||||
|
||||
Node* const result =
|
||||
CallBuiltin(Builtins::kPromiseResolveThenableJob, native_context,
|
||||
promise_to_resolve, thenable, then);
|
||||
GotoIfException(result, &if_exception, &var_exception);
|
||||
LeaveMicrotaskContext();
|
||||
SetCurrentContext(current_context);
|
||||
Goto(&loop_next);
|
||||
}
|
||||
|
||||
BIND(&is_promise_fulfill_reaction_job);
|
||||
{
|
||||
// Enter the context of the {microtask}.
|
||||
TNode<Context> microtask_context = LoadObjectField<Context>(
|
||||
microtask, PromiseReactionJobTask::kContextOffset);
|
||||
TNode<Context> native_context = LoadNativeContext(microtask_context);
|
||||
CSA_ASSERT(this, IsNativeContext(native_context));
|
||||
EnterMicrotaskContext(native_context);
|
||||
SetCurrentContext(native_context);
|
||||
|
||||
Node* const argument =
|
||||
LoadObjectField(microtask, PromiseReactionJobTask::kArgumentOffset);
|
||||
Node* const handler =
|
||||
LoadObjectField(microtask, PromiseReactionJobTask::kHandlerOffset);
|
||||
Node* const promise_or_capability = LoadObjectField(
|
||||
microtask, PromiseReactionJobTask::kPromiseOrCapabilityOffset);
|
||||
|
||||
// Run the promise before/debug hook if enabled.
|
||||
RunPromiseHook(Runtime::kPromiseHookBefore, microtask_context,
|
||||
promise_or_capability);
|
||||
|
||||
Node* const result =
|
||||
CallBuiltin(Builtins::kPromiseFulfillReactionJob, microtask_context,
|
||||
argument, handler, promise_or_capability);
|
||||
GotoIfException(result, &if_exception, &var_exception);
|
||||
|
||||
// Run the promise after/debug hook if enabled.
|
||||
RunPromiseHook(Runtime::kPromiseHookAfter, microtask_context,
|
||||
promise_or_capability);
|
||||
|
||||
LeaveMicrotaskContext();
|
||||
SetCurrentContext(current_context);
|
||||
Goto(&loop_next);
|
||||
}
|
||||
|
||||
BIND(&is_promise_reject_reaction_job);
|
||||
{
|
||||
// Enter the context of the {microtask}.
|
||||
TNode<Context> microtask_context = LoadObjectField<Context>(
|
||||
microtask, PromiseReactionJobTask::kContextOffset);
|
||||
TNode<Context> native_context = LoadNativeContext(microtask_context);
|
||||
CSA_ASSERT(this, IsNativeContext(native_context));
|
||||
EnterMicrotaskContext(native_context);
|
||||
SetCurrentContext(native_context);
|
||||
|
||||
Node* const argument =
|
||||
LoadObjectField(microtask, PromiseReactionJobTask::kArgumentOffset);
|
||||
Node* const handler =
|
||||
LoadObjectField(microtask, PromiseReactionJobTask::kHandlerOffset);
|
||||
Node* const promise_or_capability = LoadObjectField(
|
||||
microtask, PromiseReactionJobTask::kPromiseOrCapabilityOffset);
|
||||
|
||||
// Run the promise before/debug hook if enabled.
|
||||
RunPromiseHook(Runtime::kPromiseHookBefore, microtask_context,
|
||||
promise_or_capability);
|
||||
|
||||
Node* const result =
|
||||
CallBuiltin(Builtins::kPromiseRejectReactionJob, microtask_context,
|
||||
argument, handler, promise_or_capability);
|
||||
GotoIfException(result, &if_exception, &var_exception);
|
||||
|
||||
// Run the promise after/debug hook if enabled.
|
||||
RunPromiseHook(Runtime::kPromiseHookAfter, microtask_context,
|
||||
promise_or_capability);
|
||||
|
||||
LeaveMicrotaskContext();
|
||||
SetCurrentContext(current_context);
|
||||
Goto(&loop_next);
|
||||
}
|
||||
|
||||
BIND(&is_weak_factory_cleanup_job);
|
||||
{
|
||||
// Enter the context of the {weak_factory}.
|
||||
TNode<JSWeakFactory> weak_factory = LoadObjectField<JSWeakFactory>(
|
||||
microtask, WeakFactoryCleanupJobTask::kFactoryOffset);
|
||||
TNode<Context> native_context = LoadObjectField<Context>(
|
||||
weak_factory, JSWeakFactory::kNativeContextOffset);
|
||||
CSA_ASSERT(this, IsNativeContext(native_context));
|
||||
EnterMicrotaskContext(native_context);
|
||||
SetCurrentContext(native_context);
|
||||
|
||||
Node* const result = CallRuntime(Runtime::kWeakFactoryCleanupJob,
|
||||
native_context, weak_factory);
|
||||
|
||||
GotoIfException(result, &if_exception, &var_exception);
|
||||
LeaveMicrotaskContext();
|
||||
SetCurrentContext(current_context);
|
||||
Goto(&loop_next);
|
||||
}
|
||||
|
||||
BIND(&is_unreachable);
|
||||
Unreachable();
|
||||
|
||||
BIND(&if_exception);
|
||||
{
|
||||
// Report unhandled exceptions from microtasks.
|
||||
CallRuntime(Runtime::kReportMessage, current_context,
|
||||
var_exception.value());
|
||||
LeaveMicrotaskContext();
|
||||
SetCurrentContext(current_context);
|
||||
Goto(&loop_next);
|
||||
}
|
||||
|
||||
BIND(&loop_next);
|
||||
Branch(IntPtrLessThan(index.value(), num_tasks), &loop, &init_queue_loop);
|
||||
}
|
||||
}
|
||||
|
||||
BIND(&done_init_queue_loop);
|
||||
{
|
||||
// Reset the "current microtask" on the isolate.
|
||||
StoreRoot(RootIndex::kCurrentMicrotask, UndefinedConstant());
|
||||
Return(UndefinedConstant());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
Loading…
Reference in New Issue
Block a user