1 // Copyright 2016 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/builtins/builtins-promise-gen.h" 6 7 #include "src/builtins/builtins-constructor-gen.h" 8 #include "src/builtins/builtins-iterator-gen.h" 9 #include "src/builtins/builtins-utils-gen.h" 10 #include "src/builtins/builtins.h" 11 #include "src/code-factory.h" 12 #include "src/code-stub-assembler.h" 13 #include "src/objects-inl.h" 14 #include "src/objects/js-promise.h" 15 16 namespace v8 { 17 namespace internal { 18 19 using compiler::Node; 20 21 Node* PromiseBuiltinsAssembler::AllocateJSPromise(Node* context) { 22 Node* const native_context = LoadNativeContext(context); 23 Node* const promise_fun = 24 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); 25 CSA_ASSERT(this, IsFunctionWithPrototypeSlotMap(LoadMap(promise_fun))); 26 Node* const promise_map = 27 LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset); 28 Node* const promise = Allocate(JSPromise::kSizeWithEmbedderFields); 29 StoreMapNoWriteBarrier(promise, promise_map); 30 StoreObjectFieldRoot(promise, JSPromise::kPropertiesOrHashOffset, 31 Heap::kEmptyFixedArrayRootIndex); 32 StoreObjectFieldRoot(promise, JSPromise::kElementsOffset, 33 Heap::kEmptyFixedArrayRootIndex); 34 return promise; 35 } 36 37 void PromiseBuiltinsAssembler::PromiseInit(Node* promise) { 38 STATIC_ASSERT(v8::Promise::kPending == 0); 39 StoreObjectFieldNoWriteBarrier(promise, JSPromise::kReactionsOrResultOffset, 40 SmiConstant(Smi::kZero)); 41 StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset, 42 SmiConstant(Smi::kZero)); 43 for (int i = 0; i < v8::Promise::kEmbedderFieldCount; i++) { 44 int offset = JSPromise::kSize + i * kPointerSize; 45 StoreObjectFieldNoWriteBarrier(promise, offset, SmiConstant(Smi::kZero)); 46 } 47 } 48 49 Node* PromiseBuiltinsAssembler::AllocateAndInitJSPromise(Node* context) { 50 return AllocateAndInitJSPromise(context, UndefinedConstant()); 51 } 52 53 Node* PromiseBuiltinsAssembler::AllocateAndInitJSPromise(Node* context, 54 Node* parent) { 55 Node* const instance = AllocateJSPromise(context); 56 PromiseInit(instance); 57 58 Label out(this); 59 GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &out); 60 CallRuntime(Runtime::kPromiseHookInit, context, instance, parent); 61 Goto(&out); 62 63 BIND(&out); 64 return instance; 65 } 66 67 Node* PromiseBuiltinsAssembler::AllocateAndSetJSPromise( 68 Node* context, v8::Promise::PromiseState status, Node* result) { 69 DCHECK_NE(Promise::kPending, status); 70 71 Node* const instance = AllocateJSPromise(context); 72 StoreObjectFieldNoWriteBarrier(instance, JSPromise::kReactionsOrResultOffset, 73 result); 74 STATIC_ASSERT(JSPromise::kStatusShift == 0); 75 StoreObjectFieldNoWriteBarrier(instance, JSPromise::kFlagsOffset, 76 SmiConstant(status)); 77 for (int i = 0; i < v8::Promise::kEmbedderFieldCount; i++) { 78 int offset = JSPromise::kSize + i * kPointerSize; 79 StoreObjectFieldNoWriteBarrier(instance, offset, SmiConstant(0)); 80 } 81 82 Label out(this); 83 GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &out); 84 CallRuntime(Runtime::kPromiseHookInit, context, instance, 85 UndefinedConstant()); 86 Goto(&out); 87 88 BIND(&out); 89 return instance; 90 } 91 92 std::pair<Node*, Node*> 93 PromiseBuiltinsAssembler::CreatePromiseResolvingFunctions( 94 Node* promise, Node* debug_event, Node* native_context) { 95 Node* const promise_context = CreatePromiseResolvingFunctionsContext( 96 promise, debug_event, native_context); 97 Node* const map = LoadContextElement( 98 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); 99 Node* const resolve_info = LoadContextElement( 100 native_context, 101 Context::PROMISE_CAPABILITY_DEFAULT_RESOLVE_SHARED_FUN_INDEX); 102 Node* const resolve = 103 AllocateFunctionWithMapAndContext(map, resolve_info, promise_context); 104 Node* const reject_info = LoadContextElement( 105 native_context, 106 Context::PROMISE_CAPABILITY_DEFAULT_REJECT_SHARED_FUN_INDEX); 107 Node* const reject = 108 AllocateFunctionWithMapAndContext(map, reject_info, promise_context); 109 return std::make_pair(resolve, reject); 110 } 111 112 // ES #sec-newpromisecapability 113 TF_BUILTIN(NewPromiseCapability, PromiseBuiltinsAssembler) { 114 Node* const context = Parameter(Descriptor::kContext); 115 Node* const constructor = Parameter(Descriptor::kConstructor); 116 Node* const debug_event = Parameter(Descriptor::kDebugEvent); 117 Node* const native_context = LoadNativeContext(context); 118 119 Label if_not_constructor(this, Label::kDeferred), 120 if_notcallable(this, Label::kDeferred), if_fast_promise_capability(this), 121 if_slow_promise_capability(this, Label::kDeferred); 122 GotoIf(TaggedIsSmi(constructor), &if_not_constructor); 123 GotoIfNot(IsConstructorMap(LoadMap(constructor)), &if_not_constructor); 124 Branch(WordEqual(constructor, 125 LoadContextElement(native_context, 126 Context::PROMISE_FUNCTION_INDEX)), 127 &if_fast_promise_capability, &if_slow_promise_capability); 128 129 BIND(&if_fast_promise_capability); 130 { 131 Node* promise = 132 AllocateAndInitJSPromise(native_context, UndefinedConstant()); 133 134 Node* resolve = nullptr; 135 Node* reject = nullptr; 136 std::tie(resolve, reject) = 137 CreatePromiseResolvingFunctions(promise, debug_event, native_context); 138 139 Node* capability = Allocate(PromiseCapability::kSize); 140 StoreMapNoWriteBarrier(capability, Heap::kPromiseCapabilityMapRootIndex); 141 StoreObjectFieldNoWriteBarrier(capability, 142 PromiseCapability::kPromiseOffset, promise); 143 StoreObjectFieldNoWriteBarrier(capability, 144 PromiseCapability::kResolveOffset, resolve); 145 StoreObjectFieldNoWriteBarrier(capability, PromiseCapability::kRejectOffset, 146 reject); 147 Return(capability); 148 } 149 150 BIND(&if_slow_promise_capability); 151 { 152 Node* capability = Allocate(PromiseCapability::kSize); 153 StoreMapNoWriteBarrier(capability, Heap::kPromiseCapabilityMapRootIndex); 154 StoreObjectFieldRoot(capability, PromiseCapability::kPromiseOffset, 155 Heap::kUndefinedValueRootIndex); 156 StoreObjectFieldRoot(capability, PromiseCapability::kResolveOffset, 157 Heap::kUndefinedValueRootIndex); 158 StoreObjectFieldRoot(capability, PromiseCapability::kRejectOffset, 159 Heap::kUndefinedValueRootIndex); 160 161 Node* executor_context = 162 CreatePromiseGetCapabilitiesExecutorContext(capability, native_context); 163 Node* executor_info = LoadContextElement( 164 native_context, Context::PROMISE_GET_CAPABILITIES_EXECUTOR_SHARED_FUN); 165 Node* function_map = LoadContextElement( 166 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); 167 Node* executor = AllocateFunctionWithMapAndContext( 168 function_map, executor_info, executor_context); 169 170 Node* promise = ConstructJS(CodeFactory::Construct(isolate()), 171 native_context, constructor, executor); 172 StoreObjectField(capability, PromiseCapability::kPromiseOffset, promise); 173 174 Node* resolve = 175 LoadObjectField(capability, PromiseCapability::kResolveOffset); 176 GotoIf(TaggedIsSmi(resolve), &if_notcallable); 177 GotoIfNot(IsCallable(resolve), &if_notcallable); 178 179 Node* reject = 180 LoadObjectField(capability, PromiseCapability::kRejectOffset); 181 GotoIf(TaggedIsSmi(reject), &if_notcallable); 182 GotoIfNot(IsCallable(reject), &if_notcallable); 183 Return(capability); 184 } 185 186 BIND(&if_not_constructor); 187 ThrowTypeError(context, MessageTemplate::kNotConstructor, constructor); 188 189 BIND(&if_notcallable); 190 ThrowTypeError(context, MessageTemplate::kPromiseNonCallable); 191 } 192 193 Node* PromiseBuiltinsAssembler::CreatePromiseContext(Node* native_context, 194 int slots) { 195 DCHECK_GE(slots, Context::MIN_CONTEXT_SLOTS); 196 197 Node* const context = AllocateInNewSpace(FixedArray::SizeFor(slots)); 198 InitializeFunctionContext(native_context, context, slots); 199 return context; 200 } 201 202 Node* PromiseBuiltinsAssembler::CreatePromiseAllResolveElementContext( 203 Node* promise_capability, Node* native_context) { 204 CSA_ASSERT(this, IsNativeContext(native_context)); 205 206 // TODO(bmeurer): Manually fold this into a single allocation. 207 Node* const array_map = LoadContextElement( 208 native_context, Context::JS_ARRAY_PACKED_ELEMENTS_MAP_INDEX); 209 Node* const values_array = AllocateJSArray(PACKED_ELEMENTS, array_map, 210 IntPtrConstant(0), SmiConstant(0)); 211 212 Node* const context = 213 CreatePromiseContext(native_context, kPromiseAllResolveElementLength); 214 StoreContextElementNoWriteBarrier( 215 context, kPromiseAllResolveElementRemainingSlot, SmiConstant(1)); 216 StoreContextElementNoWriteBarrier( 217 context, kPromiseAllResolveElementCapabilitySlot, promise_capability); 218 StoreContextElementNoWriteBarrier( 219 context, kPromiseAllResolveElementValuesArraySlot, values_array); 220 221 return context; 222 } 223 224 Node* PromiseBuiltinsAssembler::CreatePromiseAllResolveElementFunction( 225 Node* context, TNode<Smi> index, Node* native_context) { 226 CSA_ASSERT(this, SmiGreaterThan(index, SmiConstant(0))); 227 CSA_ASSERT(this, SmiLessThanOrEqual( 228 index, SmiConstant(PropertyArray::HashField::kMax))); 229 CSA_ASSERT(this, IsNativeContext(native_context)); 230 231 Node* const map = LoadContextElement( 232 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); 233 Node* const resolve_info = LoadContextElement( 234 native_context, Context::PROMISE_ALL_RESOLVE_ELEMENT_SHARED_FUN); 235 Node* const resolve = 236 AllocateFunctionWithMapAndContext(map, resolve_info, context); 237 238 STATIC_ASSERT(PropertyArray::kNoHashSentinel == 0); 239 StoreObjectFieldNoWriteBarrier(resolve, JSFunction::kPropertiesOrHashOffset, 240 index); 241 242 return resolve; 243 } 244 245 Node* PromiseBuiltinsAssembler::CreatePromiseResolvingFunctionsContext( 246 Node* promise, Node* debug_event, Node* native_context) { 247 Node* const context = 248 CreatePromiseContext(native_context, kPromiseContextLength); 249 StoreContextElementNoWriteBarrier(context, kPromiseSlot, promise); 250 StoreContextElementNoWriteBarrier(context, kAlreadyResolvedSlot, 251 FalseConstant()); 252 StoreContextElementNoWriteBarrier(context, kDebugEventSlot, debug_event); 253 return context; 254 } 255 256 Node* PromiseBuiltinsAssembler::CreatePromiseGetCapabilitiesExecutorContext( 257 Node* promise_capability, Node* native_context) { 258 int kContextLength = kCapabilitiesContextLength; 259 Node* context = CreatePromiseContext(native_context, kContextLength); 260 StoreContextElementNoWriteBarrier(context, kCapabilitySlot, 261 promise_capability); 262 return context; 263 } 264 265 Node* PromiseBuiltinsAssembler::PromiseHasHandler(Node* promise) { 266 Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset); 267 return IsSetWord(SmiUntag(flags), 1 << JSPromise::kHasHandlerBit); 268 } 269 270 void PromiseBuiltinsAssembler::PromiseSetHasHandler(Node* promise) { 271 TNode<Smi> const flags = 272 CAST(LoadObjectField(promise, JSPromise::kFlagsOffset)); 273 TNode<Smi> const new_flags = 274 SmiOr(flags, SmiConstant(1 << JSPromise::kHasHandlerBit)); 275 StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset, new_flags); 276 } 277 278 Node* PromiseBuiltinsAssembler::IsPromiseStatus( 279 Node* actual, v8::Promise::PromiseState expected) { 280 return Word32Equal(actual, Int32Constant(expected)); 281 } 282 283 Node* PromiseBuiltinsAssembler::PromiseStatus(Node* promise) { 284 STATIC_ASSERT(JSPromise::kStatusShift == 0); 285 TNode<Smi> const flags = 286 CAST(LoadObjectField(promise, JSPromise::kFlagsOffset)); 287 return Word32And(SmiToInt32(flags), Int32Constant(JSPromise::kStatusMask)); 288 } 289 290 void PromiseBuiltinsAssembler::PromiseSetStatus( 291 Node* promise, v8::Promise::PromiseState const status) { 292 CSA_ASSERT(this, 293 IsPromiseStatus(PromiseStatus(promise), v8::Promise::kPending)); 294 CHECK_NE(status, v8::Promise::kPending); 295 296 TNode<Smi> mask = SmiConstant(status); 297 TNode<Smi> const flags = 298 CAST(LoadObjectField(promise, JSPromise::kFlagsOffset)); 299 StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset, 300 SmiOr(flags, mask)); 301 } 302 303 void PromiseBuiltinsAssembler::PromiseSetHandledHint(Node* promise) { 304 TNode<Smi> const flags = 305 CAST(LoadObjectField(promise, JSPromise::kFlagsOffset)); 306 TNode<Smi> const new_flags = 307 SmiOr(flags, SmiConstant(1 << JSPromise::kHandledHintBit)); 308 StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset, new_flags); 309 } 310 311 // ES #sec-performpromisethen 312 void PromiseBuiltinsAssembler::PerformPromiseThen( 313 Node* context, Node* promise, Node* on_fulfilled, Node* on_rejected, 314 Node* result_promise_or_capability) { 315 CSA_ASSERT(this, TaggedIsNotSmi(promise)); 316 CSA_ASSERT(this, IsJSPromise(promise)); 317 CSA_ASSERT(this, 318 Word32Or(IsCallable(on_fulfilled), IsUndefined(on_fulfilled))); 319 CSA_ASSERT(this, Word32Or(IsCallable(on_rejected), IsUndefined(on_rejected))); 320 CSA_ASSERT(this, TaggedIsNotSmi(result_promise_or_capability)); 321 CSA_ASSERT(this, Word32Or(IsJSPromise(result_promise_or_capability), 322 IsPromiseCapability(result_promise_or_capability))); 323 324 Label if_pending(this), if_notpending(this), done(this); 325 Node* const status = PromiseStatus(promise); 326 Branch(IsPromiseStatus(status, v8::Promise::kPending), &if_pending, 327 &if_notpending); 328 329 BIND(&if_pending); 330 { 331 // The {promise} is still in "Pending" state, so we just record a new 332 // PromiseReaction holding both the onFulfilled and onRejected callbacks. 333 // Once the {promise} is resolved we decide on the concrete handler to 334 // push onto the microtask queue. 335 Node* const promise_reactions = 336 LoadObjectField(promise, JSPromise::kReactionsOrResultOffset); 337 Node* const reaction = 338 AllocatePromiseReaction(promise_reactions, result_promise_or_capability, 339 on_fulfilled, on_rejected); 340 StoreObjectField(promise, JSPromise::kReactionsOrResultOffset, reaction); 341 Goto(&done); 342 } 343 344 BIND(&if_notpending); 345 { 346 VARIABLE(var_map, MachineRepresentation::kTagged); 347 VARIABLE(var_handler, MachineRepresentation::kTagged); 348 Label if_fulfilled(this), if_rejected(this, Label::kDeferred), 349 enqueue(this); 350 Branch(IsPromiseStatus(status, v8::Promise::kFulfilled), &if_fulfilled, 351 &if_rejected); 352 353 BIND(&if_fulfilled); 354 { 355 var_map.Bind(LoadRoot(Heap::kPromiseFulfillReactionJobTaskMapRootIndex)); 356 var_handler.Bind(on_fulfilled); 357 Goto(&enqueue); 358 } 359 360 BIND(&if_rejected); 361 { 362 CSA_ASSERT(this, IsPromiseStatus(status, v8::Promise::kRejected)); 363 var_map.Bind(LoadRoot(Heap::kPromiseRejectReactionJobTaskMapRootIndex)); 364 var_handler.Bind(on_rejected); 365 GotoIf(PromiseHasHandler(promise), &enqueue); 366 CallRuntime(Runtime::kPromiseRevokeReject, context, promise); 367 Goto(&enqueue); 368 } 369 370 BIND(&enqueue); 371 Node* argument = 372 LoadObjectField(promise, JSPromise::kReactionsOrResultOffset); 373 Node* microtask = AllocatePromiseReactionJobTask( 374 var_map.value(), context, argument, var_handler.value(), 375 result_promise_or_capability); 376 CallBuiltin(Builtins::kEnqueueMicrotask, NoContextConstant(), microtask); 377 Goto(&done); 378 } 379 380 BIND(&done); 381 PromiseSetHasHandler(promise); 382 } 383 384 // ES #sec-performpromisethen 385 TF_BUILTIN(PerformPromiseThen, PromiseBuiltinsAssembler) { 386 Node* const context = Parameter(Descriptor::kContext); 387 Node* const promise = Parameter(Descriptor::kPromise); 388 Node* const on_fulfilled = Parameter(Descriptor::kOnFulfilled); 389 Node* const on_rejected = Parameter(Descriptor::kOnRejected); 390 Node* const result_promise = Parameter(Descriptor::kResultPromise); 391 392 CSA_ASSERT(this, TaggedIsNotSmi(result_promise)); 393 CSA_ASSERT(this, IsJSPromise(result_promise)); 394 395 PerformPromiseThen(context, promise, on_fulfilled, on_rejected, 396 result_promise); 397 Return(result_promise); 398 } 399 400 Node* PromiseBuiltinsAssembler::AllocatePromiseReaction( 401 Node* next, Node* promise_or_capability, Node* fulfill_handler, 402 Node* reject_handler) { 403 Node* const reaction = Allocate(PromiseReaction::kSize); 404 StoreMapNoWriteBarrier(reaction, Heap::kPromiseReactionMapRootIndex); 405 StoreObjectFieldNoWriteBarrier(reaction, PromiseReaction::kNextOffset, next); 406 StoreObjectFieldNoWriteBarrier(reaction, 407 PromiseReaction::kPromiseOrCapabilityOffset, 408 promise_or_capability); 409 StoreObjectFieldNoWriteBarrier( 410 reaction, PromiseReaction::kFulfillHandlerOffset, fulfill_handler); 411 StoreObjectFieldNoWriteBarrier( 412 reaction, PromiseReaction::kRejectHandlerOffset, reject_handler); 413 return reaction; 414 } 415 416 Node* PromiseBuiltinsAssembler::AllocatePromiseReactionJobTask( 417 Node* map, Node* context, Node* argument, Node* handler, 418 Node* promise_or_capability) { 419 Node* const microtask = Allocate(PromiseReactionJobTask::kSize); 420 StoreMapNoWriteBarrier(microtask, map); 421 StoreObjectFieldNoWriteBarrier( 422 microtask, PromiseReactionJobTask::kArgumentOffset, argument); 423 StoreObjectFieldNoWriteBarrier( 424 microtask, PromiseReactionJobTask::kContextOffset, context); 425 StoreObjectFieldNoWriteBarrier( 426 microtask, PromiseReactionJobTask::kHandlerOffset, handler); 427 StoreObjectFieldNoWriteBarrier( 428 microtask, PromiseReactionJobTask::kPromiseOrCapabilityOffset, 429 promise_or_capability); 430 return microtask; 431 } 432 433 Node* PromiseBuiltinsAssembler::AllocatePromiseReactionJobTask( 434 Heap::RootListIndex map_root_index, Node* context, Node* argument, 435 Node* handler, Node* promise_or_capability) { 436 DCHECK(map_root_index == Heap::kPromiseFulfillReactionJobTaskMapRootIndex || 437 map_root_index == Heap::kPromiseRejectReactionJobTaskMapRootIndex); 438 Node* const map = LoadRoot(map_root_index); 439 return AllocatePromiseReactionJobTask(map, context, argument, handler, 440 promise_or_capability); 441 } 442 443 Node* PromiseBuiltinsAssembler::AllocatePromiseResolveThenableJobTask( 444 Node* promise_to_resolve, Node* then, Node* thenable, Node* context) { 445 Node* const microtask = Allocate(PromiseResolveThenableJobTask::kSize); 446 StoreMapNoWriteBarrier(microtask, 447 Heap::kPromiseResolveThenableJobTaskMapRootIndex); 448 StoreObjectFieldNoWriteBarrier( 449 microtask, PromiseResolveThenableJobTask::kContextOffset, context); 450 StoreObjectFieldNoWriteBarrier( 451 microtask, PromiseResolveThenableJobTask::kPromiseToResolveOffset, 452 promise_to_resolve); 453 StoreObjectFieldNoWriteBarrier( 454 microtask, PromiseResolveThenableJobTask::kThenOffset, then); 455 StoreObjectFieldNoWriteBarrier( 456 microtask, PromiseResolveThenableJobTask::kThenableOffset, thenable); 457 return microtask; 458 } 459 460 // ES #sec-triggerpromisereactions 461 Node* PromiseBuiltinsAssembler::TriggerPromiseReactions( 462 Node* context, Node* reactions, Node* argument, 463 PromiseReaction::Type type) { 464 // We need to reverse the {reactions} here, since we record them on the 465 // JSPromise in the reverse order. 466 { 467 VARIABLE(var_current, MachineRepresentation::kTagged, reactions); 468 VARIABLE(var_reversed, MachineRepresentation::kTagged, 469 SmiConstant(Smi::kZero)); 470 471 Label loop(this, {&var_current, &var_reversed}), done_loop(this); 472 Goto(&loop); 473 BIND(&loop); 474 { 475 Node* current = var_current.value(); 476 GotoIf(TaggedIsSmi(current), &done_loop); 477 var_current.Bind(LoadObjectField(current, PromiseReaction::kNextOffset)); 478 StoreObjectField(current, PromiseReaction::kNextOffset, 479 var_reversed.value()); 480 var_reversed.Bind(current); 481 Goto(&loop); 482 } 483 BIND(&done_loop); 484 reactions = var_reversed.value(); 485 } 486 487 // Morph the {reactions} into PromiseReactionJobTasks and push them 488 // onto the microtask queue. 489 { 490 VARIABLE(var_current, MachineRepresentation::kTagged, reactions); 491 492 Label loop(this, {&var_current}), done_loop(this); 493 Goto(&loop); 494 BIND(&loop); 495 { 496 Node* current = var_current.value(); 497 GotoIf(TaggedIsSmi(current), &done_loop); 498 var_current.Bind(LoadObjectField(current, PromiseReaction::kNextOffset)); 499 500 // Morph {current} from a PromiseReaction into a PromiseReactionJobTask 501 // and schedule that on the microtask queue. We try to minimize the number 502 // of stores here to avoid screwing up the store buffer. 503 STATIC_ASSERT(PromiseReaction::kSize == PromiseReactionJobTask::kSize); 504 if (type == PromiseReaction::kFulfill) { 505 StoreMapNoWriteBarrier( 506 current, Heap::kPromiseFulfillReactionJobTaskMapRootIndex); 507 StoreObjectField(current, PromiseReactionJobTask::kArgumentOffset, 508 argument); 509 StoreObjectField(current, PromiseReactionJobTask::kContextOffset, 510 context); 511 STATIC_ASSERT(PromiseReaction::kFulfillHandlerOffset == 512 PromiseReactionJobTask::kHandlerOffset); 513 STATIC_ASSERT(PromiseReaction::kPromiseOrCapabilityOffset == 514 PromiseReactionJobTask::kPromiseOrCapabilityOffset); 515 } else { 516 Node* handler = 517 LoadObjectField(current, PromiseReaction::kRejectHandlerOffset); 518 StoreMapNoWriteBarrier(current, 519 Heap::kPromiseRejectReactionJobTaskMapRootIndex); 520 StoreObjectField(current, PromiseReactionJobTask::kArgumentOffset, 521 argument); 522 StoreObjectField(current, PromiseReactionJobTask::kContextOffset, 523 context); 524 StoreObjectField(current, PromiseReactionJobTask::kHandlerOffset, 525 handler); 526 STATIC_ASSERT(PromiseReaction::kPromiseOrCapabilityOffset == 527 PromiseReactionJobTask::kPromiseOrCapabilityOffset); 528 } 529 CallBuiltin(Builtins::kEnqueueMicrotask, NoContextConstant(), current); 530 Goto(&loop); 531 } 532 BIND(&done_loop); 533 } 534 535 return UndefinedConstant(); 536 } 537 538 template <typename... TArgs> 539 Node* PromiseBuiltinsAssembler::InvokeThen(Node* native_context, Node* receiver, 540 TArgs... args) { 541 CSA_ASSERT(this, IsNativeContext(native_context)); 542 543 VARIABLE(var_result, MachineRepresentation::kTagged); 544 Label if_fast(this), if_slow(this, Label::kDeferred), done(this, &var_result); 545 GotoIf(TaggedIsSmi(receiver), &if_slow); 546 Node* const receiver_map = LoadMap(receiver); 547 // We can skip the "then" lookup on {receiver} if it's [[Prototype]] 548 // is the (initial) Promise.prototype and the Promise#then protector 549 // is intact, as that guards the lookup path for the "then" property 550 // on JSPromise instances which have the (initial) %PromisePrototype%. 551 BranchIfPromiseThenLookupChainIntact(native_context, receiver_map, &if_fast, 552 &if_slow); 553 554 BIND(&if_fast); 555 { 556 Node* const then = 557 LoadContextElement(native_context, Context::PROMISE_THEN_INDEX); 558 Node* const result = 559 CallJS(CodeFactory::CallFunction( 560 isolate(), ConvertReceiverMode::kNotNullOrUndefined), 561 native_context, then, receiver, args...); 562 var_result.Bind(result); 563 Goto(&done); 564 } 565 566 BIND(&if_slow); 567 { 568 Node* const then = GetProperty(native_context, receiver, 569 isolate()->factory()->then_string()); 570 Node* const result = CallJS( 571 CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined), 572 native_context, then, receiver, args...); 573 var_result.Bind(result); 574 Goto(&done); 575 } 576 577 BIND(&done); 578 return var_result.value(); 579 } 580 581 Node* PromiseBuiltinsAssembler::InvokeResolve(Node* native_context, 582 Node* constructor, Node* value, 583 Label* if_exception, 584 Variable* var_exception) { 585 CSA_ASSERT(this, IsNativeContext(native_context)); 586 587 VARIABLE(var_result, MachineRepresentation::kTagged); 588 Label if_fast(this), if_slow(this, Label::kDeferred), done(this, &var_result); 589 // We can skip the "resolve" lookup on {constructor} if it's the 590 // Promise constructor and the Promise.resolve protector is intact, 591 // as that guards the lookup path for the "resolve" property on the 592 // Promise constructor. 593 BranchIfPromiseResolveLookupChainIntact(native_context, constructor, &if_fast, 594 &if_slow); 595 596 BIND(&if_fast); 597 { 598 Node* const result = CallBuiltin(Builtins::kPromiseResolve, native_context, 599 constructor, value); 600 GotoIfException(result, if_exception, var_exception); 601 602 var_result.Bind(result); 603 Goto(&done); 604 } 605 606 BIND(&if_slow); 607 { 608 Node* const resolve = 609 GetProperty(native_context, constructor, factory()->resolve_string()); 610 GotoIfException(resolve, if_exception, var_exception); 611 612 Node* const result = CallJS( 613 CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined), 614 native_context, resolve, constructor, value); 615 GotoIfException(result, if_exception, var_exception); 616 617 var_result.Bind(result); 618 Goto(&done); 619 } 620 621 BIND(&done); 622 return var_result.value(); 623 } 624 625 void PromiseBuiltinsAssembler::BranchIfPromiseResolveLookupChainIntact( 626 Node* native_context, Node* constructor, Label* if_fast, Label* if_slow) { 627 CSA_ASSERT(this, IsNativeContext(native_context)); 628 629 GotoIfForceSlowPath(if_slow); 630 Node* const promise_fun = 631 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); 632 GotoIfNot(WordEqual(promise_fun, constructor), if_slow); 633 Branch(IsPromiseResolveProtectorCellInvalid(), if_slow, if_fast); 634 } 635 636 void PromiseBuiltinsAssembler::BranchIfPromiseSpeciesLookupChainIntact( 637 Node* native_context, Node* promise_map, Label* if_fast, Label* if_slow) { 638 CSA_ASSERT(this, IsNativeContext(native_context)); 639 CSA_ASSERT(this, IsJSPromiseMap(promise_map)); 640 641 Node* const promise_prototype = 642 LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX); 643 GotoIfForceSlowPath(if_slow); 644 GotoIfNot(WordEqual(LoadMapPrototype(promise_map), promise_prototype), 645 if_slow); 646 Branch(IsPromiseSpeciesProtectorCellInvalid(), if_slow, if_fast); 647 } 648 649 void PromiseBuiltinsAssembler::BranchIfPromiseThenLookupChainIntact( 650 Node* native_context, Node* receiver_map, Label* if_fast, Label* if_slow) { 651 CSA_ASSERT(this, IsMap(receiver_map)); 652 CSA_ASSERT(this, IsNativeContext(native_context)); 653 654 GotoIfForceSlowPath(if_slow); 655 GotoIfNot(IsJSPromiseMap(receiver_map), if_slow); 656 Node* const promise_prototype = 657 LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX); 658 GotoIfNot(WordEqual(LoadMapPrototype(receiver_map), promise_prototype), 659 if_slow); 660 Branch(IsPromiseThenProtectorCellInvalid(), if_slow, if_fast); 661 } 662 663 void PromiseBuiltinsAssembler::BranchIfAccessCheckFailed( 664 Node* context, Node* native_context, Node* promise_constructor, 665 Node* executor, Label* if_noaccess) { 666 VARIABLE(var_executor, MachineRepresentation::kTagged); 667 var_executor.Bind(executor); 668 Label has_access(this), call_runtime(this, Label::kDeferred); 669 670 // If executor is a bound function, load the bound function until we've 671 // reached an actual function. 672 Label found_function(this), loop_over_bound_function(this, &var_executor); 673 Goto(&loop_over_bound_function); 674 BIND(&loop_over_bound_function); 675 { 676 Node* executor_type = LoadInstanceType(var_executor.value()); 677 GotoIf(InstanceTypeEqual(executor_type, JS_FUNCTION_TYPE), &found_function); 678 GotoIfNot(InstanceTypeEqual(executor_type, JS_BOUND_FUNCTION_TYPE), 679 &call_runtime); 680 var_executor.Bind(LoadObjectField( 681 var_executor.value(), JSBoundFunction::kBoundTargetFunctionOffset)); 682 Goto(&loop_over_bound_function); 683 } 684 685 // Load the context from the function and compare it to the Promise 686 // constructor's context. If they match, everything is fine, otherwise, bail 687 // out to the runtime. 688 BIND(&found_function); 689 { 690 Node* function_context = 691 LoadObjectField(var_executor.value(), JSFunction::kContextOffset); 692 Node* native_function_context = LoadNativeContext(function_context); 693 Branch(WordEqual(native_context, native_function_context), &has_access, 694 &call_runtime); 695 } 696 697 BIND(&call_runtime); 698 { 699 Branch(WordEqual(CallRuntime(Runtime::kAllowDynamicFunction, context, 700 promise_constructor), 701 TrueConstant()), 702 &has_access, if_noaccess); 703 } 704 705 BIND(&has_access); 706 } 707 708 void PromiseBuiltinsAssembler::SetForwardingHandlerIfTrue( 709 Node* context, Node* condition, const NodeGenerator& object) { 710 Label done(this); 711 GotoIfNot(condition, &done); 712 SetPropertyStrict( 713 CAST(context), CAST(object()), 714 HeapConstant(factory()->promise_forwarding_handler_symbol()), 715 TrueConstant()); 716 Goto(&done); 717 BIND(&done); 718 } 719 720 void PromiseBuiltinsAssembler::SetPromiseHandledByIfTrue( 721 Node* context, Node* condition, Node* promise, 722 const NodeGenerator& handled_by) { 723 Label done(this); 724 GotoIfNot(condition, &done); 725 GotoIf(TaggedIsSmi(promise), &done); 726 GotoIfNot(HasInstanceType(promise, JS_PROMISE_TYPE), &done); 727 SetPropertyStrict(CAST(context), CAST(promise), 728 HeapConstant(factory()->promise_handled_by_symbol()), 729 CAST(handled_by())); 730 Goto(&done); 731 BIND(&done); 732 } 733 734 // ES #sec-promise-reject-functions 735 TF_BUILTIN(PromiseCapabilityDefaultReject, PromiseBuiltinsAssembler) { 736 Node* const reason = Parameter(Descriptor::kReason); 737 Node* const context = Parameter(Descriptor::kContext); 738 739 // 2. Let promise be F.[[Promise]]. 740 Node* const promise = LoadContextElement(context, kPromiseSlot); 741 742 // 3. Let alreadyResolved be F.[[AlreadyResolved]]. 743 Label if_already_resolved(this, Label::kDeferred); 744 Node* const already_resolved = 745 LoadContextElement(context, kAlreadyResolvedSlot); 746 747 // 4. If alreadyResolved.[[Value]] is true, return undefined. 748 GotoIf(IsTrue(already_resolved), &if_already_resolved); 749 750 // 5. Set alreadyResolved.[[Value]] to true. 751 StoreContextElementNoWriteBarrier(context, kAlreadyResolvedSlot, 752 TrueConstant()); 753 754 // 6. Return RejectPromise(promise, reason). 755 Node* const debug_event = LoadContextElement(context, kDebugEventSlot); 756 Return(CallBuiltin(Builtins::kRejectPromise, context, promise, reason, 757 debug_event)); 758 759 BIND(&if_already_resolved); 760 { 761 Return(CallRuntime(Runtime::kPromiseRejectAfterResolved, context, promise, 762 reason)); 763 } 764 } 765 766 // ES #sec-promise-resolve-functions 767 TF_BUILTIN(PromiseCapabilityDefaultResolve, PromiseBuiltinsAssembler) { 768 Node* const resolution = Parameter(Descriptor::kResolution); 769 Node* const context = Parameter(Descriptor::kContext); 770 771 // 2. Let promise be F.[[Promise]]. 772 Node* const promise = LoadContextElement(context, kPromiseSlot); 773 774 // 3. Let alreadyResolved be F.[[AlreadyResolved]]. 775 Label if_already_resolved(this, Label::kDeferred); 776 Node* const already_resolved = 777 LoadContextElement(context, kAlreadyResolvedSlot); 778 779 // 4. If alreadyResolved.[[Value]] is true, return undefined. 780 GotoIf(IsTrue(already_resolved), &if_already_resolved); 781 782 // 5. Set alreadyResolved.[[Value]] to true. 783 StoreContextElementNoWriteBarrier(context, kAlreadyResolvedSlot, 784 TrueConstant()); 785 786 // The rest of the logic (and the catch prediction) is 787 // encapsulated in the dedicated ResolvePromise builtin. 788 Return(CallBuiltin(Builtins::kResolvePromise, context, promise, resolution)); 789 790 BIND(&if_already_resolved); 791 { 792 Return(CallRuntime(Runtime::kPromiseResolveAfterResolved, context, promise, 793 resolution)); 794 } 795 } 796 797 TF_BUILTIN(PromiseConstructorLazyDeoptContinuation, PromiseBuiltinsAssembler) { 798 Node* promise = Parameter(Descriptor::kPromise); 799 Node* reject = Parameter(Descriptor::kReject); 800 Node* exception = Parameter(Descriptor::kException); 801 Node* const context = Parameter(Descriptor::kContext); 802 803 Label finally(this); 804 805 GotoIf(IsTheHole(exception), &finally); 806 CallJS(CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined), 807 context, reject, UndefinedConstant(), exception); 808 Goto(&finally); 809 810 BIND(&finally); 811 Return(promise); 812 } 813 814 // ES6 #sec-promise-executor 815 TF_BUILTIN(PromiseConstructor, PromiseBuiltinsAssembler) { 816 Node* const executor = Parameter(Descriptor::kExecutor); 817 Node* const new_target = Parameter(Descriptor::kJSNewTarget); 818 Node* const context = Parameter(Descriptor::kContext); 819 Isolate* isolate = this->isolate(); 820 821 Label if_targetisundefined(this, Label::kDeferred); 822 823 GotoIf(IsUndefined(new_target), &if_targetisundefined); 824 825 Label if_notcallable(this, Label::kDeferred); 826 827 GotoIf(TaggedIsSmi(executor), &if_notcallable); 828 829 Node* const executor_map = LoadMap(executor); 830 GotoIfNot(IsCallableMap(executor_map), &if_notcallable); 831 832 Node* const native_context = LoadNativeContext(context); 833 Node* const promise_fun = 834 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); 835 Node* const is_debug_active = IsDebugActive(); 836 Label if_targetisnotmodified(this), 837 if_targetismodified(this, Label::kDeferred), run_executor(this), 838 debug_push(this), if_noaccess(this, Label::kDeferred); 839 840 BranchIfAccessCheckFailed(context, native_context, promise_fun, executor, 841 &if_noaccess); 842 843 Branch(WordEqual(promise_fun, new_target), &if_targetisnotmodified, 844 &if_targetismodified); 845 846 VARIABLE(var_result, MachineRepresentation::kTagged); 847 VARIABLE(var_reject_call, MachineRepresentation::kTagged); 848 VARIABLE(var_reason, MachineRepresentation::kTagged); 849 850 BIND(&if_targetisnotmodified); 851 { 852 Node* const instance = AllocateAndInitJSPromise(context); 853 var_result.Bind(instance); 854 Goto(&debug_push); 855 } 856 857 BIND(&if_targetismodified); 858 { 859 ConstructorBuiltinsAssembler constructor_assembler(this->state()); 860 Node* const instance = constructor_assembler.EmitFastNewObject( 861 context, promise_fun, new_target); 862 PromiseInit(instance); 863 var_result.Bind(instance); 864 865 GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &debug_push); 866 CallRuntime(Runtime::kPromiseHookInit, context, instance, 867 UndefinedConstant()); 868 Goto(&debug_push); 869 } 870 871 BIND(&debug_push); 872 { 873 GotoIfNot(is_debug_active, &run_executor); 874 CallRuntime(Runtime::kDebugPushPromise, context, var_result.value()); 875 Goto(&run_executor); 876 } 877 878 BIND(&run_executor); 879 { 880 Label out(this), if_rejectpromise(this), debug_pop(this, Label::kDeferred); 881 882 Node *resolve, *reject; 883 std::tie(resolve, reject) = CreatePromiseResolvingFunctions( 884 var_result.value(), TrueConstant(), native_context); 885 886 Node* const maybe_exception = CallJS( 887 CodeFactory::Call(isolate, ConvertReceiverMode::kNullOrUndefined), 888 context, executor, UndefinedConstant(), resolve, reject); 889 890 GotoIfException(maybe_exception, &if_rejectpromise, &var_reason); 891 Branch(is_debug_active, &debug_pop, &out); 892 893 BIND(&if_rejectpromise); 894 { 895 CallJS(CodeFactory::Call(isolate, ConvertReceiverMode::kNullOrUndefined), 896 context, reject, UndefinedConstant(), var_reason.value()); 897 Branch(is_debug_active, &debug_pop, &out); 898 } 899 900 BIND(&debug_pop); 901 { 902 CallRuntime(Runtime::kDebugPopPromise, context); 903 Goto(&out); 904 } 905 BIND(&out); 906 Return(var_result.value()); 907 } 908 909 // 1. If NewTarget is undefined, throw a TypeError exception. 910 BIND(&if_targetisundefined); 911 ThrowTypeError(context, MessageTemplate::kNotAPromise, new_target); 912 913 // 2. If IsCallable(executor) is false, throw a TypeError exception. 914 BIND(&if_notcallable); 915 ThrowTypeError(context, MessageTemplate::kResolverNotAFunction, executor); 916 917 // Silently fail if the stack looks fishy. 918 BIND(&if_noaccess); 919 { 920 Node* const counter_id = 921 SmiConstant(v8::Isolate::kPromiseConstructorReturnedUndefined); 922 CallRuntime(Runtime::kIncrementUseCounter, context, counter_id); 923 Return(UndefinedConstant()); 924 } 925 } 926 927 // V8 Extras: v8.createPromise(parent) 928 TF_BUILTIN(PromiseInternalConstructor, PromiseBuiltinsAssembler) { 929 Node* const parent = Parameter(Descriptor::kParent); 930 Node* const context = Parameter(Descriptor::kContext); 931 Return(AllocateAndInitJSPromise(context, parent)); 932 } 933 934 // V8 Extras: v8.rejectPromise(promise, reason) 935 TF_BUILTIN(PromiseInternalReject, PromiseBuiltinsAssembler) { 936 Node* const promise = Parameter(Descriptor::kPromise); 937 Node* const reason = Parameter(Descriptor::kReason); 938 Node* const context = Parameter(Descriptor::kContext); 939 // We pass true to trigger the debugger's on exception handler. 940 Return(CallBuiltin(Builtins::kRejectPromise, context, promise, reason, 941 TrueConstant())); 942 } 943 944 // V8 Extras: v8.resolvePromise(promise, resolution) 945 TF_BUILTIN(PromiseInternalResolve, PromiseBuiltinsAssembler) { 946 Node* const promise = Parameter(Descriptor::kPromise); 947 Node* const resolution = Parameter(Descriptor::kResolution); 948 Node* const context = Parameter(Descriptor::kContext); 949 Return(CallBuiltin(Builtins::kResolvePromise, context, promise, resolution)); 950 } 951 952 // ES#sec-promise.prototype.then 953 // Promise.prototype.then ( onFulfilled, onRejected ) 954 TF_BUILTIN(PromisePrototypeThen, PromiseBuiltinsAssembler) { 955 // 1. Let promise be the this value. 956 Node* const promise = Parameter(Descriptor::kReceiver); 957 Node* const on_fulfilled = Parameter(Descriptor::kOnFulfilled); 958 Node* const on_rejected = Parameter(Descriptor::kOnRejected); 959 Node* const context = Parameter(Descriptor::kContext); 960 961 // 2. If IsPromise(promise) is false, throw a TypeError exception. 962 ThrowIfNotInstanceType(context, promise, JS_PROMISE_TYPE, 963 "Promise.prototype.then"); 964 965 // 3. Let C be ? SpeciesConstructor(promise, %Promise%). 966 Label fast_promise_capability(this), slow_constructor(this, Label::kDeferred), 967 slow_promise_capability(this, Label::kDeferred); 968 Node* const native_context = LoadNativeContext(context); 969 Node* const promise_fun = 970 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); 971 Node* const promise_map = LoadMap(promise); 972 BranchIfPromiseSpeciesLookupChainIntact( 973 native_context, promise_map, &fast_promise_capability, &slow_constructor); 974 975 BIND(&slow_constructor); 976 Node* const constructor = 977 SpeciesConstructor(native_context, promise, promise_fun); 978 Branch(WordEqual(constructor, promise_fun), &fast_promise_capability, 979 &slow_promise_capability); 980 981 // 4. Let resultCapability be ? NewPromiseCapability(C). 982 Label perform_promise_then(this); 983 VARIABLE(var_result_promise, MachineRepresentation::kTagged); 984 VARIABLE(var_result_promise_or_capability, MachineRepresentation::kTagged); 985 986 BIND(&fast_promise_capability); 987 { 988 Node* const result_promise = AllocateAndInitJSPromise(context, promise); 989 var_result_promise_or_capability.Bind(result_promise); 990 var_result_promise.Bind(result_promise); 991 Goto(&perform_promise_then); 992 } 993 994 BIND(&slow_promise_capability); 995 { 996 Node* const debug_event = TrueConstant(); 997 Node* const capability = CallBuiltin(Builtins::kNewPromiseCapability, 998 context, constructor, debug_event); 999 var_result_promise.Bind( 1000 LoadObjectField(capability, PromiseCapability::kPromiseOffset)); 1001 var_result_promise_or_capability.Bind(capability); 1002 Goto(&perform_promise_then); 1003 } 1004 1005 // 5. Return PerformPromiseThen(promise, onFulfilled, onRejected, 1006 // resultCapability). 1007 BIND(&perform_promise_then); 1008 { 1009 // We do some work of the PerformPromiseThen operation here, in that 1010 // we check the handlers and turn non-callable handlers into undefined. 1011 // This is because this is the one and only callsite of PerformPromiseThen 1012 // that has to do this. 1013 1014 // 3. If IsCallable(onFulfilled) is false, then 1015 // a. Set onFulfilled to undefined. 1016 VARIABLE(var_on_fulfilled, MachineRepresentation::kTagged, on_fulfilled); 1017 Label if_fulfilled_done(this), if_fulfilled_notcallable(this); 1018 GotoIf(TaggedIsSmi(on_fulfilled), &if_fulfilled_notcallable); 1019 Branch(IsCallable(on_fulfilled), &if_fulfilled_done, 1020 &if_fulfilled_notcallable); 1021 BIND(&if_fulfilled_notcallable); 1022 var_on_fulfilled.Bind(UndefinedConstant()); 1023 Goto(&if_fulfilled_done); 1024 BIND(&if_fulfilled_done); 1025 1026 // 4. If IsCallable(onRejected) is false, then 1027 // a. Set onRejected to undefined. 1028 VARIABLE(var_on_rejected, MachineRepresentation::kTagged, on_rejected); 1029 Label if_rejected_done(this), if_rejected_notcallable(this); 1030 GotoIf(TaggedIsSmi(on_rejected), &if_rejected_notcallable); 1031 Branch(IsCallable(on_rejected), &if_rejected_done, 1032 &if_rejected_notcallable); 1033 BIND(&if_rejected_notcallable); 1034 var_on_rejected.Bind(UndefinedConstant()); 1035 Goto(&if_rejected_done); 1036 BIND(&if_rejected_done); 1037 1038 PerformPromiseThen(context, promise, var_on_fulfilled.value(), 1039 var_on_rejected.value(), 1040 var_result_promise_or_capability.value()); 1041 Return(var_result_promise.value()); 1042 } 1043 } 1044 1045 // ES#sec-promise.prototype.catch 1046 // Promise.prototype.catch ( onRejected ) 1047 TF_BUILTIN(PromisePrototypeCatch, PromiseBuiltinsAssembler) { 1048 // 1. Let promise be the this value. 1049 Node* const receiver = Parameter(Descriptor::kReceiver); 1050 Node* const on_fulfilled = UndefinedConstant(); 1051 Node* const on_rejected = Parameter(Descriptor::kOnRejected); 1052 Node* const context = Parameter(Descriptor::kContext); 1053 1054 // 2. Return ? Invoke(promise, "then", undefined, onRejected ). 1055 Node* const native_context = LoadNativeContext(context); 1056 Return(InvokeThen(native_context, receiver, on_fulfilled, on_rejected)); 1057 } 1058 1059 // ES #sec-promiseresolvethenablejob 1060 TF_BUILTIN(PromiseResolveThenableJob, PromiseBuiltinsAssembler) { 1061 Node* const native_context = Parameter(Descriptor::kContext); 1062 Node* const promise_to_resolve = Parameter(Descriptor::kPromiseToResolve); 1063 Node* const thenable = Parameter(Descriptor::kThenable); 1064 Node* const then = Parameter(Descriptor::kThen); 1065 1066 CSA_ASSERT(this, TaggedIsNotSmi(thenable)); 1067 CSA_ASSERT(this, IsJSReceiver(thenable)); 1068 CSA_ASSERT(this, IsJSPromise(promise_to_resolve)); 1069 CSA_ASSERT(this, IsNativeContext(native_context)); 1070 1071 // We can use a simple optimization here if we know that {then} is the initial 1072 // Promise.prototype.then method, and {thenable} is a JSPromise whose 1073 // @@species lookup chain is intact: We can connect {thenable} and 1074 // {promise_to_resolve} directly in that case and avoid the allocation of a 1075 // temporary JSPromise and the closures plus context. 1076 // 1077 // We take the generic (slow-)path if a PromiseHook is enabled or the debugger 1078 // is active, to make sure we expose spec compliant behavior. 1079 Label if_fast(this), if_slow(this, Label::kDeferred); 1080 Node* const promise_then = 1081 LoadContextElement(native_context, Context::PROMISE_THEN_INDEX); 1082 GotoIfNot(WordEqual(then, promise_then), &if_slow); 1083 Node* const thenable_map = LoadMap(thenable); 1084 GotoIfNot(IsJSPromiseMap(thenable_map), &if_slow); 1085 GotoIf(IsPromiseHookEnabled(), &if_slow); 1086 GotoIf(IsDebugActive(), &if_slow); 1087 BranchIfPromiseSpeciesLookupChainIntact(native_context, thenable_map, 1088 &if_fast, &if_slow); 1089 1090 BIND(&if_fast); 1091 { 1092 // We know that the {thenable} is a JSPromise, which doesn't require 1093 // any special treatment and that {then} corresponds to the initial 1094 // Promise.prototype.then method. So instead of allocating a temporary 1095 // JSPromise to connect the {thenable} with the {promise_to_resolve}, 1096 // we can directly schedule the {promise_to_resolve} with default 1097 // handlers onto the {thenable} promise. This does not only save the 1098 // JSPromise allocation, but also avoids the allocation of the two 1099 // resolving closures and the shared context. 1100 // 1101 // What happens normally in this case is 1102 // 1103 // resolve, reject = CreateResolvingFunctions(promise_to_resolve) 1104 // result_capability = NewPromiseCapability(%Promise%) 1105 // PerformPromiseThen(thenable, resolve, reject, result_capability) 1106 // 1107 // which means that PerformPromiseThen will either schedule a new 1108 // PromiseReaction with resolve and reject or a PromiseReactionJob 1109 // with resolve or reject based on the state of {thenable}. And 1110 // resolve or reject will just invoke the default [[Resolve]] or 1111 // [[Reject]] functions on the {promise_to_resolve}. 1112 // 1113 // This is the same as just doing 1114 // 1115 // PerformPromiseThen(thenable, undefined, undefined, promise_to_resolve) 1116 // 1117 // which performs exactly the same (observable) steps. 1118 TailCallBuiltin(Builtins::kPerformPromiseThen, native_context, thenable, 1119 UndefinedConstant(), UndefinedConstant(), 1120 promise_to_resolve); 1121 } 1122 1123 BIND(&if_slow); 1124 { 1125 Node* resolve = nullptr; 1126 Node* reject = nullptr; 1127 std::tie(resolve, reject) = CreatePromiseResolvingFunctions( 1128 promise_to_resolve, FalseConstant(), native_context); 1129 1130 Label if_exception(this, Label::kDeferred); 1131 VARIABLE(var_exception, MachineRepresentation::kTagged, TheHoleConstant()); 1132 Node* const result = CallJS( 1133 CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined), 1134 native_context, then, thenable, resolve, reject); 1135 GotoIfException(result, &if_exception, &var_exception); 1136 Return(result); 1137 1138 BIND(&if_exception); 1139 { 1140 // We need to reject the {thenable}. 1141 Node* const result = CallJS( 1142 CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined), 1143 native_context, reject, UndefinedConstant(), var_exception.value()); 1144 Return(result); 1145 } 1146 } 1147 } 1148 1149 // ES #sec-promisereactionjob 1150 void PromiseBuiltinsAssembler::PromiseReactionJob(Node* context, Node* argument, 1151 Node* handler, 1152 Node* promise_or_capability, 1153 PromiseReaction::Type type) { 1154 CSA_ASSERT(this, TaggedIsNotSmi(handler)); 1155 CSA_ASSERT(this, Word32Or(IsUndefined(handler), IsCallable(handler))); 1156 CSA_ASSERT(this, TaggedIsNotSmi(promise_or_capability)); 1157 CSA_ASSERT(this, Word32Or(IsJSPromise(promise_or_capability), 1158 IsPromiseCapability(promise_or_capability))); 1159 1160 VARIABLE(var_handler_result, MachineRepresentation::kTagged, argument); 1161 Label if_handler_callable(this), if_fulfill(this), if_reject(this); 1162 Branch(IsUndefined(handler), 1163 type == PromiseReaction::kFulfill ? &if_fulfill : &if_reject, 1164 &if_handler_callable); 1165 1166 BIND(&if_handler_callable); 1167 { 1168 Node* const result = CallJS( 1169 CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined), 1170 context, handler, UndefinedConstant(), argument); 1171 GotoIfException(result, &if_reject, &var_handler_result); 1172 var_handler_result.Bind(result); 1173 Goto(&if_fulfill); 1174 } 1175 1176 BIND(&if_fulfill); 1177 { 1178 Label if_promise(this), if_promise_capability(this, Label::kDeferred); 1179 Node* const value = var_handler_result.value(); 1180 Branch(IsPromiseCapability(promise_or_capability), &if_promise_capability, 1181 &if_promise); 1182 1183 BIND(&if_promise); 1184 { 1185 // For fast native promises we can skip the indirection 1186 // via the promiseCapability.[[Resolve]] function and 1187 // run the resolve logic directly from here. 1188 TailCallBuiltin(Builtins::kResolvePromise, context, promise_or_capability, 1189 value); 1190 } 1191 1192 BIND(&if_promise_capability); 1193 { 1194 // In the general case we need to call the (user provided) 1195 // promiseCapability.[[Resolve]] function. 1196 Node* const resolve = LoadObjectField(promise_or_capability, 1197 PromiseCapability::kResolveOffset); 1198 Node* const result = CallJS( 1199 CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined), 1200 context, resolve, UndefinedConstant(), value); 1201 GotoIfException(result, &if_reject, &var_handler_result); 1202 Return(result); 1203 } 1204 } 1205 1206 BIND(&if_reject); 1207 if (type == PromiseReaction::kReject) { 1208 Label if_promise(this), if_promise_capability(this, Label::kDeferred); 1209 Node* const reason = var_handler_result.value(); 1210 Branch(IsPromiseCapability(promise_or_capability), &if_promise_capability, 1211 &if_promise); 1212 1213 BIND(&if_promise); 1214 { 1215 // For fast native promises we can skip the indirection 1216 // via the promiseCapability.[[Reject]] function and 1217 // run the resolve logic directly from here. 1218 TailCallBuiltin(Builtins::kRejectPromise, context, promise_or_capability, 1219 reason, FalseConstant()); 1220 } 1221 1222 BIND(&if_promise_capability); 1223 { 1224 // In the general case we need to call the (user provided) 1225 // promiseCapability.[[Reject]] function. 1226 Label if_exception(this, Label::kDeferred); 1227 VARIABLE(var_exception, MachineRepresentation::kTagged, 1228 TheHoleConstant()); 1229 Node* const reject = LoadObjectField(promise_or_capability, 1230 PromiseCapability::kRejectOffset); 1231 Node* const result = CallJS( 1232 CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined), 1233 context, reject, UndefinedConstant(), reason); 1234 GotoIfException(result, &if_exception, &var_exception); 1235 Return(result); 1236 1237 // Swallow the exception here. 1238 BIND(&if_exception); 1239 TailCallRuntime(Runtime::kReportMessage, context, var_exception.value()); 1240 } 1241 } else { 1242 // We have to call out to the dedicated PromiseRejectReactionJob builtin 1243 // here, instead of just doing the work inline, as otherwise the catch 1244 // predictions in the debugger will be wrong, which just walks the stack 1245 // and checks for certain builtins. 1246 TailCallBuiltin(Builtins::kPromiseRejectReactionJob, context, 1247 var_handler_result.value(), UndefinedConstant(), 1248 promise_or_capability); 1249 } 1250 } 1251 1252 // ES #sec-promisereactionjob 1253 TF_BUILTIN(PromiseFulfillReactionJob, PromiseBuiltinsAssembler) { 1254 Node* const context = Parameter(Descriptor::kContext); 1255 Node* const value = Parameter(Descriptor::kValue); 1256 Node* const handler = Parameter(Descriptor::kHandler); 1257 Node* const promise_or_capability = 1258 Parameter(Descriptor::kPromiseOrCapability); 1259 1260 PromiseReactionJob(context, value, handler, promise_or_capability, 1261 PromiseReaction::kFulfill); 1262 } 1263 1264 // ES #sec-promisereactionjob 1265 TF_BUILTIN(PromiseRejectReactionJob, PromiseBuiltinsAssembler) { 1266 Node* const context = Parameter(Descriptor::kContext); 1267 Node* const reason = Parameter(Descriptor::kReason); 1268 Node* const handler = Parameter(Descriptor::kHandler); 1269 Node* const promise_or_capability = 1270 Parameter(Descriptor::kPromiseOrCapability); 1271 1272 PromiseReactionJob(context, reason, handler, promise_or_capability, 1273 PromiseReaction::kReject); 1274 } 1275 1276 TF_BUILTIN(PromiseResolveTrampoline, PromiseBuiltinsAssembler) { 1277 // 1. Let C be the this value. 1278 Node* receiver = Parameter(Descriptor::kReceiver); 1279 Node* value = Parameter(Descriptor::kValue); 1280 Node* context = Parameter(Descriptor::kContext); 1281 1282 // 2. If Type(C) is not Object, throw a TypeError exception. 1283 ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject, 1284 "PromiseResolve"); 1285 1286 // 3. Return ? PromiseResolve(C, x). 1287 Return(CallBuiltin(Builtins::kPromiseResolve, context, receiver, value)); 1288 } 1289 1290 TF_BUILTIN(PromiseResolve, PromiseBuiltinsAssembler) { 1291 Node* constructor = Parameter(Descriptor::kConstructor); 1292 Node* value = Parameter(Descriptor::kValue); 1293 Node* context = Parameter(Descriptor::kContext); 1294 1295 CSA_ASSERT(this, IsJSReceiver(constructor)); 1296 1297 Node* const native_context = LoadNativeContext(context); 1298 Node* const promise_fun = 1299 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); 1300 1301 Label if_slow_constructor(this, Label::kDeferred), if_need_to_allocate(this); 1302 1303 // Check if {value} is a JSPromise. 1304 GotoIf(TaggedIsSmi(value), &if_need_to_allocate); 1305 Node* const value_map = LoadMap(value); 1306 GotoIfNot(IsJSPromiseMap(value_map), &if_need_to_allocate); 1307 1308 // We can skip the "constructor" lookup on {value} if it's [[Prototype]] 1309 // is the (initial) Promise.prototype and the @@species protector is 1310 // intact, as that guards the lookup path for "constructor" on 1311 // JSPromise instances which have the (initial) Promise.prototype. 1312 Node* const promise_prototype = 1313 LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX); 1314 GotoIfNot(WordEqual(LoadMapPrototype(value_map), promise_prototype), 1315 &if_slow_constructor); 1316 GotoIf(IsPromiseSpeciesProtectorCellInvalid(), &if_slow_constructor); 1317 1318 // If the {constructor} is the Promise function, we just immediately 1319 // return the {value} here and don't bother wrapping it into a 1320 // native Promise. 1321 GotoIfNot(WordEqual(promise_fun, constructor), &if_slow_constructor); 1322 Return(value); 1323 1324 // At this point, value or/and constructor are not native promises, but 1325 // they could be of the same subclass. 1326 BIND(&if_slow_constructor); 1327 { 1328 Node* const value_constructor = 1329 GetProperty(context, value, isolate()->factory()->constructor_string()); 1330 GotoIfNot(WordEqual(value_constructor, constructor), &if_need_to_allocate); 1331 Return(value); 1332 } 1333 1334 BIND(&if_need_to_allocate); 1335 { 1336 Label if_nativepromise(this), if_notnativepromise(this, Label::kDeferred); 1337 Branch(WordEqual(promise_fun, constructor), &if_nativepromise, 1338 &if_notnativepromise); 1339 1340 // This adds a fast path for native promises that don't need to 1341 // create NewPromiseCapability. 1342 BIND(&if_nativepromise); 1343 { 1344 Node* const result = AllocateAndInitJSPromise(context); 1345 CallBuiltin(Builtins::kResolvePromise, context, result, value); 1346 Return(result); 1347 } 1348 1349 BIND(&if_notnativepromise); 1350 { 1351 Node* const debug_event = TrueConstant(); 1352 Node* const capability = CallBuiltin(Builtins::kNewPromiseCapability, 1353 context, constructor, debug_event); 1354 1355 Node* const resolve = 1356 LoadObjectField(capability, PromiseCapability::kResolveOffset); 1357 CallJS( 1358 CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined), 1359 context, resolve, UndefinedConstant(), value); 1360 1361 Node* const result = 1362 LoadObjectField(capability, PromiseCapability::kPromiseOffset); 1363 Return(result); 1364 } 1365 } 1366 } 1367 1368 // ES6 #sec-getcapabilitiesexecutor-functions 1369 TF_BUILTIN(PromiseGetCapabilitiesExecutor, PromiseBuiltinsAssembler) { 1370 Node* const resolve = Parameter(Descriptor::kResolve); 1371 Node* const reject = Parameter(Descriptor::kReject); 1372 Node* const context = Parameter(Descriptor::kContext); 1373 1374 Node* const capability = LoadContextElement(context, kCapabilitySlot); 1375 1376 Label if_alreadyinvoked(this, Label::kDeferred); 1377 GotoIfNot(IsUndefined( 1378 LoadObjectField(capability, PromiseCapability::kResolveOffset)), 1379 &if_alreadyinvoked); 1380 GotoIfNot(IsUndefined( 1381 LoadObjectField(capability, PromiseCapability::kRejectOffset)), 1382 &if_alreadyinvoked); 1383 1384 StoreObjectField(capability, PromiseCapability::kResolveOffset, resolve); 1385 StoreObjectField(capability, PromiseCapability::kRejectOffset, reject); 1386 1387 Return(UndefinedConstant()); 1388 1389 BIND(&if_alreadyinvoked); 1390 ThrowTypeError(context, MessageTemplate::kPromiseExecutorAlreadyInvoked); 1391 } 1392 1393 TF_BUILTIN(PromiseReject, PromiseBuiltinsAssembler) { 1394 // 1. Let C be the this value. 1395 Node* const receiver = Parameter(Descriptor::kReceiver); 1396 Node* const reason = Parameter(Descriptor::kReason); 1397 Node* const context = Parameter(Descriptor::kContext); 1398 1399 // 2. If Type(C) is not Object, throw a TypeError exception. 1400 ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject, 1401 "PromiseReject"); 1402 1403 Label if_nativepromise(this), if_custompromise(this, Label::kDeferred); 1404 Node* const native_context = LoadNativeContext(context); 1405 1406 Node* const promise_fun = 1407 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); 1408 Branch(WordEqual(promise_fun, receiver), &if_nativepromise, 1409 &if_custompromise); 1410 1411 BIND(&if_nativepromise); 1412 { 1413 Node* const promise = 1414 AllocateAndSetJSPromise(context, v8::Promise::kRejected, reason); 1415 CallRuntime(Runtime::kPromiseRejectEventFromStack, context, promise, 1416 reason); 1417 Return(promise); 1418 } 1419 1420 BIND(&if_custompromise); 1421 { 1422 // 3. Let promiseCapability be ? NewPromiseCapability(C). 1423 Node* const debug_event = TrueConstant(); 1424 Node* const capability = CallBuiltin(Builtins::kNewPromiseCapability, 1425 context, receiver, debug_event); 1426 1427 // 4. Perform ? Call(promiseCapability.[[Reject]], undefined, r ). 1428 Node* const reject = 1429 LoadObjectField(capability, PromiseCapability::kRejectOffset); 1430 CallJS(CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined), 1431 context, reject, UndefinedConstant(), reason); 1432 1433 // 5. Return promiseCapability.[[Promise]]. 1434 Node* const promise = 1435 LoadObjectField(capability, PromiseCapability::kPromiseOffset); 1436 Return(promise); 1437 } 1438 } 1439 1440 std::pair<Node*, Node*> PromiseBuiltinsAssembler::CreatePromiseFinallyFunctions( 1441 Node* on_finally, Node* constructor, Node* native_context) { 1442 Node* const promise_context = 1443 CreatePromiseContext(native_context, kPromiseFinallyContextLength); 1444 StoreContextElementNoWriteBarrier(promise_context, kOnFinallySlot, 1445 on_finally); 1446 StoreContextElementNoWriteBarrier(promise_context, kConstructorSlot, 1447 constructor); 1448 Node* const map = LoadContextElement( 1449 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); 1450 Node* const then_finally_info = LoadContextElement( 1451 native_context, Context::PROMISE_THEN_FINALLY_SHARED_FUN); 1452 Node* const then_finally = AllocateFunctionWithMapAndContext( 1453 map, then_finally_info, promise_context); 1454 Node* const catch_finally_info = LoadContextElement( 1455 native_context, Context::PROMISE_CATCH_FINALLY_SHARED_FUN); 1456 Node* const catch_finally = AllocateFunctionWithMapAndContext( 1457 map, catch_finally_info, promise_context); 1458 return std::make_pair(then_finally, catch_finally); 1459 } 1460 1461 TF_BUILTIN(PromiseValueThunkFinally, PromiseBuiltinsAssembler) { 1462 Node* const context = Parameter(Descriptor::kContext); 1463 1464 Node* const value = LoadContextElement(context, kValueSlot); 1465 Return(value); 1466 } 1467 1468 Node* PromiseBuiltinsAssembler::CreateValueThunkFunction(Node* value, 1469 Node* native_context) { 1470 Node* const value_thunk_context = CreatePromiseContext( 1471 native_context, kPromiseValueThunkOrReasonContextLength); 1472 StoreContextElementNoWriteBarrier(value_thunk_context, kValueSlot, value); 1473 Node* const map = LoadContextElement( 1474 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); 1475 Node* const value_thunk_info = LoadContextElement( 1476 native_context, Context::PROMISE_VALUE_THUNK_FINALLY_SHARED_FUN); 1477 Node* const value_thunk = AllocateFunctionWithMapAndContext( 1478 map, value_thunk_info, value_thunk_context); 1479 return value_thunk; 1480 } 1481 1482 TF_BUILTIN(PromiseThenFinally, PromiseBuiltinsAssembler) { 1483 CSA_ASSERT_JS_ARGC_EQ(this, 1); 1484 1485 Node* const value = Parameter(Descriptor::kValue); 1486 Node* const context = Parameter(Descriptor::kContext); 1487 1488 // 1. Let onFinally be F.[[OnFinally]]. 1489 Node* const on_finally = LoadContextElement(context, kOnFinallySlot); 1490 1491 // 2. Assert: IsCallable(onFinally) is true. 1492 CSA_ASSERT(this, IsCallable(on_finally)); 1493 1494 // 3. Let result be ? Call(onFinally). 1495 Node* const result = CallJS( 1496 CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined), 1497 context, on_finally, UndefinedConstant()); 1498 1499 // 4. Let C be F.[[Constructor]]. 1500 Node* const constructor = LoadContextElement(context, kConstructorSlot); 1501 1502 // 5. Assert: IsConstructor(C) is true. 1503 CSA_ASSERT(this, IsConstructor(constructor)); 1504 1505 // 6. Let promise be ? PromiseResolve(C, result). 1506 Node* const promise = 1507 CallBuiltin(Builtins::kPromiseResolve, context, constructor, result); 1508 1509 // 7. Let valueThunk be equivalent to a function that returns value. 1510 Node* const native_context = LoadNativeContext(context); 1511 Node* const value_thunk = CreateValueThunkFunction(value, native_context); 1512 1513 // 8. Return ? Invoke(promise, "then", valueThunk ). 1514 Return(InvokeThen(native_context, promise, value_thunk)); 1515 } 1516 1517 TF_BUILTIN(PromiseThrowerFinally, PromiseBuiltinsAssembler) { 1518 Node* const context = Parameter(Descriptor::kContext); 1519 1520 Node* const reason = LoadContextElement(context, kValueSlot); 1521 CallRuntime(Runtime::kThrow, context, reason); 1522 Unreachable(); 1523 } 1524 1525 Node* PromiseBuiltinsAssembler::CreateThrowerFunction(Node* reason, 1526 Node* native_context) { 1527 Node* const thrower_context = CreatePromiseContext( 1528 native_context, kPromiseValueThunkOrReasonContextLength); 1529 StoreContextElementNoWriteBarrier(thrower_context, kValueSlot, reason); 1530 Node* const map = LoadContextElement( 1531 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); 1532 Node* const thrower_info = LoadContextElement( 1533 native_context, Context::PROMISE_THROWER_FINALLY_SHARED_FUN); 1534 Node* const thrower = 1535 AllocateFunctionWithMapAndContext(map, thrower_info, thrower_context); 1536 return thrower; 1537 } 1538 1539 TF_BUILTIN(PromiseCatchFinally, PromiseBuiltinsAssembler) { 1540 CSA_ASSERT_JS_ARGC_EQ(this, 1); 1541 1542 Node* const reason = Parameter(Descriptor::kReason); 1543 Node* const context = Parameter(Descriptor::kContext); 1544 1545 // 1. Let onFinally be F.[[OnFinally]]. 1546 Node* const on_finally = LoadContextElement(context, kOnFinallySlot); 1547 1548 // 2. Assert: IsCallable(onFinally) is true. 1549 CSA_ASSERT(this, IsCallable(on_finally)); 1550 1551 // 3. Let result be ? Call(onFinally). 1552 Node* result = CallJS( 1553 CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined), 1554 context, on_finally, UndefinedConstant()); 1555 1556 // 4. Let C be F.[[Constructor]]. 1557 Node* const constructor = LoadContextElement(context, kConstructorSlot); 1558 1559 // 5. Assert: IsConstructor(C) is true. 1560 CSA_ASSERT(this, IsConstructor(constructor)); 1561 1562 // 6. Let promise be ? PromiseResolve(C, result). 1563 Node* const promise = 1564 CallBuiltin(Builtins::kPromiseResolve, context, constructor, result); 1565 1566 // 7. Let thrower be equivalent to a function that throws reason. 1567 Node* const native_context = LoadNativeContext(context); 1568 Node* const thrower = CreateThrowerFunction(reason, native_context); 1569 1570 // 8. Return ? Invoke(promise, "then", thrower ). 1571 Return(InvokeThen(native_context, promise, thrower)); 1572 } 1573 1574 TF_BUILTIN(PromisePrototypeFinally, PromiseBuiltinsAssembler) { 1575 CSA_ASSERT_JS_ARGC_EQ(this, 1); 1576 1577 // 1. Let promise be the this value. 1578 Node* const receiver = Parameter(Descriptor::kReceiver); 1579 Node* const on_finally = Parameter(Descriptor::kOnFinally); 1580 Node* const context = Parameter(Descriptor::kContext); 1581 1582 // 2. If Type(promise) is not Object, throw a TypeError exception. 1583 ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject, 1584 "Promise.prototype.finally"); 1585 1586 // 3. Let C be ? SpeciesConstructor(promise, %Promise%). 1587 Node* const native_context = LoadNativeContext(context); 1588 Node* const promise_fun = 1589 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); 1590 VARIABLE(var_constructor, MachineRepresentation::kTagged, promise_fun); 1591 Label slow_constructor(this, Label::kDeferred), done_constructor(this); 1592 Node* const receiver_map = LoadMap(receiver); 1593 GotoIfNot(IsJSPromiseMap(receiver_map), &slow_constructor); 1594 BranchIfPromiseSpeciesLookupChainIntact(native_context, receiver_map, 1595 &done_constructor, &slow_constructor); 1596 BIND(&slow_constructor); 1597 { 1598 Node* const constructor = 1599 SpeciesConstructor(context, receiver, promise_fun); 1600 var_constructor.Bind(constructor); 1601 Goto(&done_constructor); 1602 } 1603 BIND(&done_constructor); 1604 Node* const constructor = var_constructor.value(); 1605 1606 // 4. Assert: IsConstructor(C) is true. 1607 CSA_ASSERT(this, IsConstructor(constructor)); 1608 1609 VARIABLE(var_then_finally, MachineRepresentation::kTagged); 1610 VARIABLE(var_catch_finally, MachineRepresentation::kTagged); 1611 1612 Label if_notcallable(this, Label::kDeferred), perform_finally(this); 1613 1614 GotoIf(TaggedIsSmi(on_finally), &if_notcallable); 1615 GotoIfNot(IsCallable(on_finally), &if_notcallable); 1616 1617 // 6. Else, 1618 // a. Let thenFinally be a new built-in function object as defined 1619 // in ThenFinally Function. 1620 // b. Let catchFinally be a new built-in function object as 1621 // defined in CatchFinally Function. 1622 // c. Set thenFinally and catchFinally's [[Constructor]] internal 1623 // slots to C. 1624 // d. Set thenFinally and catchFinally's [[OnFinally]] internal 1625 // slots to onFinally. 1626 Node* then_finally = nullptr; 1627 Node* catch_finally = nullptr; 1628 std::tie(then_finally, catch_finally) = 1629 CreatePromiseFinallyFunctions(on_finally, constructor, native_context); 1630 var_then_finally.Bind(then_finally); 1631 var_catch_finally.Bind(catch_finally); 1632 Goto(&perform_finally); 1633 1634 // 5. If IsCallable(onFinally) is not true, 1635 // a. Let thenFinally be onFinally. 1636 // b. Let catchFinally be onFinally. 1637 BIND(&if_notcallable); 1638 { 1639 var_then_finally.Bind(on_finally); 1640 var_catch_finally.Bind(on_finally); 1641 Goto(&perform_finally); 1642 } 1643 1644 // 7. Return ? Invoke(promise, "then", thenFinally, catchFinally ). 1645 BIND(&perform_finally); 1646 Return(InvokeThen(native_context, receiver, var_then_finally.value(), 1647 var_catch_finally.value())); 1648 } 1649 1650 // ES #sec-fulfillpromise 1651 TF_BUILTIN(FulfillPromise, PromiseBuiltinsAssembler) { 1652 Node* const promise = Parameter(Descriptor::kPromise); 1653 Node* const value = Parameter(Descriptor::kValue); 1654 Node* const context = Parameter(Descriptor::kContext); 1655 1656 CSA_ASSERT(this, TaggedIsNotSmi(promise)); 1657 CSA_ASSERT(this, IsJSPromise(promise)); 1658 1659 // 2. Let reactions be promise.[[PromiseFulfillReactions]]. 1660 Node* const reactions = 1661 LoadObjectField(promise, JSPromise::kReactionsOrResultOffset); 1662 1663 // 3. Set promise.[[PromiseResult]] to value. 1664 // 4. Set promise.[[PromiseFulfillReactions]] to undefined. 1665 // 5. Set promise.[[PromiseRejectReactions]] to undefined. 1666 StoreObjectField(promise, JSPromise::kReactionsOrResultOffset, value); 1667 1668 // 6. Set promise.[[PromiseState]] to "fulfilled". 1669 PromiseSetStatus(promise, Promise::kFulfilled); 1670 1671 // 7. Return TriggerPromiseReactions(reactions, value). 1672 Return(TriggerPromiseReactions(context, reactions, value, 1673 PromiseReaction::kFulfill)); 1674 } 1675 1676 // ES #sec-rejectpromise 1677 TF_BUILTIN(RejectPromise, PromiseBuiltinsAssembler) { 1678 Node* const promise = Parameter(Descriptor::kPromise); 1679 Node* const reason = Parameter(Descriptor::kReason); 1680 Node* const debug_event = Parameter(Descriptor::kDebugEvent); 1681 Node* const context = Parameter(Descriptor::kContext); 1682 1683 CSA_ASSERT(this, TaggedIsNotSmi(promise)); 1684 CSA_ASSERT(this, IsJSPromise(promise)); 1685 CSA_ASSERT(this, IsBoolean(debug_event)); 1686 Label if_runtime(this, Label::kDeferred); 1687 1688 // If promise hook is enabled or the debugger is active, let 1689 // the runtime handle this operation, which greatly reduces 1690 // the complexity here and also avoids a couple of back and 1691 // forth between JavaScript and C++ land. 1692 GotoIf(IsPromiseHookEnabled(), &if_runtime); 1693 GotoIf(IsDebugActive(), &if_runtime); 1694 1695 // 7. If promise.[[PromiseIsHandled]] is false, perform 1696 // HostPromiseRejectionTracker(promise, "reject"). 1697 // We don't try to handle rejecting {promise} without handler 1698 // here, but we let the C++ code take care of this completely. 1699 GotoIfNot(PromiseHasHandler(promise), &if_runtime); 1700 1701 // 2. Let reactions be promise.[[PromiseRejectReactions]]. 1702 Node* reactions = 1703 LoadObjectField(promise, JSPromise::kReactionsOrResultOffset); 1704 1705 // 3. Set promise.[[PromiseResult]] to reason. 1706 // 4. Set promise.[[PromiseFulfillReactions]] to undefined. 1707 // 5. Set promise.[[PromiseRejectReactions]] to undefined. 1708 StoreObjectField(promise, JSPromise::kReactionsOrResultOffset, reason); 1709 1710 // 6. Set promise.[[PromiseState]] to "rejected". 1711 PromiseSetStatus(promise, Promise::kRejected); 1712 1713 // 7. Return TriggerPromiseReactions(reactions, reason). 1714 Return(TriggerPromiseReactions(context, reactions, reason, 1715 PromiseReaction::kReject)); 1716 1717 BIND(&if_runtime); 1718 TailCallRuntime(Runtime::kRejectPromise, context, promise, reason, 1719 debug_event); 1720 } 1721 1722 // ES #sec-promise-resolve-functions 1723 TF_BUILTIN(ResolvePromise, PromiseBuiltinsAssembler) { 1724 Node* const promise = Parameter(Descriptor::kPromise); 1725 Node* const resolution = Parameter(Descriptor::kResolution); 1726 Node* const context = Parameter(Descriptor::kContext); 1727 1728 CSA_ASSERT(this, TaggedIsNotSmi(promise)); 1729 CSA_ASSERT(this, IsJSPromise(promise)); 1730 1731 Label do_enqueue(this), if_fulfill(this), if_reject(this, Label::kDeferred), 1732 if_runtime(this, Label::kDeferred); 1733 VARIABLE(var_reason, MachineRepresentation::kTagged); 1734 VARIABLE(var_then, MachineRepresentation::kTagged); 1735 1736 // If promise hook is enabled or the debugger is active, let 1737 // the runtime handle this operation, which greatly reduces 1738 // the complexity here and also avoids a couple of back and 1739 // forth between JavaScript and C++ land. 1740 GotoIf(IsPromiseHookEnabled(), &if_runtime); 1741 GotoIf(IsDebugActive(), &if_runtime); 1742 1743 // 6. If SameValue(resolution, promise) is true, then 1744 // We can use pointer comparison here, since the {promise} is guaranteed 1745 // to be a JSPromise inside this function and thus is reference comparable. 1746 GotoIf(WordEqual(promise, resolution), &if_runtime); 1747 1748 // 7. If Type(resolution) is not Object, then 1749 GotoIf(TaggedIsSmi(resolution), &if_fulfill); 1750 Node* const resolution_map = LoadMap(resolution); 1751 GotoIfNot(IsJSReceiverMap(resolution_map), &if_fulfill); 1752 1753 // We can skip the "then" lookup on {resolution} if its [[Prototype]] 1754 // is the (initial) Promise.prototype and the Promise#then protector 1755 // is intact, as that guards the lookup path for the "then" property 1756 // on JSPromise instances which have the (initial) %PromisePrototype%. 1757 Label if_fast(this), if_receiver(this), if_slow(this, Label::kDeferred); 1758 Node* const native_context = LoadNativeContext(context); 1759 GotoIfForceSlowPath(&if_slow); 1760 GotoIf(IsPromiseThenProtectorCellInvalid(), &if_slow); 1761 GotoIfNot(IsJSPromiseMap(resolution_map), &if_receiver); 1762 Node* const promise_prototype = 1763 LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX); 1764 Branch(WordEqual(LoadMapPrototype(resolution_map), promise_prototype), 1765 &if_fast, &if_slow); 1766 1767 BIND(&if_fast); 1768 { 1769 // The {resolution} is a native Promise in this case. 1770 Node* const then = 1771 LoadContextElement(native_context, Context::PROMISE_THEN_INDEX); 1772 var_then.Bind(then); 1773 Goto(&do_enqueue); 1774 } 1775 1776 BIND(&if_receiver); 1777 { 1778 // We can skip the lookup of "then" if the {resolution} is a (newly 1779 // created) IterResultObject, as the Promise#then() protector also 1780 // ensures that the intrinsic %ObjectPrototype% doesn't contain any 1781 // "then" property. This helps to avoid negative lookups on iterator 1782 // results from async generators. 1783 CSA_ASSERT(this, IsJSReceiverMap(resolution_map)); 1784 CSA_ASSERT(this, Word32BinaryNot(IsPromiseThenProtectorCellInvalid())); 1785 Node* const iterator_result_map = 1786 LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX); 1787 Branch(WordEqual(resolution_map, iterator_result_map), &if_fulfill, 1788 &if_slow); 1789 } 1790 1791 BIND(&if_slow); 1792 { 1793 // 8. Let then be Get(resolution, "then"). 1794 Node* const then = 1795 GetProperty(context, resolution, isolate()->factory()->then_string()); 1796 1797 // 9. If then is an abrupt completion, then 1798 GotoIfException(then, &if_reject, &var_reason); 1799 1800 // 11. If IsCallable(thenAction) is false, then 1801 GotoIf(TaggedIsSmi(then), &if_fulfill); 1802 Node* const then_map = LoadMap(then); 1803 GotoIfNot(IsCallableMap(then_map), &if_fulfill); 1804 var_then.Bind(then); 1805 Goto(&do_enqueue); 1806 } 1807 1808 BIND(&do_enqueue); 1809 { 1810 // 12. Perform EnqueueJob("PromiseJobs", PromiseResolveThenableJob, 1811 // promise, resolution, thenAction). 1812 Node* const task = AllocatePromiseResolveThenableJobTask( 1813 promise, var_then.value(), resolution, native_context); 1814 TailCallBuiltin(Builtins::kEnqueueMicrotask, native_context, task); 1815 } 1816 1817 BIND(&if_fulfill); 1818 { 1819 // 7.b Return FulfillPromise(promise, resolution). 1820 TailCallBuiltin(Builtins::kFulfillPromise, context, promise, resolution); 1821 } 1822 1823 BIND(&if_runtime); 1824 Return(CallRuntime(Runtime::kResolvePromise, context, promise, resolution)); 1825 1826 BIND(&if_reject); 1827 { 1828 // 9.a Return RejectPromise(promise, then.[[Value]]). 1829 TailCallBuiltin(Builtins::kRejectPromise, context, promise, 1830 var_reason.value(), FalseConstant()); 1831 } 1832 } 1833 1834 Node* PromiseBuiltinsAssembler::PerformPromiseAll( 1835 Node* context, Node* constructor, Node* capability, 1836 const IteratorRecord& iterator, Label* if_exception, 1837 Variable* var_exception) { 1838 IteratorBuiltinsAssembler iter_assembler(state()); 1839 1840 Node* const instrumenting = IsDebugActive(); 1841 Node* const native_context = LoadNativeContext(context); 1842 1843 // For catch prediction, don't treat the .then calls as handling it; 1844 // instead, recurse outwards. 1845 SetForwardingHandlerIfTrue( 1846 native_context, instrumenting, 1847 LoadObjectField(capability, PromiseCapability::kRejectOffset)); 1848 1849 Node* const resolve_element_context = 1850 CreatePromiseAllResolveElementContext(capability, native_context); 1851 1852 TVARIABLE(Smi, var_index, SmiConstant(1)); 1853 Label loop(this, &var_index), done_loop(this), 1854 too_many_elements(this, Label::kDeferred), 1855 close_iterator(this, Label::kDeferred); 1856 Goto(&loop); 1857 BIND(&loop); 1858 { 1859 // Let next be IteratorStep(iteratorRecord.[[Iterator]]). 1860 // If next is an abrupt completion, set iteratorRecord.[[Done]] to true. 1861 // ReturnIfAbrupt(next). 1862 Node* const fast_iterator_result_map = 1863 LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX); 1864 Node* const next = iter_assembler.IteratorStep( 1865 native_context, iterator, &done_loop, fast_iterator_result_map, 1866 if_exception, var_exception); 1867 1868 // Let nextValue be IteratorValue(next). 1869 // If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to 1870 // true. 1871 // ReturnIfAbrupt(nextValue). 1872 Node* const next_value = iter_assembler.IteratorValue( 1873 native_context, next, fast_iterator_result_map, if_exception, 1874 var_exception); 1875 1876 // Let nextPromise be ? Invoke(constructor, "resolve", nextValue ). 1877 Node* const next_promise = 1878 InvokeResolve(native_context, constructor, next_value, &close_iterator, 1879 var_exception); 1880 1881 // Check if we reached the limit. 1882 TNode<Smi> const index = var_index.value(); 1883 GotoIf(SmiEqual(index, SmiConstant(PropertyArray::HashField::kMax)), 1884 &too_many_elements); 1885 1886 // Set remainingElementsCount.[[Value]] to 1887 // remainingElementsCount.[[Value]] + 1. 1888 TNode<Smi> const remaining_elements_count = CAST(LoadContextElement( 1889 resolve_element_context, kPromiseAllResolveElementRemainingSlot)); 1890 StoreContextElementNoWriteBarrier( 1891 resolve_element_context, kPromiseAllResolveElementRemainingSlot, 1892 SmiAdd(remaining_elements_count, SmiConstant(1))); 1893 1894 // Let resolveElement be CreateBuiltinFunction(steps, 1895 // [[AlreadyCalled]], 1896 // [[Index]], 1897 // [[Values]], 1898 // [[Capability]], 1899 // [[RemainingElements]] ). 1900 // Set resolveElement.[[AlreadyCalled]] to a Record { [[Value]]: false }. 1901 // Set resolveElement.[[Index]] to index. 1902 // Set resolveElement.[[Values]] to values. 1903 // Set resolveElement.[[Capability]] to resultCapability. 1904 // Set resolveElement.[[RemainingElements]] to remainingElementsCount. 1905 Node* const resolve_element_fun = CreatePromiseAllResolveElementFunction( 1906 resolve_element_context, index, native_context); 1907 1908 // Perform ? Invoke(nextPromise, "then", resolveElement, 1909 // resultCapability.[[Reject]] ). 1910 Node* const then = 1911 GetProperty(native_context, next_promise, factory()->then_string()); 1912 GotoIfException(then, &close_iterator, var_exception); 1913 1914 Node* const then_call = CallJS( 1915 CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined), 1916 native_context, then, next_promise, resolve_element_fun, 1917 LoadObjectField(capability, PromiseCapability::kRejectOffset)); 1918 GotoIfException(then_call, &close_iterator, var_exception); 1919 1920 // For catch prediction, mark that rejections here are semantically 1921 // handled by the combined Promise. 1922 SetPromiseHandledByIfTrue(native_context, instrumenting, then_call, [=]() { 1923 // Load promiseCapability.[[Promise]] 1924 return LoadObjectField(capability, PromiseCapability::kPromiseOffset); 1925 }); 1926 1927 // Set index to index + 1. 1928 var_index = SmiAdd(index, SmiConstant(1)); 1929 Goto(&loop); 1930 } 1931 1932 BIND(&too_many_elements); 1933 { 1934 // If there are too many elements (currently more than 2**21-1), raise a 1935 // RangeError here (which is caught directly and turned into a rejection) 1936 // of the resulting promise. We could gracefully handle this case as well 1937 // and support more than this number of elements by going to a separate 1938 // function and pass the larger indices via a separate context, but it 1939 // doesn't seem likely that we need this, and it's unclear how the rest 1940 // of the system deals with 2**21 live Promises anyways. 1941 Node* const result = 1942 CallRuntime(Runtime::kThrowRangeError, native_context, 1943 SmiConstant(MessageTemplate::kTooManyElementsInPromiseAll)); 1944 GotoIfException(result, &close_iterator, var_exception); 1945 Unreachable(); 1946 } 1947 1948 BIND(&close_iterator); 1949 { 1950 // Exception must be bound to a JS value. 1951 CSA_ASSERT(this, IsNotTheHole(var_exception->value())); 1952 iter_assembler.IteratorCloseOnException(native_context, iterator, 1953 if_exception, var_exception); 1954 } 1955 1956 BIND(&done_loop); 1957 { 1958 Label resolve_promise(this, Label::kDeferred), return_promise(this); 1959 // Set iteratorRecord.[[Done]] to true. 1960 // Set remainingElementsCount.[[Value]] to 1961 // remainingElementsCount.[[Value]] - 1. 1962 TNode<Smi> remaining_elements_count = CAST(LoadContextElement( 1963 resolve_element_context, kPromiseAllResolveElementRemainingSlot)); 1964 remaining_elements_count = SmiSub(remaining_elements_count, SmiConstant(1)); 1965 StoreContextElementNoWriteBarrier(resolve_element_context, 1966 kPromiseAllResolveElementRemainingSlot, 1967 remaining_elements_count); 1968 GotoIf(SmiEqual(remaining_elements_count, SmiConstant(0)), 1969 &resolve_promise); 1970 1971 // Pre-allocate the backing store for the {values_array} to the desired 1972 // capacity here. We may already have elements here in case of some 1973 // fancy Thenable that calls the resolve callback immediately, so we need 1974 // to handle that correctly here. 1975 Node* const values_array = LoadContextElement( 1976 resolve_element_context, kPromiseAllResolveElementValuesArraySlot); 1977 Node* const old_elements = LoadElements(values_array); 1978 TNode<Smi> const old_capacity = LoadFixedArrayBaseLength(old_elements); 1979 TNode<Smi> const new_capacity = var_index.value(); 1980 GotoIf(SmiGreaterThanOrEqual(old_capacity, new_capacity), &return_promise); 1981 Node* const new_elements = 1982 AllocateFixedArray(PACKED_ELEMENTS, new_capacity, SMI_PARAMETERS, 1983 AllocationFlag::kAllowLargeObjectAllocation); 1984 CopyFixedArrayElements(PACKED_ELEMENTS, old_elements, PACKED_ELEMENTS, 1985 new_elements, SmiConstant(0), old_capacity, 1986 new_capacity, UPDATE_WRITE_BARRIER, SMI_PARAMETERS); 1987 StoreObjectField(values_array, JSArray::kElementsOffset, new_elements); 1988 Goto(&return_promise); 1989 1990 // If remainingElementsCount.[[Value]] is 0, then 1991 // Let valuesArray be CreateArrayFromList(values). 1992 // Perform ? Call(resultCapability.[[Resolve]], undefined, 1993 // valuesArray ). 1994 BIND(&resolve_promise); 1995 { 1996 Node* const resolve = 1997 LoadObjectField(capability, PromiseCapability::kResolveOffset); 1998 Node* const values_array = LoadContextElement( 1999 resolve_element_context, kPromiseAllResolveElementValuesArraySlot); 2000 Node* const resolve_call = CallJS( 2001 CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined), 2002 native_context, resolve, UndefinedConstant(), values_array); 2003 GotoIfException(resolve_call, if_exception, var_exception); 2004 Goto(&return_promise); 2005 } 2006 2007 // Return resultCapability.[[Promise]]. 2008 BIND(&return_promise); 2009 } 2010 2011 Node* const promise = 2012 LoadObjectField(capability, PromiseCapability::kPromiseOffset); 2013 return promise; 2014 } 2015 2016 // ES#sec-promise.all 2017 // Promise.all ( iterable ) 2018 TF_BUILTIN(PromiseAll, PromiseBuiltinsAssembler) { 2019 IteratorBuiltinsAssembler iter_assembler(state()); 2020 2021 // Let C be the this value. 2022 // If Type(C) is not Object, throw a TypeError exception. 2023 Node* const receiver = Parameter(Descriptor::kReceiver); 2024 Node* const context = Parameter(Descriptor::kContext); 2025 ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject, 2026 "Promise.all"); 2027 2028 // Let promiseCapability be ? NewPromiseCapability(C). 2029 // Don't fire debugEvent so that forwarding the rejection through all does not 2030 // trigger redundant ExceptionEvents 2031 Node* const debug_event = FalseConstant(); 2032 Node* const capability = CallBuiltin(Builtins::kNewPromiseCapability, context, 2033 receiver, debug_event); 2034 2035 VARIABLE(var_exception, MachineRepresentation::kTagged, TheHoleConstant()); 2036 Label reject_promise(this, &var_exception, Label::kDeferred); 2037 2038 // Let iterator be GetIterator(iterable). 2039 // IfAbruptRejectPromise(iterator, promiseCapability). 2040 Node* const iterable = Parameter(Descriptor::kIterable); 2041 IteratorRecord iterator = iter_assembler.GetIterator( 2042 context, iterable, &reject_promise, &var_exception); 2043 2044 // Let result be PerformPromiseAll(iteratorRecord, C, promiseCapability). 2045 // If result is an abrupt completion, then 2046 // If iteratorRecord.[[Done]] is false, let result be 2047 // IteratorClose(iterator, result). 2048 // IfAbruptRejectPromise(result, promiseCapability). 2049 Node* const result = PerformPromiseAll( 2050 context, receiver, capability, iterator, &reject_promise, &var_exception); 2051 2052 Return(result); 2053 2054 BIND(&reject_promise); 2055 { 2056 // Exception must be bound to a JS value. 2057 CSA_SLOW_ASSERT(this, IsNotTheHole(var_exception.value())); 2058 Node* const reject = 2059 LoadObjectField(capability, PromiseCapability::kRejectOffset); 2060 CallJS(CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined), 2061 context, reject, UndefinedConstant(), var_exception.value()); 2062 2063 Node* const promise = 2064 LoadObjectField(capability, PromiseCapability::kPromiseOffset); 2065 Return(promise); 2066 } 2067 } 2068 2069 TF_BUILTIN(PromiseAllResolveElementClosure, PromiseBuiltinsAssembler) { 2070 TNode<Object> value = CAST(Parameter(Descriptor::kValue)); 2071 TNode<Context> context = CAST(Parameter(Descriptor::kContext)); 2072 TNode<JSFunction> function = CAST(Parameter(Descriptor::kJSTarget)); 2073 2074 Label already_called(this, Label::kDeferred), resolve_promise(this); 2075 2076 // We use the {function}s context as the marker to remember whether this 2077 // resolve element closure was already called. It points to the resolve 2078 // element context (which is a FunctionContext) until it was called the 2079 // first time, in which case we make it point to the native context here 2080 // to mark this resolve element closure as done. 2081 GotoIf(IsNativeContext(context), &already_called); 2082 CSA_ASSERT(this, SmiEqual(LoadFixedArrayBaseLength(context), 2083 SmiConstant(kPromiseAllResolveElementLength))); 2084 TNode<Context> native_context = LoadNativeContext(context); 2085 StoreObjectField(function, JSFunction::kContextOffset, native_context); 2086 2087 // Determine the index from the {function}. 2088 Label unreachable(this, Label::kDeferred); 2089 STATIC_ASSERT(PropertyArray::kNoHashSentinel == 0); 2090 TNode<IntPtrT> identity_hash = 2091 LoadJSReceiverIdentityHash(function, &unreachable); 2092 CSA_ASSERT(this, IntPtrGreaterThan(identity_hash, IntPtrConstant(0))); 2093 TNode<IntPtrT> index = IntPtrSub(identity_hash, IntPtrConstant(1)); 2094 2095 // Check if we need to grow the [[ValuesArray]] to store {value} at {index}. 2096 TNode<JSArray> values_array = CAST( 2097 LoadContextElement(context, kPromiseAllResolveElementValuesArraySlot)); 2098 TNode<FixedArray> elements = CAST(LoadElements(values_array)); 2099 TNode<IntPtrT> values_length = 2100 LoadAndUntagObjectField(values_array, JSArray::kLengthOffset); 2101 Label if_inbounds(this), if_outofbounds(this), done(this); 2102 Branch(IntPtrLessThan(index, values_length), &if_inbounds, &if_outofbounds); 2103 2104 BIND(&if_outofbounds); 2105 { 2106 // Check if we need to grow the backing store. 2107 TNode<IntPtrT> new_length = IntPtrAdd(index, IntPtrConstant(1)); 2108 TNode<IntPtrT> elements_length = 2109 LoadAndUntagObjectField(elements, FixedArray::kLengthOffset); 2110 Label if_grow(this, Label::kDeferred), if_nogrow(this); 2111 Branch(IntPtrLessThan(index, elements_length), &if_nogrow, &if_grow); 2112 2113 BIND(&if_grow); 2114 { 2115 // We need to grow the backing store to fit the {index} as well. 2116 TNode<IntPtrT> new_elements_length = 2117 IntPtrMin(CalculateNewElementsCapacity(new_length), 2118 IntPtrConstant(PropertyArray::HashField::kMax + 1)); 2119 CSA_ASSERT(this, IntPtrLessThan(index, new_elements_length)); 2120 CSA_ASSERT(this, IntPtrLessThan(elements_length, new_elements_length)); 2121 TNode<FixedArray> new_elements = 2122 CAST(AllocateFixedArray(PACKED_ELEMENTS, new_elements_length, 2123 AllocationFlag::kAllowLargeObjectAllocation)); 2124 CopyFixedArrayElements(PACKED_ELEMENTS, elements, PACKED_ELEMENTS, 2125 new_elements, elements_length, 2126 new_elements_length); 2127 StoreFixedArrayElement(new_elements, index, value); 2128 2129 // Update backing store and "length" on {values_array}. 2130 StoreObjectField(values_array, JSArray::kElementsOffset, new_elements); 2131 StoreObjectFieldNoWriteBarrier(values_array, JSArray::kLengthOffset, 2132 SmiTag(new_length)); 2133 Goto(&done); 2134 } 2135 2136 BIND(&if_nogrow); 2137 { 2138 // The {index} is within bounds of the {elements} backing store, so 2139 // just store the {value} and update the "length" of the {values_array}. 2140 StoreObjectFieldNoWriteBarrier(values_array, JSArray::kLengthOffset, 2141 SmiTag(new_length)); 2142 StoreFixedArrayElement(elements, index, value); 2143 Goto(&done); 2144 } 2145 } 2146 2147 BIND(&if_inbounds); 2148 { 2149 // The {index} is in bounds of the {values_array}, 2150 // just store the {value} and continue. 2151 StoreFixedArrayElement(elements, index, value); 2152 Goto(&done); 2153 } 2154 2155 BIND(&done); 2156 TNode<Smi> remaining_elements_count = 2157 CAST(LoadContextElement(context, kPromiseAllResolveElementRemainingSlot)); 2158 remaining_elements_count = SmiSub(remaining_elements_count, SmiConstant(1)); 2159 StoreContextElement(context, kPromiseAllResolveElementRemainingSlot, 2160 remaining_elements_count); 2161 GotoIf(SmiEqual(remaining_elements_count, SmiConstant(0)), &resolve_promise); 2162 Return(UndefinedConstant()); 2163 2164 BIND(&resolve_promise); 2165 TNode<PromiseCapability> capability = CAST( 2166 LoadContextElement(context, kPromiseAllResolveElementCapabilitySlot)); 2167 TNode<Object> resolve = 2168 LoadObjectField(capability, PromiseCapability::kResolveOffset); 2169 CallJS(CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined), 2170 context, resolve, UndefinedConstant(), values_array); 2171 Return(UndefinedConstant()); 2172 2173 BIND(&already_called); 2174 Return(UndefinedConstant()); 2175 2176 BIND(&unreachable); 2177 Unreachable(); 2178 } 2179 2180 // ES#sec-promise.race 2181 // Promise.race ( iterable ) 2182 TF_BUILTIN(PromiseRace, PromiseBuiltinsAssembler) { 2183 IteratorBuiltinsAssembler iter_assembler(state()); 2184 VARIABLE(var_exception, MachineRepresentation::kTagged, TheHoleConstant()); 2185 2186 Node* const receiver = Parameter(Descriptor::kReceiver); 2187 Node* const context = Parameter(Descriptor::kContext); 2188 ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject, 2189 "Promise.race"); 2190 2191 // Let promiseCapability be ? NewPromiseCapability(C). 2192 // Don't fire debugEvent so that forwarding the rejection through all does not 2193 // trigger redundant ExceptionEvents 2194 Node* const debug_event = FalseConstant(); 2195 Node* const capability = CallBuiltin(Builtins::kNewPromiseCapability, context, 2196 receiver, debug_event); 2197 2198 Node* const resolve = 2199 LoadObjectField(capability, PromiseCapability::kResolveOffset); 2200 Node* const reject = 2201 LoadObjectField(capability, PromiseCapability::kRejectOffset); 2202 2203 Node* const instrumenting = IsDebugActive(); 2204 2205 Label close_iterator(this, Label::kDeferred); 2206 Label reject_promise(this, Label::kDeferred); 2207 2208 // For catch prediction, don't treat the .then calls as handling it; 2209 // instead, recurse outwards. 2210 SetForwardingHandlerIfTrue(context, instrumenting, reject); 2211 2212 // Let iterator be GetIterator(iterable). 2213 // IfAbruptRejectPromise(iterator, promiseCapability). 2214 Node* const iterable = Parameter(Descriptor::kIterable); 2215 IteratorRecord iterator = iter_assembler.GetIterator( 2216 context, iterable, &reject_promise, &var_exception); 2217 2218 // Let result be PerformPromiseRace(iteratorRecord, C, promiseCapability). 2219 { 2220 Label loop(this), break_loop(this); 2221 Goto(&loop); 2222 BIND(&loop); 2223 { 2224 Node* const native_context = LoadNativeContext(context); 2225 Node* const fast_iterator_result_map = LoadContextElement( 2226 native_context, Context::ITERATOR_RESULT_MAP_INDEX); 2227 2228 // Let next be IteratorStep(iteratorRecord.[[Iterator]]). 2229 // If next is an abrupt completion, set iteratorRecord.[[Done]] to true. 2230 // ReturnIfAbrupt(next). 2231 Node* const next = iter_assembler.IteratorStep( 2232 context, iterator, &break_loop, fast_iterator_result_map, 2233 &reject_promise, &var_exception); 2234 2235 // Let nextValue be IteratorValue(next). 2236 // If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to 2237 // true. 2238 // ReturnIfAbrupt(nextValue). 2239 Node* const next_value = 2240 iter_assembler.IteratorValue(context, next, fast_iterator_result_map, 2241 &reject_promise, &var_exception); 2242 2243 // Let nextPromise be ? Invoke(constructor, "resolve", nextValue ). 2244 Node* const next_promise = 2245 InvokeResolve(native_context, receiver, next_value, &close_iterator, 2246 &var_exception); 2247 2248 // Perform ? Invoke(nextPromise, "then", resolveElement, 2249 // resultCapability.[[Reject]] ). 2250 Node* const then = 2251 GetProperty(context, next_promise, factory()->then_string()); 2252 GotoIfException(then, &close_iterator, &var_exception); 2253 2254 Node* const then_call = 2255 CallJS(CodeFactory::Call(isolate(), 2256 ConvertReceiverMode::kNotNullOrUndefined), 2257 context, then, next_promise, resolve, reject); 2258 GotoIfException(then_call, &close_iterator, &var_exception); 2259 2260 // For catch prediction, mark that rejections here are semantically 2261 // handled by the combined Promise. 2262 SetPromiseHandledByIfTrue(context, instrumenting, then_call, [=]() { 2263 // Load promiseCapability.[[Promise]] 2264 return LoadObjectField(capability, PromiseCapability::kPromiseOffset); 2265 }); 2266 Goto(&loop); 2267 } 2268 2269 BIND(&break_loop); 2270 Return(LoadObjectField(capability, PromiseCapability::kPromiseOffset)); 2271 } 2272 2273 BIND(&close_iterator); 2274 { 2275 CSA_ASSERT(this, IsNotTheHole(var_exception.value())); 2276 iter_assembler.IteratorCloseOnException(context, iterator, &reject_promise, 2277 &var_exception); 2278 } 2279 2280 BIND(&reject_promise); 2281 { 2282 Node* const reject = 2283 LoadObjectField(capability, PromiseCapability::kRejectOffset); 2284 CallJS(CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined), 2285 context, reject, UndefinedConstant(), var_exception.value()); 2286 2287 Node* const promise = 2288 LoadObjectField(capability, PromiseCapability::kPromiseOffset); 2289 Return(promise); 2290 } 2291 } 2292 2293 } // namespace internal 2294 } // namespace v8 2295