1// Copyright 2018 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "src/api/api.h" 6#include "src/builtins/builtins-utils-gen.h" 7#include "src/codegen/code-stub-assembler.h" 8#include "src/execution/microtask-queue.h" 9#include "src/objects/js-weak-refs.h" 10#include "src/objects/microtask-inl.h" 11#include "src/objects/promise.h" 12#include "src/objects/smi-inl.h" 13 14namespace v8 { 15namespace internal { 16 17using compiler::ScopedExceptionHandler; 18 19class MicrotaskQueueBuiltinsAssembler : public CodeStubAssembler { 20 public: 21 explicit MicrotaskQueueBuiltinsAssembler(compiler::CodeAssemblerState* state) 22 : CodeStubAssembler(state) {} 23 24 TNode<RawPtrT> GetMicrotaskQueue(TNode<Context> context); 25 TNode<RawPtrT> GetMicrotaskRingBuffer(TNode<RawPtrT> microtask_queue); 26 TNode<IntPtrT> GetMicrotaskQueueCapacity(TNode<RawPtrT> microtask_queue); 27 TNode<IntPtrT> GetMicrotaskQueueSize(TNode<RawPtrT> microtask_queue); 28 void SetMicrotaskQueueSize(TNode<RawPtrT> microtask_queue, 29 TNode<IntPtrT> new_size); 30 TNode<IntPtrT> GetMicrotaskQueueStart(TNode<RawPtrT> microtask_queue); 31 void SetMicrotaskQueueStart(TNode<RawPtrT> microtask_queue, 32 TNode<IntPtrT> new_start); 33 TNode<IntPtrT> CalculateRingBufferOffset(TNode<IntPtrT> capacity, 34 TNode<IntPtrT> start, 35 TNode<IntPtrT> index); 36 37 void PrepareForContext(TNode<Context> microtask_context, Label* bailout); 38 void RunSingleMicrotask(TNode<Context> current_context, 39 TNode<Microtask> microtask); 40 void IncrementFinishedMicrotaskCount(TNode<RawPtrT> microtask_queue); 41 42 TNode<Context> GetCurrentContext(); 43 void SetCurrentContext(TNode<Context> context); 44 45 TNode<IntPtrT> GetEnteredContextCount(); 46 void EnterMicrotaskContext(TNode<Context> native_context); 47 void RewindEnteredContext(TNode<IntPtrT> saved_entered_context_count); 48 49 void RunAllPromiseHooks(PromiseHookType type, TNode<Context> context, 50 TNode<HeapObject> promise_or_capability); 51 void RunPromiseHook(Runtime::FunctionId id, TNode<Context> context, 52 TNode<HeapObject> promise_or_capability, 53 TNode<Uint32T> promiseHookFlags); 54}; 55 56TNode<RawPtrT> MicrotaskQueueBuiltinsAssembler::GetMicrotaskQueue( 57 TNode<Context> native_context) { 58 CSA_DCHECK(this, IsNativeContext(native_context)); 59 return LoadExternalPointerFromObject(native_context, 60 NativeContext::kMicrotaskQueueOffset, 61 kNativeContextMicrotaskQueueTag); 62} 63 64TNode<RawPtrT> MicrotaskQueueBuiltinsAssembler::GetMicrotaskRingBuffer( 65 TNode<RawPtrT> microtask_queue) { 66 return Load<RawPtrT>(microtask_queue, 67 IntPtrConstant(MicrotaskQueue::kRingBufferOffset)); 68} 69 70TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::GetMicrotaskQueueCapacity( 71 TNode<RawPtrT> microtask_queue) { 72 return Load<IntPtrT>(microtask_queue, 73 IntPtrConstant(MicrotaskQueue::kCapacityOffset)); 74} 75 76TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::GetMicrotaskQueueSize( 77 TNode<RawPtrT> microtask_queue) { 78 return Load<IntPtrT>(microtask_queue, 79 IntPtrConstant(MicrotaskQueue::kSizeOffset)); 80} 81 82void MicrotaskQueueBuiltinsAssembler::SetMicrotaskQueueSize( 83 TNode<RawPtrT> microtask_queue, TNode<IntPtrT> new_size) { 84 StoreNoWriteBarrier(MachineType::PointerRepresentation(), microtask_queue, 85 IntPtrConstant(MicrotaskQueue::kSizeOffset), new_size); 86} 87 88TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::GetMicrotaskQueueStart( 89 TNode<RawPtrT> microtask_queue) { 90 return Load<IntPtrT>(microtask_queue, 91 IntPtrConstant(MicrotaskQueue::kStartOffset)); 92} 93 94void MicrotaskQueueBuiltinsAssembler::SetMicrotaskQueueStart( 95 TNode<RawPtrT> microtask_queue, TNode<IntPtrT> new_start) { 96 StoreNoWriteBarrier(MachineType::PointerRepresentation(), microtask_queue, 97 IntPtrConstant(MicrotaskQueue::kStartOffset), new_start); 98} 99 100TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::CalculateRingBufferOffset( 101 TNode<IntPtrT> capacity, TNode<IntPtrT> start, TNode<IntPtrT> index) { 102 return TimesSystemPointerSize( 103 WordAnd(IntPtrAdd(start, index), IntPtrSub(capacity, IntPtrConstant(1)))); 104} 105 106void MicrotaskQueueBuiltinsAssembler::PrepareForContext( 107 TNode<Context> native_context, Label* bailout) { 108 CSA_DCHECK(this, IsNativeContext(native_context)); 109 110 // Skip the microtask execution if the associated context is shutdown. 111 GotoIf(WordEqual(GetMicrotaskQueue(native_context), IntPtrConstant(0)), 112 bailout); 113 114 EnterMicrotaskContext(native_context); 115 SetCurrentContext(native_context); 116} 117 118void MicrotaskQueueBuiltinsAssembler::RunSingleMicrotask( 119 TNode<Context> current_context, TNode<Microtask> microtask) { 120 CSA_DCHECK(this, TaggedIsNotSmi(microtask)); 121 122 StoreRoot(RootIndex::kCurrentMicrotask, microtask); 123 TNode<IntPtrT> saved_entered_context_count = GetEnteredContextCount(); 124 TNode<Map> microtask_map = LoadMap(microtask); 125 TNode<Uint16T> microtask_type = LoadMapInstanceType(microtask_map); 126 127 TVARIABLE(Object, var_exception); 128 Label if_exception(this, Label::kDeferred); 129 Label is_callable(this), is_callback(this), 130 is_promise_fulfill_reaction_job(this), 131 is_promise_reject_reaction_job(this), 132 is_promise_resolve_thenable_job(this), 133 is_unreachable(this, Label::kDeferred), done(this); 134 135 int32_t case_values[] = {CALLABLE_TASK_TYPE, CALLBACK_TASK_TYPE, 136 PROMISE_FULFILL_REACTION_JOB_TASK_TYPE, 137 PROMISE_REJECT_REACTION_JOB_TASK_TYPE, 138 PROMISE_RESOLVE_THENABLE_JOB_TASK_TYPE}; 139 Label* case_labels[] = { 140 &is_callable, &is_callback, &is_promise_fulfill_reaction_job, 141 &is_promise_reject_reaction_job, &is_promise_resolve_thenable_job}; 142 static_assert(arraysize(case_values) == arraysize(case_labels), ""); 143 Switch(microtask_type, &is_unreachable, case_values, case_labels, 144 arraysize(case_labels)); 145 146 BIND(&is_callable); 147 { 148 // Enter the context of the {microtask}. 149 TNode<Context> microtask_context = 150 LoadObjectField<Context>(microtask, CallableTask::kContextOffset); 151 TNode<NativeContext> native_context = LoadNativeContext(microtask_context); 152 PrepareForContext(native_context, &done); 153 154 TNode<JSReceiver> callable = 155 LoadObjectField<JSReceiver>(microtask, CallableTask::kCallableOffset); 156 { 157 ScopedExceptionHandler handler(this, &if_exception, &var_exception); 158 Call(microtask_context, callable, UndefinedConstant()); 159 } 160 RewindEnteredContext(saved_entered_context_count); 161 SetCurrentContext(current_context); 162 Goto(&done); 163 } 164 165 BIND(&is_callback); 166 { 167 const TNode<Object> microtask_callback = 168 LoadObjectField(microtask, CallbackTask::kCallbackOffset); 169 const TNode<Object> microtask_data = 170 LoadObjectField(microtask, CallbackTask::kDataOffset); 171 172 // If this turns out to become a bottleneck because of the calls 173 // to C++ via CEntry, we can choose to speed them up using a 174 // similar mechanism that we use for the CallApiFunction stub, 175 // except that calling the MicrotaskCallback is even easier, since 176 // it doesn't accept any tagged parameters, doesn't return a value 177 // and ignores exceptions. 178 // 179 // But from our current measurements it doesn't seem to be a 180 // serious performance problem, even if the microtask is full 181 // of CallHandlerTasks (which is not a realistic use case anyways). 182 { 183 ScopedExceptionHandler handler(this, &if_exception, &var_exception); 184 CallRuntime(Runtime::kRunMicrotaskCallback, current_context, 185 microtask_callback, microtask_data); 186 } 187 Goto(&done); 188 } 189 190 BIND(&is_promise_resolve_thenable_job); 191 { 192 // Enter the context of the {microtask}. 193 TNode<Context> microtask_context = LoadObjectField<Context>( 194 microtask, PromiseResolveThenableJobTask::kContextOffset); 195 TNode<NativeContext> native_context = LoadNativeContext(microtask_context); 196 PrepareForContext(native_context, &done); 197 198 const TNode<Object> promise_to_resolve = LoadObjectField( 199 microtask, PromiseResolveThenableJobTask::kPromiseToResolveOffset); 200 const TNode<Object> then = 201 LoadObjectField(microtask, PromiseResolveThenableJobTask::kThenOffset); 202 const TNode<Object> thenable = LoadObjectField( 203 microtask, PromiseResolveThenableJobTask::kThenableOffset); 204 205 RunAllPromiseHooks(PromiseHookType::kBefore, microtask_context, 206 CAST(promise_to_resolve)); 207 208 { 209 ScopedExceptionHandler handler(this, &if_exception, &var_exception); 210 CallBuiltin(Builtin::kPromiseResolveThenableJob, native_context, 211 promise_to_resolve, thenable, then); 212 } 213 214 RunAllPromiseHooks(PromiseHookType::kAfter, microtask_context, 215 CAST(promise_to_resolve)); 216 217 RewindEnteredContext(saved_entered_context_count); 218 SetCurrentContext(current_context); 219 Goto(&done); 220 } 221 222 BIND(&is_promise_fulfill_reaction_job); 223 { 224 // Enter the context of the {microtask}. 225 TNode<Context> microtask_context = LoadObjectField<Context>( 226 microtask, PromiseReactionJobTask::kContextOffset); 227 TNode<NativeContext> native_context = LoadNativeContext(microtask_context); 228 PrepareForContext(native_context, &done); 229 230 const TNode<Object> argument = 231 LoadObjectField(microtask, PromiseReactionJobTask::kArgumentOffset); 232 const TNode<Object> job_handler = 233 LoadObjectField(microtask, PromiseReactionJobTask::kHandlerOffset); 234 const TNode<HeapObject> promise_or_capability = CAST(LoadObjectField( 235 microtask, PromiseReactionJobTask::kPromiseOrCapabilityOffset)); 236 237 TNode<Object> preserved_embedder_data = LoadObjectField( 238 microtask, 239 PromiseReactionJobTask::kContinuationPreservedEmbedderDataOffset); 240 Label preserved_data_done(this); 241 GotoIf(IsUndefined(preserved_embedder_data), &preserved_data_done); 242 StoreContextElement(native_context, 243 Context::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX, 244 preserved_embedder_data); 245 Goto(&preserved_data_done); 246 BIND(&preserved_data_done); 247 248 // Run the promise before/debug hook if enabled. 249 RunAllPromiseHooks(PromiseHookType::kBefore, microtask_context, 250 promise_or_capability); 251 252 { 253 ScopedExceptionHandler handler(this, &if_exception, &var_exception); 254 CallBuiltin(Builtin::kPromiseFulfillReactionJob, microtask_context, 255 argument, job_handler, promise_or_capability); 256 } 257 258 // Run the promise after/debug hook if enabled. 259 RunAllPromiseHooks(PromiseHookType::kAfter, microtask_context, 260 promise_or_capability); 261 262 Label preserved_data_reset_done(this); 263 GotoIf(IsUndefined(preserved_embedder_data), &preserved_data_reset_done); 264 StoreContextElement(native_context, 265 Context::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX, 266 UndefinedConstant()); 267 Goto(&preserved_data_reset_done); 268 BIND(&preserved_data_reset_done); 269 270 RewindEnteredContext(saved_entered_context_count); 271 SetCurrentContext(current_context); 272 Goto(&done); 273 } 274 275 BIND(&is_promise_reject_reaction_job); 276 { 277 // Enter the context of the {microtask}. 278 TNode<Context> microtask_context = LoadObjectField<Context>( 279 microtask, PromiseReactionJobTask::kContextOffset); 280 TNode<NativeContext> native_context = LoadNativeContext(microtask_context); 281 PrepareForContext(native_context, &done); 282 283 const TNode<Object> argument = 284 LoadObjectField(microtask, PromiseReactionJobTask::kArgumentOffset); 285 const TNode<Object> job_handler = 286 LoadObjectField(microtask, PromiseReactionJobTask::kHandlerOffset); 287 const TNode<HeapObject> promise_or_capability = CAST(LoadObjectField( 288 microtask, PromiseReactionJobTask::kPromiseOrCapabilityOffset)); 289 290 TNode<Object> preserved_embedder_data = LoadObjectField( 291 microtask, 292 PromiseReactionJobTask::kContinuationPreservedEmbedderDataOffset); 293 Label preserved_data_done(this); 294 GotoIf(IsUndefined(preserved_embedder_data), &preserved_data_done); 295 StoreContextElement(native_context, 296 Context::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX, 297 preserved_embedder_data); 298 Goto(&preserved_data_done); 299 BIND(&preserved_data_done); 300 301 // Run the promise before/debug hook if enabled. 302 RunAllPromiseHooks(PromiseHookType::kBefore, microtask_context, 303 promise_or_capability); 304 305 { 306 ScopedExceptionHandler handler(this, &if_exception, &var_exception); 307 CallBuiltin(Builtin::kPromiseRejectReactionJob, microtask_context, 308 argument, job_handler, promise_or_capability); 309 } 310 311 // Run the promise after/debug hook if enabled. 312 RunAllPromiseHooks(PromiseHookType::kAfter, microtask_context, 313 promise_or_capability); 314 315 Label preserved_data_reset_done(this); 316 GotoIf(IsUndefined(preserved_embedder_data), &preserved_data_reset_done); 317 StoreContextElement(native_context, 318 Context::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX, 319 UndefinedConstant()); 320 Goto(&preserved_data_reset_done); 321 BIND(&preserved_data_reset_done); 322 323 RewindEnteredContext(saved_entered_context_count); 324 SetCurrentContext(current_context); 325 Goto(&done); 326 } 327 328 BIND(&is_unreachable); 329 Unreachable(); 330 331 BIND(&if_exception); 332 { 333 // Report unhandled exceptions from microtasks. 334 CallRuntime(Runtime::kReportMessageFromMicrotask, GetCurrentContext(), 335 var_exception.value()); 336 RewindEnteredContext(saved_entered_context_count); 337 SetCurrentContext(current_context); 338 Goto(&done); 339 } 340 341 BIND(&done); 342} 343 344void MicrotaskQueueBuiltinsAssembler::IncrementFinishedMicrotaskCount( 345 TNode<RawPtrT> microtask_queue) { 346 TNode<IntPtrT> count = Load<IntPtrT>( 347 microtask_queue, 348 IntPtrConstant(MicrotaskQueue::kFinishedMicrotaskCountOffset)); 349 TNode<IntPtrT> new_count = IntPtrAdd(count, IntPtrConstant(1)); 350 StoreNoWriteBarrier( 351 MachineType::PointerRepresentation(), microtask_queue, 352 IntPtrConstant(MicrotaskQueue::kFinishedMicrotaskCountOffset), new_count); 353} 354 355TNode<Context> MicrotaskQueueBuiltinsAssembler::GetCurrentContext() { 356 auto ref = ExternalReference::Create(kContextAddress, isolate()); 357 // TODO(delphick): Add a checked cast. For now this is not possible as context 358 // can actually be Smi(0). 359 return TNode<Context>::UncheckedCast(LoadFullTagged(ExternalConstant(ref))); 360} 361 362void MicrotaskQueueBuiltinsAssembler::SetCurrentContext( 363 TNode<Context> context) { 364 auto ref = ExternalReference::Create(kContextAddress, isolate()); 365 StoreFullTaggedNoWriteBarrier(ExternalConstant(ref), context); 366} 367 368TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::GetEnteredContextCount() { 369 auto ref = ExternalReference::handle_scope_implementer_address(isolate()); 370 TNode<RawPtrT> hsi = Load<RawPtrT>(ExternalConstant(ref)); 371 372 using ContextStack = DetachableVector<Context>; 373 TNode<IntPtrT> size_offset = 374 IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset + 375 ContextStack::kSizeOffset); 376 return Load<IntPtrT>(hsi, size_offset); 377} 378 379void MicrotaskQueueBuiltinsAssembler::EnterMicrotaskContext( 380 TNode<Context> native_context) { 381 CSA_DCHECK(this, IsNativeContext(native_context)); 382 383 auto ref = ExternalReference::handle_scope_implementer_address(isolate()); 384 TNode<RawPtrT> hsi = Load<RawPtrT>(ExternalConstant(ref)); 385 386 using ContextStack = DetachableVector<Context>; 387 TNode<IntPtrT> capacity_offset = 388 IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset + 389 ContextStack::kCapacityOffset); 390 TNode<IntPtrT> size_offset = 391 IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset + 392 ContextStack::kSizeOffset); 393 394 TNode<IntPtrT> capacity = Load<IntPtrT>(hsi, capacity_offset); 395 TNode<IntPtrT> size = Load<IntPtrT>(hsi, size_offset); 396 397 Label if_append(this), if_grow(this, Label::kDeferred), done(this); 398 Branch(WordEqual(size, capacity), &if_grow, &if_append); 399 BIND(&if_append); 400 { 401 TNode<IntPtrT> data_offset = 402 IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset + 403 ContextStack::kDataOffset); 404 TNode<RawPtrT> data = Load<RawPtrT>(hsi, data_offset); 405 StoreFullTaggedNoWriteBarrier(data, TimesSystemPointerSize(size), 406 native_context); 407 408 TNode<IntPtrT> new_size = IntPtrAdd(size, IntPtrConstant(1)); 409 StoreNoWriteBarrier(MachineType::PointerRepresentation(), hsi, size_offset, 410 new_size); 411 412 using FlagStack = DetachableVector<int8_t>; 413 TNode<IntPtrT> flag_data_offset = 414 IntPtrConstant(HandleScopeImplementer::kIsMicrotaskContextOffset + 415 FlagStack::kDataOffset); 416 TNode<IntPtrT> flag_capacity_offset = 417 IntPtrConstant(HandleScopeImplementer::kIsMicrotaskContextOffset + 418 FlagStack::kCapacityOffset); 419 TNode<IntPtrT> flag_size_offset = 420 IntPtrConstant(HandleScopeImplementer::kIsMicrotaskContextOffset + 421 FlagStack::kSizeOffset); 422 // Ensure both stacks are in sync. 423 USE(flag_capacity_offset); 424 CSA_DCHECK(this, 425 WordEqual(capacity, Load<IntPtrT>(hsi, flag_capacity_offset))); 426 CSA_DCHECK(this, WordEqual(size, Load<IntPtrT>(hsi, flag_size_offset))); 427 428 TNode<RawPtrT> flag_data = Load<RawPtrT>(hsi, flag_data_offset); 429 StoreNoWriteBarrier(MachineRepresentation::kWord8, flag_data, size, 430 BoolConstant(true)); 431 StoreNoWriteBarrier(MachineType::PointerRepresentation(), hsi, 432 flag_size_offset, new_size); 433 434 Goto(&done); 435 } 436 437 BIND(&if_grow); 438 { 439 TNode<ExternalReference> function = 440 ExternalConstant(ExternalReference::call_enter_context_function()); 441 CallCFunction(function, MachineType::Int32(), 442 std::make_pair(MachineType::Pointer(), hsi), 443 std::make_pair(MachineType::Pointer(), 444 BitcastTaggedToWord(native_context))); 445 Goto(&done); 446 } 447 448 BIND(&done); 449} 450 451void MicrotaskQueueBuiltinsAssembler::RewindEnteredContext( 452 TNode<IntPtrT> saved_entered_context_count) { 453 auto ref = ExternalReference::handle_scope_implementer_address(isolate()); 454 TNode<RawPtrT> hsi = Load<RawPtrT>(ExternalConstant(ref)); 455 456 using ContextStack = DetachableVector<Context>; 457 TNode<IntPtrT> size_offset = 458 IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset + 459 ContextStack::kSizeOffset); 460 461 if (DEBUG_BOOL) { 462 TNode<IntPtrT> size = Load<IntPtrT>(hsi, size_offset); 463 CSA_CHECK(this, IntPtrLessThan(IntPtrConstant(0), size)); 464 CSA_CHECK(this, IntPtrLessThanOrEqual(saved_entered_context_count, size)); 465 } 466 467 StoreNoWriteBarrier(MachineType::PointerRepresentation(), hsi, size_offset, 468 saved_entered_context_count); 469 470 using FlagStack = DetachableVector<int8_t>; 471 StoreNoWriteBarrier( 472 MachineType::PointerRepresentation(), hsi, 473 IntPtrConstant(HandleScopeImplementer::kIsMicrotaskContextOffset + 474 FlagStack::kSizeOffset), 475 saved_entered_context_count); 476} 477 478void MicrotaskQueueBuiltinsAssembler::RunAllPromiseHooks( 479 PromiseHookType type, TNode<Context> context, 480 TNode<HeapObject> promise_or_capability) { 481 TNode<Uint32T> promiseHookFlags = PromiseHookFlags(); 482#ifdef V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS 483 Label hook(this, Label::kDeferred), done_hook(this); 484 Branch(NeedsAnyPromiseHooks(promiseHookFlags), &hook, &done_hook); 485 BIND(&hook); 486 { 487#endif 488 switch (type) { 489 case PromiseHookType::kBefore: 490#ifdef V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS 491 RunContextPromiseHookBefore(context, promise_or_capability, 492 promiseHookFlags); 493#endif 494 RunPromiseHook(Runtime::kPromiseHookBefore, context, 495 promise_or_capability, promiseHookFlags); 496 break; 497 case PromiseHookType::kAfter: 498#ifdef V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS 499 RunContextPromiseHookAfter(context, promise_or_capability, 500 promiseHookFlags); 501#endif 502 RunPromiseHook(Runtime::kPromiseHookAfter, context, 503 promise_or_capability, promiseHookFlags); 504 break; 505 default: 506 UNREACHABLE(); 507 } 508#ifdef V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS 509 Goto(&done_hook); 510 } 511 BIND(&done_hook); 512#endif 513} 514 515void MicrotaskQueueBuiltinsAssembler::RunPromiseHook( 516 Runtime::FunctionId id, TNode<Context> context, 517 TNode<HeapObject> promise_or_capability, 518 TNode<Uint32T> promiseHookFlags) { 519 Label hook(this, Label::kDeferred), done_hook(this); 520 Branch(IsIsolatePromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate( 521 promiseHookFlags), &hook, &done_hook); 522 BIND(&hook); 523 { 524 // Get to the underlying JSPromise instance. 525 TNode<HeapObject> promise = Select<HeapObject>( 526 IsPromiseCapability(promise_or_capability), 527 [=] { 528 return CAST(LoadObjectField(promise_or_capability, 529 PromiseCapability::kPromiseOffset)); 530 }, 531 532 [=] { return promise_or_capability; }); 533 GotoIf(IsUndefined(promise), &done_hook); 534 CallRuntime(id, context, promise); 535 Goto(&done_hook); 536 } 537 BIND(&done_hook); 538} 539 540TF_BUILTIN(EnqueueMicrotask, MicrotaskQueueBuiltinsAssembler) { 541 auto microtask = Parameter<Microtask>(Descriptor::kMicrotask); 542 auto context = Parameter<Context>(Descriptor::kContext); 543 TNode<NativeContext> native_context = LoadNativeContext(context); 544 TNode<RawPtrT> microtask_queue = GetMicrotaskQueue(native_context); 545 546 // Do not store the microtask if MicrotaskQueue is not available, that may 547 // happen when the context shutdown. 548 Label if_shutdown(this, Label::kDeferred); 549 GotoIf(WordEqual(microtask_queue, IntPtrConstant(0)), &if_shutdown); 550 551 TNode<RawPtrT> ring_buffer = GetMicrotaskRingBuffer(microtask_queue); 552 TNode<IntPtrT> capacity = GetMicrotaskQueueCapacity(microtask_queue); 553 TNode<IntPtrT> size = GetMicrotaskQueueSize(microtask_queue); 554 TNode<IntPtrT> start = GetMicrotaskQueueStart(microtask_queue); 555 556 Label if_grow(this, Label::kDeferred); 557 GotoIf(IntPtrEqual(size, capacity), &if_grow); 558 559 // |microtask_queue| has an unused slot to store |microtask|. 560 { 561 StoreNoWriteBarrier(MachineType::PointerRepresentation(), ring_buffer, 562 CalculateRingBufferOffset(capacity, start, size), 563 BitcastTaggedToWord(microtask)); 564 StoreNoWriteBarrier(MachineType::PointerRepresentation(), microtask_queue, 565 IntPtrConstant(MicrotaskQueue::kSizeOffset), 566 IntPtrAdd(size, IntPtrConstant(1))); 567 Return(UndefinedConstant()); 568 } 569 570 // |microtask_queue| has no space to store |microtask|. Fall back to C++ 571 // implementation to grow the buffer. 572 BIND(&if_grow); 573 { 574 TNode<ExternalReference> isolate_constant = 575 ExternalConstant(ExternalReference::isolate_address(isolate())); 576 TNode<ExternalReference> function = 577 ExternalConstant(ExternalReference::call_enqueue_microtask_function()); 578 CallCFunction(function, MachineType::AnyTagged(), 579 std::make_pair(MachineType::Pointer(), isolate_constant), 580 std::make_pair(MachineType::IntPtr(), microtask_queue), 581 std::make_pair(MachineType::AnyTagged(), microtask)); 582 Return(UndefinedConstant()); 583 } 584 585 Bind(&if_shutdown); 586 Return(UndefinedConstant()); 587} 588 589TF_BUILTIN(RunMicrotasks, MicrotaskQueueBuiltinsAssembler) { 590 // Load the current context from the isolate. 591 TNode<Context> current_context = GetCurrentContext(); 592 593 auto microtask_queue = 594 UncheckedParameter<RawPtrT>(Descriptor::kMicrotaskQueue); 595 596 Label loop(this), done(this); 597 Goto(&loop); 598 BIND(&loop); 599 600 TNode<IntPtrT> size = GetMicrotaskQueueSize(microtask_queue); 601 602 // Exit if the queue is empty. 603 GotoIf(WordEqual(size, IntPtrConstant(0)), &done); 604 605 TNode<RawPtrT> ring_buffer = GetMicrotaskRingBuffer(microtask_queue); 606 TNode<IntPtrT> capacity = GetMicrotaskQueueCapacity(microtask_queue); 607 TNode<IntPtrT> start = GetMicrotaskQueueStart(microtask_queue); 608 609 TNode<IntPtrT> offset = 610 CalculateRingBufferOffset(capacity, start, IntPtrConstant(0)); 611 TNode<RawPtrT> microtask_pointer = Load<RawPtrT>(ring_buffer, offset); 612 TNode<Microtask> microtask = CAST(BitcastWordToTagged(microtask_pointer)); 613 614 TNode<IntPtrT> new_size = IntPtrSub(size, IntPtrConstant(1)); 615 TNode<IntPtrT> new_start = WordAnd(IntPtrAdd(start, IntPtrConstant(1)), 616 IntPtrSub(capacity, IntPtrConstant(1))); 617 618 // Remove |microtask| from |ring_buffer| before running it, since its 619 // invocation may add another microtask into |ring_buffer|. 620 SetMicrotaskQueueSize(microtask_queue, new_size); 621 SetMicrotaskQueueStart(microtask_queue, new_start); 622 623 RunSingleMicrotask(current_context, microtask); 624 IncrementFinishedMicrotaskCount(microtask_queue); 625 Goto(&loop); 626 627 BIND(&done); 628 { 629 // Reset the "current microtask" on the isolate. 630 StoreRoot(RootIndex::kCurrentMicrotask, UndefinedConstant()); 631 Return(UndefinedConstant()); 632 } 633} 634 635} // namespace internal 636} // namespace v8 637