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.h" 6 #include "src/builtins/builtins-constructor.h" 7 #include "src/builtins/builtins-utils.h" 8 #include "src/builtins/builtins.h" 9 #include "src/code-factory.h" 10 #include "src/code-stub-assembler.h" 11 #include "src/objects-inl.h" 12 13 namespace v8 { 14 namespace internal { 15 16 typedef compiler::Node Node; 17 typedef CodeStubAssembler::ParameterMode ParameterMode; 18 typedef compiler::CodeAssemblerState CodeAssemblerState; 19 20 Node* PromiseBuiltinsAssembler::AllocateJSPromise(Node* context) { 21 Node* const native_context = LoadNativeContext(context); 22 Node* const promise_fun = 23 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); 24 Node* const initial_map = 25 LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset); 26 Node* const instance = AllocateJSObjectFromMap(initial_map); 27 return instance; 28 } 29 30 void PromiseBuiltinsAssembler::PromiseInit(Node* promise) { 31 StoreObjectField(promise, JSPromise::kStatusOffset, 32 SmiConstant(v8::Promise::kPending)); 33 StoreObjectField(promise, JSPromise::kFlagsOffset, SmiConstant(0)); 34 } 35 36 Node* PromiseBuiltinsAssembler::AllocateAndInitJSPromise(Node* context) { 37 return AllocateAndInitJSPromise(context, UndefinedConstant()); 38 } 39 40 Node* PromiseBuiltinsAssembler::AllocateAndInitJSPromise(Node* context, 41 Node* parent) { 42 Node* const instance = AllocateJSPromise(context); 43 PromiseInit(instance); 44 45 Label out(this); 46 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &out); 47 CallRuntime(Runtime::kPromiseHookInit, context, instance, parent); 48 Goto(&out); 49 50 Bind(&out); 51 return instance; 52 } 53 54 Node* PromiseBuiltinsAssembler::AllocateAndSetJSPromise(Node* context, 55 Node* status, 56 Node* result) { 57 CSA_ASSERT(this, TaggedIsSmi(status)); 58 59 Node* const instance = AllocateJSPromise(context); 60 61 StoreObjectFieldNoWriteBarrier(instance, JSPromise::kStatusOffset, status); 62 StoreObjectFieldNoWriteBarrier(instance, JSPromise::kResultOffset, result); 63 StoreObjectFieldNoWriteBarrier(instance, JSPromise::kFlagsOffset, 64 SmiConstant(0)); 65 66 Label out(this); 67 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &out); 68 CallRuntime(Runtime::kPromiseHookInit, context, instance, 69 UndefinedConstant()); 70 Goto(&out); 71 72 Bind(&out); 73 return instance; 74 } 75 76 std::pair<Node*, Node*> 77 PromiseBuiltinsAssembler::CreatePromiseResolvingFunctions( 78 Node* promise, Node* debug_event, Node* native_context) { 79 Node* const promise_context = CreatePromiseResolvingFunctionsContext( 80 promise, debug_event, native_context); 81 Node* const map = LoadContextElement( 82 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); 83 Node* const resolve_info = 84 LoadContextElement(native_context, Context::PROMISE_RESOLVE_SHARED_FUN); 85 Node* const resolve = 86 AllocateFunctionWithMapAndContext(map, resolve_info, promise_context); 87 Node* const reject_info = 88 LoadContextElement(native_context, Context::PROMISE_REJECT_SHARED_FUN); 89 Node* const reject = 90 AllocateFunctionWithMapAndContext(map, reject_info, promise_context); 91 return std::make_pair(resolve, reject); 92 } 93 94 Node* PromiseBuiltinsAssembler::NewPromiseCapability(Node* context, 95 Node* constructor, 96 Node* debug_event) { 97 if (debug_event == nullptr) { 98 debug_event = TrueConstant(); 99 } 100 101 Node* native_context = LoadNativeContext(context); 102 103 Node* map = LoadRoot(Heap::kJSPromiseCapabilityMapRootIndex); 104 Node* capability = AllocateJSObjectFromMap(map); 105 106 StoreObjectFieldNoWriteBarrier( 107 capability, JSPromiseCapability::kPromiseOffset, UndefinedConstant()); 108 StoreObjectFieldNoWriteBarrier( 109 capability, JSPromiseCapability::kResolveOffset, UndefinedConstant()); 110 StoreObjectFieldNoWriteBarrier(capability, JSPromiseCapability::kRejectOffset, 111 UndefinedConstant()); 112 113 Variable var_result(this, MachineRepresentation::kTagged); 114 var_result.Bind(capability); 115 116 Label if_builtin_promise(this), if_custom_promise(this, Label::kDeferred), 117 out(this); 118 Branch(WordEqual(constructor, 119 LoadContextElement(native_context, 120 Context::PROMISE_FUNCTION_INDEX)), 121 &if_builtin_promise, &if_custom_promise); 122 123 Bind(&if_builtin_promise); 124 { 125 Node* promise = AllocateJSPromise(context); 126 PromiseInit(promise); 127 StoreObjectFieldNoWriteBarrier( 128 capability, JSPromiseCapability::kPromiseOffset, promise); 129 130 Node* resolve = nullptr; 131 Node* reject = nullptr; 132 133 std::tie(resolve, reject) = 134 CreatePromiseResolvingFunctions(promise, debug_event, native_context); 135 StoreObjectField(capability, JSPromiseCapability::kResolveOffset, resolve); 136 StoreObjectField(capability, JSPromiseCapability::kRejectOffset, reject); 137 138 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &out); 139 CallRuntime(Runtime::kPromiseHookInit, context, promise, 140 UndefinedConstant()); 141 Goto(&out); 142 } 143 144 Bind(&if_custom_promise); 145 { 146 Label if_notcallable(this, Label::kDeferred); 147 Node* executor_context = 148 CreatePromiseGetCapabilitiesExecutorContext(capability, native_context); 149 Node* executor_info = LoadContextElement( 150 native_context, Context::PROMISE_GET_CAPABILITIES_EXECUTOR_SHARED_FUN); 151 Node* function_map = LoadContextElement( 152 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); 153 Node* executor = AllocateFunctionWithMapAndContext( 154 function_map, executor_info, executor_context); 155 156 Node* promise = ConstructJS(CodeFactory::Construct(isolate()), context, 157 constructor, executor); 158 159 Node* resolve = 160 LoadObjectField(capability, JSPromiseCapability::kResolveOffset); 161 GotoIf(TaggedIsSmi(resolve), &if_notcallable); 162 GotoIfNot(IsCallableMap(LoadMap(resolve)), &if_notcallable); 163 164 Node* reject = 165 LoadObjectField(capability, JSPromiseCapability::kRejectOffset); 166 GotoIf(TaggedIsSmi(reject), &if_notcallable); 167 GotoIfNot(IsCallableMap(LoadMap(reject)), &if_notcallable); 168 169 StoreObjectField(capability, JSPromiseCapability::kPromiseOffset, promise); 170 171 Goto(&out); 172 173 Bind(&if_notcallable); 174 Node* message = SmiConstant(MessageTemplate::kPromiseNonCallable); 175 StoreObjectField(capability, JSPromiseCapability::kPromiseOffset, 176 UndefinedConstant()); 177 StoreObjectField(capability, JSPromiseCapability::kResolveOffset, 178 UndefinedConstant()); 179 StoreObjectField(capability, JSPromiseCapability::kRejectOffset, 180 UndefinedConstant()); 181 CallRuntime(Runtime::kThrowTypeError, context, message); 182 Unreachable(); 183 } 184 185 Bind(&out); 186 return var_result.value(); 187 } 188 189 Node* PromiseBuiltinsAssembler::CreatePromiseContext(Node* native_context, 190 int slots) { 191 DCHECK_GE(slots, Context::MIN_CONTEXT_SLOTS); 192 193 Node* const context = Allocate(FixedArray::SizeFor(slots)); 194 StoreMapNoWriteBarrier(context, Heap::kFunctionContextMapRootIndex); 195 StoreObjectFieldNoWriteBarrier(context, FixedArray::kLengthOffset, 196 SmiConstant(slots)); 197 198 Node* const empty_fn = 199 LoadContextElement(native_context, Context::CLOSURE_INDEX); 200 StoreContextElementNoWriteBarrier(context, Context::CLOSURE_INDEX, empty_fn); 201 StoreContextElementNoWriteBarrier(context, Context::PREVIOUS_INDEX, 202 UndefinedConstant()); 203 StoreContextElementNoWriteBarrier(context, Context::EXTENSION_INDEX, 204 TheHoleConstant()); 205 StoreContextElementNoWriteBarrier(context, Context::NATIVE_CONTEXT_INDEX, 206 native_context); 207 return context; 208 } 209 210 Node* PromiseBuiltinsAssembler::CreatePromiseResolvingFunctionsContext( 211 Node* promise, Node* debug_event, Node* native_context) { 212 Node* const context = 213 CreatePromiseContext(native_context, kPromiseContextLength); 214 StoreContextElementNoWriteBarrier(context, kAlreadyVisitedSlot, 215 SmiConstant(0)); 216 StoreContextElementNoWriteBarrier(context, kPromiseSlot, promise); 217 StoreContextElementNoWriteBarrier(context, kDebugEventSlot, debug_event); 218 return context; 219 } 220 221 Node* PromiseBuiltinsAssembler::CreatePromiseGetCapabilitiesExecutorContext( 222 Node* promise_capability, Node* native_context) { 223 int kContextLength = kCapabilitiesContextLength; 224 Node* context = CreatePromiseContext(native_context, kContextLength); 225 StoreContextElementNoWriteBarrier(context, kCapabilitySlot, 226 promise_capability); 227 return context; 228 } 229 230 Node* PromiseBuiltinsAssembler::ThrowIfNotJSReceiver( 231 Node* context, Node* value, MessageTemplate::Template msg_template, 232 const char* method_name) { 233 Label out(this), throw_exception(this, Label::kDeferred); 234 Variable var_value_map(this, MachineRepresentation::kTagged); 235 236 GotoIf(TaggedIsSmi(value), &throw_exception); 237 238 // Load the instance type of the {value}. 239 var_value_map.Bind(LoadMap(value)); 240 Node* const value_instance_type = LoadMapInstanceType(var_value_map.value()); 241 242 Branch(IsJSReceiverInstanceType(value_instance_type), &out, &throw_exception); 243 244 // The {value} is not a compatible receiver for this method. 245 Bind(&throw_exception); 246 { 247 Node* const method = 248 method_name == nullptr 249 ? UndefinedConstant() 250 : HeapConstant( 251 isolate()->factory()->NewStringFromAsciiChecked(method_name)); 252 Node* const message_id = SmiConstant(msg_template); 253 CallRuntime(Runtime::kThrowTypeError, context, message_id, method); 254 Unreachable(); 255 } 256 257 Bind(&out); 258 return var_value_map.value(); 259 } 260 261 Node* PromiseBuiltinsAssembler::PromiseHasHandler(Node* promise) { 262 Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset); 263 return IsSetWord(SmiUntag(flags), 1 << JSPromise::kHasHandlerBit); 264 } 265 266 void PromiseBuiltinsAssembler::PromiseSetHasHandler(Node* promise) { 267 Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset); 268 Node* const new_flags = 269 SmiOr(flags, SmiConstant(1 << JSPromise::kHasHandlerBit)); 270 StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset, new_flags); 271 } 272 273 void PromiseBuiltinsAssembler::PromiseSetHandledHint(Node* promise) { 274 Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset); 275 Node* const new_flags = 276 SmiOr(flags, SmiConstant(1 << JSPromise::kHandledHintBit)); 277 StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset, new_flags); 278 } 279 280 Node* PromiseBuiltinsAssembler::SpeciesConstructor(Node* context, Node* object, 281 Node* default_constructor) { 282 Isolate* isolate = this->isolate(); 283 Variable var_result(this, MachineRepresentation::kTagged); 284 var_result.Bind(default_constructor); 285 286 // 2. Let C be ? Get(O, "constructor"). 287 Node* const constructor_str = 288 HeapConstant(isolate->factory()->constructor_string()); 289 Callable getproperty_callable = CodeFactory::GetProperty(isolate); 290 Node* const constructor = 291 CallStub(getproperty_callable, context, object, constructor_str); 292 293 // 3. If C is undefined, return defaultConstructor. 294 Label out(this); 295 GotoIf(IsUndefined(constructor), &out); 296 297 // 4. If Type(C) is not Object, throw a TypeError exception. 298 ThrowIfNotJSReceiver(context, constructor, 299 MessageTemplate::kConstructorNotReceiver); 300 301 // 5. Let S be ? Get(C, @@species). 302 Node* const species_symbol = 303 HeapConstant(isolate->factory()->species_symbol()); 304 Node* const species = 305 CallStub(getproperty_callable, context, constructor, species_symbol); 306 307 // 6. If S is either undefined or null, return defaultConstructor. 308 GotoIf(IsUndefined(species), &out); 309 GotoIf(WordEqual(species, NullConstant()), &out); 310 311 // 7. If IsConstructor(S) is true, return S. 312 Label throw_error(this); 313 Node* species_bitfield = LoadMapBitField(LoadMap(species)); 314 GotoIfNot(Word32Equal(Word32And(species_bitfield, 315 Int32Constant((1 << Map::kIsConstructor))), 316 Int32Constant(1 << Map::kIsConstructor)), 317 &throw_error); 318 var_result.Bind(species); 319 Goto(&out); 320 321 // 8. Throw a TypeError exception. 322 Bind(&throw_error); 323 { 324 Node* const message_id = 325 SmiConstant(MessageTemplate::kSpeciesNotConstructor); 326 CallRuntime(Runtime::kThrowTypeError, context, message_id); 327 Unreachable(); 328 } 329 330 Bind(&out); 331 return var_result.value(); 332 } 333 334 void PromiseBuiltinsAssembler::AppendPromiseCallback(int offset, Node* promise, 335 Node* value) { 336 Node* elements = LoadObjectField(promise, offset); 337 Node* length = LoadFixedArrayBaseLength(elements); 338 CodeStubAssembler::ParameterMode mode = OptimalParameterMode(); 339 length = TaggedToParameter(length, mode); 340 341 Node* delta = IntPtrOrSmiConstant(1, mode); 342 Node* new_capacity = IntPtrOrSmiAdd(length, delta, mode); 343 344 const ElementsKind kind = FAST_ELEMENTS; 345 const WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER; 346 const CodeStubAssembler::AllocationFlags flags = 347 CodeStubAssembler::kAllowLargeObjectAllocation; 348 int additional_offset = 0; 349 350 Node* new_elements = AllocateFixedArray(kind, new_capacity, mode, flags); 351 352 CopyFixedArrayElements(kind, elements, new_elements, length, barrier_mode, 353 mode); 354 StoreFixedArrayElement(new_elements, length, value, barrier_mode, 355 additional_offset, mode); 356 357 StoreObjectField(promise, offset, new_elements); 358 } 359 360 Node* PromiseBuiltinsAssembler::InternalPromiseThen(Node* context, 361 Node* promise, 362 Node* on_resolve, 363 Node* on_reject) { 364 Isolate* isolate = this->isolate(); 365 366 // 2. If IsPromise(promise) is false, throw a TypeError exception. 367 ThrowIfNotInstanceType(context, promise, JS_PROMISE_TYPE, 368 "Promise.prototype.then"); 369 370 Node* const native_context = LoadNativeContext(context); 371 Node* const promise_fun = 372 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); 373 374 // 3. Let C be ? SpeciesConstructor(promise, %Promise%). 375 Node* constructor = SpeciesConstructor(context, promise, promise_fun); 376 377 // 4. Let resultCapability be ? NewPromiseCapability(C). 378 Callable call_callable = CodeFactory::Call(isolate); 379 Label fast_promise_capability(this), promise_capability(this), 380 perform_promise_then(this); 381 Variable var_deferred_promise(this, MachineRepresentation::kTagged), 382 var_deferred_on_resolve(this, MachineRepresentation::kTagged), 383 var_deferred_on_reject(this, MachineRepresentation::kTagged); 384 385 Branch(WordEqual(promise_fun, constructor), &fast_promise_capability, 386 &promise_capability); 387 388 Bind(&fast_promise_capability); 389 { 390 Node* const deferred_promise = AllocateAndInitJSPromise(context, promise); 391 var_deferred_promise.Bind(deferred_promise); 392 var_deferred_on_resolve.Bind(UndefinedConstant()); 393 var_deferred_on_reject.Bind(UndefinedConstant()); 394 Goto(&perform_promise_then); 395 } 396 397 Bind(&promise_capability); 398 { 399 Node* const capability = NewPromiseCapability(context, constructor); 400 var_deferred_promise.Bind( 401 LoadObjectField(capability, JSPromiseCapability::kPromiseOffset)); 402 var_deferred_on_resolve.Bind( 403 LoadObjectField(capability, JSPromiseCapability::kResolveOffset)); 404 var_deferred_on_reject.Bind( 405 LoadObjectField(capability, JSPromiseCapability::kRejectOffset)); 406 Goto(&perform_promise_then); 407 } 408 409 // 5. Return PerformPromiseThen(promise, onFulfilled, onRejected, 410 // resultCapability). 411 Bind(&perform_promise_then); 412 Node* const result = InternalPerformPromiseThen( 413 context, promise, on_resolve, on_reject, var_deferred_promise.value(), 414 var_deferred_on_resolve.value(), var_deferred_on_reject.value()); 415 return result; 416 } 417 418 Node* PromiseBuiltinsAssembler::InternalPerformPromiseThen( 419 Node* context, Node* promise, Node* on_resolve, Node* on_reject, 420 Node* deferred_promise, Node* deferred_on_resolve, 421 Node* deferred_on_reject) { 422 423 Variable var_on_resolve(this, MachineRepresentation::kTagged), 424 var_on_reject(this, MachineRepresentation::kTagged); 425 426 var_on_resolve.Bind(on_resolve); 427 var_on_reject.Bind(on_reject); 428 429 Label out(this), if_onresolvenotcallable(this), onrejectcheck(this), 430 append_callbacks(this); 431 GotoIf(TaggedIsSmi(on_resolve), &if_onresolvenotcallable); 432 433 Isolate* isolate = this->isolate(); 434 Node* const on_resolve_map = LoadMap(on_resolve); 435 Branch(IsCallableMap(on_resolve_map), &onrejectcheck, 436 &if_onresolvenotcallable); 437 438 Bind(&if_onresolvenotcallable); 439 { 440 Node* const default_resolve_handler_symbol = HeapConstant( 441 isolate->factory()->promise_default_resolve_handler_symbol()); 442 var_on_resolve.Bind(default_resolve_handler_symbol); 443 Goto(&onrejectcheck); 444 } 445 446 Bind(&onrejectcheck); 447 { 448 Label if_onrejectnotcallable(this); 449 GotoIf(TaggedIsSmi(on_reject), &if_onrejectnotcallable); 450 451 Node* const on_reject_map = LoadMap(on_reject); 452 Branch(IsCallableMap(on_reject_map), &append_callbacks, 453 &if_onrejectnotcallable); 454 455 Bind(&if_onrejectnotcallable); 456 { 457 Node* const default_reject_handler_symbol = HeapConstant( 458 isolate->factory()->promise_default_reject_handler_symbol()); 459 var_on_reject.Bind(default_reject_handler_symbol); 460 Goto(&append_callbacks); 461 } 462 } 463 464 Bind(&append_callbacks); 465 { 466 Label fulfilled_check(this); 467 Node* const status = LoadObjectField(promise, JSPromise::kStatusOffset); 468 GotoIfNot(SmiEqual(status, SmiConstant(v8::Promise::kPending)), 469 &fulfilled_check); 470 471 Node* const existing_deferred_promise = 472 LoadObjectField(promise, JSPromise::kDeferredPromiseOffset); 473 474 Label if_noexistingcallbacks(this), if_existingcallbacks(this); 475 Branch(IsUndefined(existing_deferred_promise), &if_noexistingcallbacks, 476 &if_existingcallbacks); 477 478 Bind(&if_noexistingcallbacks); 479 { 480 // Store callbacks directly in the slots. 481 StoreObjectField(promise, JSPromise::kDeferredPromiseOffset, 482 deferred_promise); 483 StoreObjectField(promise, JSPromise::kDeferredOnResolveOffset, 484 deferred_on_resolve); 485 StoreObjectField(promise, JSPromise::kDeferredOnRejectOffset, 486 deferred_on_reject); 487 StoreObjectField(promise, JSPromise::kFulfillReactionsOffset, 488 var_on_resolve.value()); 489 StoreObjectField(promise, JSPromise::kRejectReactionsOffset, 490 var_on_reject.value()); 491 Goto(&out); 492 } 493 494 Bind(&if_existingcallbacks); 495 { 496 Label if_singlecallback(this), if_multiplecallbacks(this); 497 BranchIfJSObject(existing_deferred_promise, &if_singlecallback, 498 &if_multiplecallbacks); 499 500 Bind(&if_singlecallback); 501 { 502 // Create new FixedArrays to store callbacks, and migrate 503 // existing callbacks. 504 Node* const deferred_promise_arr = 505 AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2)); 506 StoreFixedArrayElement(deferred_promise_arr, 0, 507 existing_deferred_promise); 508 StoreFixedArrayElement(deferred_promise_arr, 1, deferred_promise); 509 510 Node* const deferred_on_resolve_arr = 511 AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2)); 512 StoreFixedArrayElement( 513 deferred_on_resolve_arr, 0, 514 LoadObjectField(promise, JSPromise::kDeferredOnResolveOffset)); 515 StoreFixedArrayElement(deferred_on_resolve_arr, 1, deferred_on_resolve); 516 517 Node* const deferred_on_reject_arr = 518 AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2)); 519 StoreFixedArrayElement( 520 deferred_on_reject_arr, 0, 521 LoadObjectField(promise, JSPromise::kDeferredOnRejectOffset)); 522 StoreFixedArrayElement(deferred_on_reject_arr, 1, deferred_on_reject); 523 524 Node* const fulfill_reactions = 525 AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2)); 526 StoreFixedArrayElement( 527 fulfill_reactions, 0, 528 LoadObjectField(promise, JSPromise::kFulfillReactionsOffset)); 529 StoreFixedArrayElement(fulfill_reactions, 1, var_on_resolve.value()); 530 531 Node* const reject_reactions = 532 AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2)); 533 StoreFixedArrayElement( 534 reject_reactions, 0, 535 LoadObjectField(promise, JSPromise::kRejectReactionsOffset)); 536 StoreFixedArrayElement(reject_reactions, 1, var_on_reject.value()); 537 538 // Store new FixedArrays in promise. 539 StoreObjectField(promise, JSPromise::kDeferredPromiseOffset, 540 deferred_promise_arr); 541 StoreObjectField(promise, JSPromise::kDeferredOnResolveOffset, 542 deferred_on_resolve_arr); 543 StoreObjectField(promise, JSPromise::kDeferredOnRejectOffset, 544 deferred_on_reject_arr); 545 StoreObjectField(promise, JSPromise::kFulfillReactionsOffset, 546 fulfill_reactions); 547 StoreObjectField(promise, JSPromise::kRejectReactionsOffset, 548 reject_reactions); 549 Goto(&out); 550 } 551 552 Bind(&if_multiplecallbacks); 553 { 554 AppendPromiseCallback(JSPromise::kDeferredPromiseOffset, promise, 555 deferred_promise); 556 AppendPromiseCallback(JSPromise::kDeferredOnResolveOffset, promise, 557 deferred_on_resolve); 558 AppendPromiseCallback(JSPromise::kDeferredOnRejectOffset, promise, 559 deferred_on_reject); 560 AppendPromiseCallback(JSPromise::kFulfillReactionsOffset, promise, 561 var_on_resolve.value()); 562 AppendPromiseCallback(JSPromise::kRejectReactionsOffset, promise, 563 var_on_reject.value()); 564 Goto(&out); 565 } 566 } 567 568 Bind(&fulfilled_check); 569 { 570 Label reject(this); 571 Node* const result = LoadObjectField(promise, JSPromise::kResultOffset); 572 GotoIfNot(WordEqual(status, SmiConstant(v8::Promise::kFulfilled)), 573 &reject); 574 575 Node* info = AllocatePromiseReactionJobInfo( 576 result, var_on_resolve.value(), deferred_promise, deferred_on_resolve, 577 deferred_on_reject, context); 578 // TODO(gsathya): Move this to TF 579 CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, info); 580 Goto(&out); 581 582 Bind(&reject); 583 { 584 Node* const has_handler = PromiseHasHandler(promise); 585 Label enqueue(this); 586 587 // TODO(gsathya): Fold these runtime calls and move to TF. 588 GotoIf(has_handler, &enqueue); 589 CallRuntime(Runtime::kPromiseRevokeReject, context, promise); 590 Goto(&enqueue); 591 592 Bind(&enqueue); 593 { 594 Node* info = AllocatePromiseReactionJobInfo( 595 result, var_on_reject.value(), deferred_promise, 596 deferred_on_resolve, deferred_on_reject, context); 597 // TODO(gsathya): Move this to TF 598 CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, info); 599 Goto(&out); 600 } 601 } 602 } 603 } 604 605 Bind(&out); 606 PromiseSetHasHandler(promise); 607 return deferred_promise; 608 } 609 610 // Promise fast path implementations rely on unmodified JSPromise instances. 611 // We use a fairly coarse granularity for this and simply check whether both 612 // the promise itself is unmodified (i.e. its map has not changed) and its 613 // prototype is unmodified. 614 // TODO(gsathya): Refactor this out to prevent code dupe with builtins-regexp 615 void PromiseBuiltinsAssembler::BranchIfFastPath(Node* context, Node* promise, 616 Label* if_isunmodified, 617 Label* if_ismodified) { 618 Node* const native_context = LoadNativeContext(context); 619 Node* const promise_fun = 620 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); 621 BranchIfFastPath(native_context, promise_fun, promise, if_isunmodified, 622 if_ismodified); 623 } 624 625 void PromiseBuiltinsAssembler::BranchIfFastPath(Node* native_context, 626 Node* promise_fun, 627 Node* promise, 628 Label* if_isunmodified, 629 Label* if_ismodified) { 630 CSA_ASSERT(this, IsNativeContext(native_context)); 631 CSA_ASSERT(this, 632 WordEqual(promise_fun, 633 LoadContextElement(native_context, 634 Context::PROMISE_FUNCTION_INDEX))); 635 636 Node* const map = LoadMap(promise); 637 Node* const initial_map = 638 LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset); 639 Node* const has_initialmap = WordEqual(map, initial_map); 640 641 GotoIfNot(has_initialmap, if_ismodified); 642 643 Node* const initial_proto_initial_map = 644 LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_MAP_INDEX); 645 Node* const proto_map = LoadMap(LoadMapPrototype(map)); 646 Node* const proto_has_initialmap = 647 WordEqual(proto_map, initial_proto_initial_map); 648 649 Branch(proto_has_initialmap, if_isunmodified, if_ismodified); 650 } 651 652 Node* PromiseBuiltinsAssembler::AllocatePromiseResolveThenableJobInfo( 653 Node* thenable, Node* then, Node* resolve, Node* reject, Node* context) { 654 Node* const info = Allocate(PromiseResolveThenableJobInfo::kSize); 655 StoreMapNoWriteBarrier(info, 656 Heap::kPromiseResolveThenableJobInfoMapRootIndex); 657 StoreObjectFieldNoWriteBarrier( 658 info, PromiseResolveThenableJobInfo::kThenableOffset, thenable); 659 StoreObjectFieldNoWriteBarrier( 660 info, PromiseResolveThenableJobInfo::kThenOffset, then); 661 StoreObjectFieldNoWriteBarrier( 662 info, PromiseResolveThenableJobInfo::kResolveOffset, resolve); 663 StoreObjectFieldNoWriteBarrier( 664 info, PromiseResolveThenableJobInfo::kRejectOffset, reject); 665 StoreObjectFieldNoWriteBarrier( 666 info, PromiseResolveThenableJobInfo::kContextOffset, context); 667 return info; 668 } 669 670 void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context, 671 Node* promise, 672 Node* result) { 673 Isolate* isolate = this->isolate(); 674 675 Variable var_reason(this, MachineRepresentation::kTagged), 676 var_then(this, MachineRepresentation::kTagged); 677 678 Label do_enqueue(this), fulfill(this), if_cycle(this, Label::kDeferred), 679 if_rejectpromise(this, Label::kDeferred), out(this); 680 681 Label cycle_check(this); 682 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &cycle_check); 683 CallRuntime(Runtime::kPromiseHookResolve, context, promise); 684 Goto(&cycle_check); 685 686 Bind(&cycle_check); 687 // 6. If SameValue(resolution, promise) is true, then 688 GotoIf(SameValue(promise, result, context), &if_cycle); 689 690 // 7. If Type(resolution) is not Object, then 691 GotoIf(TaggedIsSmi(result), &fulfill); 692 GotoIfNot(IsJSReceiver(result), &fulfill); 693 694 Label if_nativepromise(this), if_notnativepromise(this, Label::kDeferred); 695 Node* const native_context = LoadNativeContext(context); 696 Node* const promise_fun = 697 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); 698 BranchIfFastPath(native_context, promise_fun, result, &if_nativepromise, 699 &if_notnativepromise); 700 701 // Resolution is a native promise and if it's already resolved or 702 // rejected, shortcircuit the resolution procedure by directly 703 // reusing the value from the promise. 704 Bind(&if_nativepromise); 705 { 706 Node* const thenable_status = 707 LoadObjectField(result, JSPromise::kStatusOffset); 708 Node* const thenable_value = 709 LoadObjectField(result, JSPromise::kResultOffset); 710 711 Label if_isnotpending(this); 712 GotoIfNot(SmiEqual(SmiConstant(v8::Promise::kPending), thenable_status), 713 &if_isnotpending); 714 715 // TODO(gsathya): Use a marker here instead of the actual then 716 // callback, and check for the marker in PromiseResolveThenableJob 717 // and perform PromiseThen. 718 Node* const then = 719 LoadContextElement(native_context, Context::PROMISE_THEN_INDEX); 720 var_then.Bind(then); 721 Goto(&do_enqueue); 722 723 Bind(&if_isnotpending); 724 { 725 Label if_fulfilled(this), if_rejected(this); 726 Branch(SmiEqual(SmiConstant(v8::Promise::kFulfilled), thenable_status), 727 &if_fulfilled, &if_rejected); 728 729 Bind(&if_fulfilled); 730 { 731 PromiseFulfill(context, promise, thenable_value, 732 v8::Promise::kFulfilled); 733 PromiseSetHasHandler(promise); 734 Goto(&out); 735 } 736 737 Bind(&if_rejected); 738 { 739 Label reject(this); 740 Node* const has_handler = PromiseHasHandler(result); 741 742 // Promise has already been rejected, but had no handler. 743 // Revoke previously triggered reject event. 744 GotoIf(has_handler, &reject); 745 CallRuntime(Runtime::kPromiseRevokeReject, context, result); 746 Goto(&reject); 747 748 Bind(&reject); 749 // Don't cause a debug event as this case is forwarding a rejection 750 InternalPromiseReject(context, promise, thenable_value, false); 751 PromiseSetHasHandler(result); 752 Goto(&out); 753 } 754 } 755 } 756 757 Bind(&if_notnativepromise); 758 { 759 // 8. Let then be Get(resolution, "then"). 760 Node* const then_str = HeapConstant(isolate->factory()->then_string()); 761 Callable getproperty_callable = CodeFactory::GetProperty(isolate); 762 Node* const then = 763 CallStub(getproperty_callable, context, result, then_str); 764 765 // 9. If then is an abrupt completion, then 766 GotoIfException(then, &if_rejectpromise, &var_reason); 767 768 // 11. If IsCallable(thenAction) is false, then 769 GotoIf(TaggedIsSmi(then), &fulfill); 770 Node* const then_map = LoadMap(then); 771 GotoIfNot(IsCallableMap(then_map), &fulfill); 772 var_then.Bind(then); 773 Goto(&do_enqueue); 774 } 775 776 Bind(&do_enqueue); 777 { 778 // TODO(gsathya): Add fast path for native promises with unmodified 779 // PromiseThen (which don't need these resolving functions, but 780 // instead can just call resolve/reject directly). 781 Node* resolve = nullptr; 782 Node* reject = nullptr; 783 std::tie(resolve, reject) = CreatePromiseResolvingFunctions( 784 promise, FalseConstant(), native_context); 785 786 Node* const info = AllocatePromiseResolveThenableJobInfo( 787 result, var_then.value(), resolve, reject, context); 788 789 Label enqueue(this); 790 GotoIfNot(IsDebugActive(), &enqueue); 791 792 GotoIf(TaggedIsSmi(result), &enqueue); 793 GotoIfNot(HasInstanceType(result, JS_PROMISE_TYPE), &enqueue); 794 795 // Mark the dependency of the new promise on the resolution 796 Node* const key = 797 HeapConstant(isolate->factory()->promise_handled_by_symbol()); 798 CallRuntime(Runtime::kSetProperty, context, result, key, promise, 799 SmiConstant(STRICT)); 800 Goto(&enqueue); 801 802 // 12. Perform EnqueueJob("PromiseJobs", 803 // PromiseResolveThenableJob, promise, resolution, thenAction). 804 Bind(&enqueue); 805 // TODO(gsathya): Move this to TF 806 CallRuntime(Runtime::kEnqueuePromiseResolveThenableJob, context, info); 807 Goto(&out); 808 } 809 810 // 7.b Return FulfillPromise(promise, resolution). 811 Bind(&fulfill); 812 { 813 PromiseFulfill(context, promise, result, v8::Promise::kFulfilled); 814 Goto(&out); 815 } 816 817 Bind(&if_cycle); 818 { 819 // 6.a Let selfResolutionError be a newly created TypeError object. 820 Node* const message_id = SmiConstant(MessageTemplate::kPromiseCyclic); 821 Node* const error = 822 CallRuntime(Runtime::kNewTypeError, context, message_id, result); 823 var_reason.Bind(error); 824 825 // 6.b Return RejectPromise(promise, selfResolutionError). 826 Goto(&if_rejectpromise); 827 } 828 829 // 9.a Return RejectPromise(promise, then.[[Value]]). 830 Bind(&if_rejectpromise); 831 { 832 InternalPromiseReject(context, promise, var_reason.value(), true); 833 Goto(&out); 834 } 835 836 Bind(&out); 837 } 838 839 void PromiseBuiltinsAssembler::PromiseFulfill( 840 Node* context, Node* promise, Node* result, 841 v8::Promise::PromiseState status) { 842 Label do_promisereset(this), debug_async_event_enqueue_recurring(this); 843 844 Node* const status_smi = SmiConstant(static_cast<int>(status)); 845 Node* const deferred_promise = 846 LoadObjectField(promise, JSPromise::kDeferredPromiseOffset); 847 848 GotoIf(IsUndefined(deferred_promise), &debug_async_event_enqueue_recurring); 849 850 Node* const tasks = 851 status == v8::Promise::kFulfilled 852 ? LoadObjectField(promise, JSPromise::kFulfillReactionsOffset) 853 : LoadObjectField(promise, JSPromise::kRejectReactionsOffset); 854 855 Node* const deferred_on_resolve = 856 LoadObjectField(promise, JSPromise::kDeferredOnResolveOffset); 857 Node* const deferred_on_reject = 858 LoadObjectField(promise, JSPromise::kDeferredOnRejectOffset); 859 860 Node* const info = AllocatePromiseReactionJobInfo( 861 result, tasks, deferred_promise, deferred_on_resolve, deferred_on_reject, 862 context); 863 864 CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, info); 865 Goto(&debug_async_event_enqueue_recurring); 866 867 Bind(&debug_async_event_enqueue_recurring); 868 { 869 GotoIfNot(IsDebugActive(), &do_promisereset); 870 CallRuntime(Runtime::kDebugAsyncEventEnqueueRecurring, context, promise, 871 status_smi); 872 Goto(&do_promisereset); 873 } 874 875 Bind(&do_promisereset); 876 { 877 StoreObjectField(promise, JSPromise::kStatusOffset, status_smi); 878 StoreObjectField(promise, JSPromise::kResultOffset, result); 879 StoreObjectFieldRoot(promise, JSPromise::kDeferredPromiseOffset, 880 Heap::kUndefinedValueRootIndex); 881 StoreObjectFieldRoot(promise, JSPromise::kDeferredOnResolveOffset, 882 Heap::kUndefinedValueRootIndex); 883 StoreObjectFieldRoot(promise, JSPromise::kDeferredOnRejectOffset, 884 Heap::kUndefinedValueRootIndex); 885 StoreObjectFieldRoot(promise, JSPromise::kFulfillReactionsOffset, 886 Heap::kUndefinedValueRootIndex); 887 StoreObjectFieldRoot(promise, JSPromise::kRejectReactionsOffset, 888 Heap::kUndefinedValueRootIndex); 889 } 890 } 891 892 void PromiseBuiltinsAssembler::BranchIfAccessCheckFailed( 893 Node* context, Node* native_context, Node* promise_constructor, 894 Node* executor, Label* if_noaccess) { 895 Variable var_executor(this, MachineRepresentation::kTagged); 896 var_executor.Bind(executor); 897 Label has_access(this), call_runtime(this, Label::kDeferred); 898 899 // If executor is a bound function, load the bound function until we've 900 // reached an actual function. 901 Label found_function(this), loop_over_bound_function(this, &var_executor); 902 Goto(&loop_over_bound_function); 903 Bind(&loop_over_bound_function); 904 { 905 Node* executor_type = LoadInstanceType(var_executor.value()); 906 GotoIf(InstanceTypeEqual(executor_type, JS_FUNCTION_TYPE), &found_function); 907 GotoIfNot(InstanceTypeEqual(executor_type, JS_BOUND_FUNCTION_TYPE), 908 &call_runtime); 909 var_executor.Bind(LoadObjectField( 910 var_executor.value(), JSBoundFunction::kBoundTargetFunctionOffset)); 911 Goto(&loop_over_bound_function); 912 } 913 914 // Load the context from the function and compare it to the Promise 915 // constructor's context. If they match, everything is fine, otherwise, bail 916 // out to the runtime. 917 Bind(&found_function); 918 { 919 Node* function_context = 920 LoadObjectField(var_executor.value(), JSFunction::kContextOffset); 921 Node* native_function_context = LoadNativeContext(function_context); 922 Branch(WordEqual(native_context, native_function_context), &has_access, 923 &call_runtime); 924 } 925 926 Bind(&call_runtime); 927 { 928 Branch(WordEqual(CallRuntime(Runtime::kAllowDynamicFunction, context, 929 promise_constructor), 930 BooleanConstant(true)), 931 &has_access, if_noaccess); 932 } 933 934 Bind(&has_access); 935 } 936 937 void PromiseBuiltinsAssembler::InternalPromiseReject(Node* context, 938 Node* promise, Node* value, 939 Node* debug_event) { 940 Label out(this); 941 GotoIfNot(IsDebugActive(), &out); 942 GotoIfNot(WordEqual(TrueConstant(), debug_event), &out); 943 CallRuntime(Runtime::kDebugPromiseReject, context, promise, value); 944 Goto(&out); 945 946 Bind(&out); 947 InternalPromiseReject(context, promise, value, false); 948 } 949 950 // This duplicates a lot of logic from PromiseRejectEvent in 951 // runtime-promise.cc 952 void PromiseBuiltinsAssembler::InternalPromiseReject(Node* context, 953 Node* promise, Node* value, 954 bool debug_event) { 955 Label fulfill(this), report_unhandledpromise(this), run_promise_hook(this); 956 957 if (debug_event) { 958 GotoIfNot(IsDebugActive(), &run_promise_hook); 959 CallRuntime(Runtime::kDebugPromiseReject, context, promise, value); 960 Goto(&run_promise_hook); 961 } else { 962 Goto(&run_promise_hook); 963 } 964 965 Bind(&run_promise_hook); 966 { 967 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &report_unhandledpromise); 968 CallRuntime(Runtime::kPromiseHookResolve, context, promise); 969 Goto(&report_unhandledpromise); 970 } 971 972 Bind(&report_unhandledpromise); 973 { 974 GotoIf(PromiseHasHandler(promise), &fulfill); 975 CallRuntime(Runtime::kReportPromiseReject, context, promise, value); 976 Goto(&fulfill); 977 } 978 979 Bind(&fulfill); 980 PromiseFulfill(context, promise, value, v8::Promise::kRejected); 981 } 982 983 // ES#sec-promise-reject-functions 984 // Promise Reject Functions 985 TF_BUILTIN(PromiseRejectClosure, PromiseBuiltinsAssembler) { 986 Node* const value = Parameter(1); 987 Node* const context = Parameter(4); 988 989 Label out(this); 990 991 // 3. Let alreadyResolved be F.[[AlreadyResolved]]. 992 int has_already_visited_slot = kAlreadyVisitedSlot; 993 994 Node* const has_already_visited = 995 LoadContextElement(context, has_already_visited_slot); 996 997 // 4. If alreadyResolved.[[Value]] is true, return undefined. 998 GotoIf(SmiEqual(has_already_visited, SmiConstant(1)), &out); 999 1000 // 5.Set alreadyResolved.[[Value]] to true. 1001 StoreContextElementNoWriteBarrier(context, has_already_visited_slot, 1002 SmiConstant(1)); 1003 1004 // 2. Let promise be F.[[Promise]]. 1005 Node* const promise = 1006 LoadContextElement(context, IntPtrConstant(kPromiseSlot)); 1007 Node* const debug_event = 1008 LoadContextElement(context, IntPtrConstant(kDebugEventSlot)); 1009 1010 InternalPromiseReject(context, promise, value, debug_event); 1011 Return(UndefinedConstant()); 1012 1013 Bind(&out); 1014 Return(UndefinedConstant()); 1015 } 1016 1017 TF_BUILTIN(PromiseConstructor, PromiseBuiltinsAssembler) { 1018 Node* const executor = Parameter(1); 1019 Node* const new_target = Parameter(2); 1020 Node* const context = Parameter(4); 1021 Isolate* isolate = this->isolate(); 1022 1023 Label if_targetisundefined(this, Label::kDeferred); 1024 1025 GotoIf(IsUndefined(new_target), &if_targetisundefined); 1026 1027 Label if_notcallable(this, Label::kDeferred); 1028 1029 GotoIf(TaggedIsSmi(executor), &if_notcallable); 1030 1031 Node* const executor_map = LoadMap(executor); 1032 GotoIfNot(IsCallableMap(executor_map), &if_notcallable); 1033 1034 Node* const native_context = LoadNativeContext(context); 1035 Node* const promise_fun = 1036 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); 1037 Node* const is_debug_active = IsDebugActive(); 1038 Label if_targetisnotmodified(this), 1039 if_targetismodified(this, Label::kDeferred), run_executor(this), 1040 debug_push(this), if_noaccess(this, Label::kDeferred); 1041 1042 BranchIfAccessCheckFailed(context, native_context, promise_fun, executor, 1043 &if_noaccess); 1044 1045 Branch(WordEqual(promise_fun, new_target), &if_targetisnotmodified, 1046 &if_targetismodified); 1047 1048 Variable var_result(this, MachineRepresentation::kTagged), 1049 var_reject_call(this, MachineRepresentation::kTagged), 1050 var_reason(this, MachineRepresentation::kTagged); 1051 1052 Bind(&if_targetisnotmodified); 1053 { 1054 Node* const instance = AllocateAndInitJSPromise(context); 1055 var_result.Bind(instance); 1056 Goto(&debug_push); 1057 } 1058 1059 Bind(&if_targetismodified); 1060 { 1061 ConstructorBuiltinsAssembler constructor_assembler(this->state()); 1062 Node* const instance = constructor_assembler.EmitFastNewObject( 1063 context, promise_fun, new_target); 1064 PromiseInit(instance); 1065 var_result.Bind(instance); 1066 1067 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &debug_push); 1068 CallRuntime(Runtime::kPromiseHookInit, context, instance, 1069 UndefinedConstant()); 1070 Goto(&debug_push); 1071 } 1072 1073 Bind(&debug_push); 1074 { 1075 GotoIfNot(is_debug_active, &run_executor); 1076 CallRuntime(Runtime::kDebugPushPromise, context, var_result.value()); 1077 Goto(&run_executor); 1078 } 1079 1080 Bind(&run_executor); 1081 { 1082 Label out(this), if_rejectpromise(this), debug_pop(this, Label::kDeferred); 1083 1084 Node *resolve, *reject; 1085 std::tie(resolve, reject) = CreatePromiseResolvingFunctions( 1086 var_result.value(), TrueConstant(), native_context); 1087 Callable call_callable = CodeFactory::Call(isolate); 1088 1089 Node* const maybe_exception = CallJS(call_callable, context, executor, 1090 UndefinedConstant(), resolve, reject); 1091 1092 GotoIfException(maybe_exception, &if_rejectpromise, &var_reason); 1093 Branch(is_debug_active, &debug_pop, &out); 1094 1095 Bind(&if_rejectpromise); 1096 { 1097 Callable call_callable = CodeFactory::Call(isolate); 1098 CallJS(call_callable, context, reject, UndefinedConstant(), 1099 var_reason.value()); 1100 Branch(is_debug_active, &debug_pop, &out); 1101 } 1102 1103 Bind(&debug_pop); 1104 { 1105 CallRuntime(Runtime::kDebugPopPromise, context); 1106 Goto(&out); 1107 } 1108 Bind(&out); 1109 Return(var_result.value()); 1110 } 1111 1112 // 1. If NewTarget is undefined, throw a TypeError exception. 1113 Bind(&if_targetisundefined); 1114 { 1115 Node* const message_id = SmiConstant(MessageTemplate::kNotAPromise); 1116 CallRuntime(Runtime::kThrowTypeError, context, message_id, new_target); 1117 Unreachable(); 1118 } 1119 1120 // 2. If IsCallable(executor) is false, throw a TypeError exception. 1121 Bind(&if_notcallable); 1122 { 1123 Node* const message_id = 1124 SmiConstant(MessageTemplate::kResolverNotAFunction); 1125 CallRuntime(Runtime::kThrowTypeError, context, message_id, executor); 1126 Unreachable(); 1127 } 1128 1129 // Silently fail if the stack looks fishy. 1130 Bind(&if_noaccess); 1131 { 1132 Node* const counter_id = 1133 SmiConstant(v8::Isolate::kPromiseConstructorReturnedUndefined); 1134 CallRuntime(Runtime::kIncrementUseCounter, context, counter_id); 1135 Return(UndefinedConstant()); 1136 } 1137 } 1138 1139 TF_BUILTIN(PromiseInternalConstructor, PromiseBuiltinsAssembler) { 1140 Node* const parent = Parameter(1); 1141 Node* const context = Parameter(4); 1142 Return(AllocateAndInitJSPromise(context, parent)); 1143 } 1144 1145 TF_BUILTIN(IsPromise, PromiseBuiltinsAssembler) { 1146 Node* const maybe_promise = Parameter(1); 1147 Label if_notpromise(this, Label::kDeferred); 1148 1149 GotoIf(TaggedIsSmi(maybe_promise), &if_notpromise); 1150 1151 Node* const result = 1152 SelectBooleanConstant(HasInstanceType(maybe_promise, JS_PROMISE_TYPE)); 1153 Return(result); 1154 1155 Bind(&if_notpromise); 1156 Return(FalseConstant()); 1157 } 1158 1159 // ES#sec-promise.prototype.then 1160 // Promise.prototype.catch ( onFulfilled, onRejected ) 1161 TF_BUILTIN(PromiseThen, PromiseBuiltinsAssembler) { 1162 // 1. Let promise be the this value. 1163 Node* const promise = Parameter(0); 1164 Node* const on_resolve = Parameter(1); 1165 Node* const on_reject = Parameter(2); 1166 Node* const context = Parameter(5); 1167 1168 Node* const result = 1169 InternalPromiseThen(context, promise, on_resolve, on_reject); 1170 Return(result); 1171 } 1172 1173 // ES#sec-promise-resolve-functions 1174 // Promise Resolve Functions 1175 TF_BUILTIN(PromiseResolveClosure, PromiseBuiltinsAssembler) { 1176 Node* const value = Parameter(1); 1177 Node* const context = Parameter(4); 1178 1179 Label out(this); 1180 1181 // 3. Let alreadyResolved be F.[[AlreadyResolved]]. 1182 int has_already_visited_slot = kAlreadyVisitedSlot; 1183 1184 Node* const has_already_visited = 1185 LoadContextElement(context, has_already_visited_slot); 1186 1187 // 4. If alreadyResolved.[[Value]] is true, return undefined. 1188 GotoIf(SmiEqual(has_already_visited, SmiConstant(1)), &out); 1189 1190 // 5.Set alreadyResolved.[[Value]] to true. 1191 StoreContextElementNoWriteBarrier(context, has_already_visited_slot, 1192 SmiConstant(1)); 1193 1194 // 2. Let promise be F.[[Promise]]. 1195 Node* const promise = 1196 LoadContextElement(context, IntPtrConstant(kPromiseSlot)); 1197 1198 InternalResolvePromise(context, promise, value); 1199 Return(UndefinedConstant()); 1200 1201 Bind(&out); 1202 Return(UndefinedConstant()); 1203 } 1204 1205 TF_BUILTIN(ResolvePromise, PromiseBuiltinsAssembler) { 1206 Node* const promise = Parameter(1); 1207 Node* const result = Parameter(2); 1208 Node* const context = Parameter(5); 1209 1210 InternalResolvePromise(context, promise, result); 1211 Return(UndefinedConstant()); 1212 } 1213 1214 TF_BUILTIN(PromiseHandleReject, PromiseBuiltinsAssembler) { 1215 typedef PromiseHandleRejectDescriptor Descriptor; 1216 1217 Node* const promise = Parameter(Descriptor::kPromise); 1218 Node* const on_reject = Parameter(Descriptor::kOnReject); 1219 Node* const exception = Parameter(Descriptor::kException); 1220 Node* const context = Parameter(Descriptor::kContext); 1221 1222 Callable call_callable = CodeFactory::Call(isolate()); 1223 Variable var_unused(this, MachineRepresentation::kTagged); 1224 1225 Label if_internalhandler(this), if_customhandler(this, Label::kDeferred); 1226 Branch(IsUndefined(on_reject), &if_internalhandler, &if_customhandler); 1227 1228 Bind(&if_internalhandler); 1229 { 1230 InternalPromiseReject(context, promise, exception, false); 1231 Return(UndefinedConstant()); 1232 } 1233 1234 Bind(&if_customhandler); 1235 { 1236 CallJS(call_callable, context, on_reject, UndefinedConstant(), exception); 1237 Return(UndefinedConstant()); 1238 } 1239 } 1240 1241 TF_BUILTIN(PromiseHandle, PromiseBuiltinsAssembler) { 1242 Node* const value = Parameter(1); 1243 Node* const handler = Parameter(2); 1244 Node* const deferred_promise = Parameter(3); 1245 Node* const deferred_on_resolve = Parameter(4); 1246 Node* const deferred_on_reject = Parameter(5); 1247 Node* const context = Parameter(8); 1248 Isolate* isolate = this->isolate(); 1249 1250 Variable var_reason(this, MachineRepresentation::kTagged); 1251 1252 Node* const is_debug_active = IsDebugActive(); 1253 Label run_handler(this), if_rejectpromise(this), promisehook_before(this), 1254 promisehook_after(this), debug_pop(this); 1255 1256 GotoIfNot(is_debug_active, &promisehook_before); 1257 CallRuntime(Runtime::kDebugPushPromise, context, deferred_promise); 1258 Goto(&promisehook_before); 1259 1260 Bind(&promisehook_before); 1261 { 1262 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &run_handler); 1263 CallRuntime(Runtime::kPromiseHookBefore, context, deferred_promise); 1264 Goto(&run_handler); 1265 } 1266 1267 Bind(&run_handler); 1268 { 1269 Label if_defaulthandler(this), if_callablehandler(this), 1270 if_internalhandler(this), if_customhandler(this, Label::kDeferred); 1271 Variable var_result(this, MachineRepresentation::kTagged); 1272 1273 Branch(IsSymbol(handler), &if_defaulthandler, &if_callablehandler); 1274 1275 Bind(&if_defaulthandler); 1276 { 1277 Label if_resolve(this), if_reject(this); 1278 Node* const default_resolve_handler_symbol = HeapConstant( 1279 isolate->factory()->promise_default_resolve_handler_symbol()); 1280 Branch(WordEqual(default_resolve_handler_symbol, handler), &if_resolve, 1281 &if_reject); 1282 1283 Bind(&if_resolve); 1284 { 1285 var_result.Bind(value); 1286 Branch(IsUndefined(deferred_on_resolve), &if_internalhandler, 1287 &if_customhandler); 1288 } 1289 1290 Bind(&if_reject); 1291 { 1292 var_reason.Bind(value); 1293 Goto(&if_rejectpromise); 1294 } 1295 } 1296 1297 Bind(&if_callablehandler); 1298 { 1299 Callable call_callable = CodeFactory::Call(isolate); 1300 Node* const result = 1301 CallJS(call_callable, context, handler, UndefinedConstant(), value); 1302 var_result.Bind(result); 1303 GotoIfException(result, &if_rejectpromise, &var_reason); 1304 Branch(IsUndefined(deferred_on_resolve), &if_internalhandler, 1305 &if_customhandler); 1306 } 1307 1308 Bind(&if_internalhandler); 1309 InternalResolvePromise(context, deferred_promise, var_result.value()); 1310 Goto(&promisehook_after); 1311 1312 Bind(&if_customhandler); 1313 { 1314 Callable call_callable = CodeFactory::Call(isolate); 1315 Node* const maybe_exception = 1316 CallJS(call_callable, context, deferred_on_resolve, 1317 UndefinedConstant(), var_result.value()); 1318 GotoIfException(maybe_exception, &if_rejectpromise, &var_reason); 1319 Goto(&promisehook_after); 1320 } 1321 } 1322 1323 Bind(&if_rejectpromise); 1324 { 1325 Callable promise_handle_reject = CodeFactory::PromiseHandleReject(isolate); 1326 CallStub(promise_handle_reject, context, deferred_promise, 1327 deferred_on_reject, var_reason.value()); 1328 Goto(&promisehook_after); 1329 } 1330 1331 Bind(&promisehook_after); 1332 { 1333 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &debug_pop); 1334 CallRuntime(Runtime::kPromiseHookAfter, context, deferred_promise); 1335 Goto(&debug_pop); 1336 } 1337 1338 Bind(&debug_pop); 1339 { 1340 Label out(this); 1341 1342 GotoIfNot(is_debug_active, &out); 1343 CallRuntime(Runtime::kDebugPopPromise, context); 1344 Goto(&out); 1345 1346 Bind(&out); 1347 Return(UndefinedConstant()); 1348 } 1349 } 1350 1351 // ES#sec-promise.prototype.catch 1352 // Promise.prototype.catch ( onRejected ) 1353 TF_BUILTIN(PromiseCatch, PromiseBuiltinsAssembler) { 1354 // 1. Let promise be the this value. 1355 Node* const promise = Parameter(0); 1356 Node* const on_resolve = UndefinedConstant(); 1357 Node* const on_reject = Parameter(1); 1358 Node* const context = Parameter(4); 1359 1360 Label if_internalthen(this), if_customthen(this, Label::kDeferred); 1361 GotoIf(TaggedIsSmi(promise), &if_customthen); 1362 BranchIfFastPath(context, promise, &if_internalthen, &if_customthen); 1363 1364 Bind(&if_internalthen); 1365 { 1366 Node* const result = 1367 InternalPromiseThen(context, promise, on_resolve, on_reject); 1368 Return(result); 1369 } 1370 1371 Bind(&if_customthen); 1372 { 1373 Isolate* isolate = this->isolate(); 1374 Node* const then_str = HeapConstant(isolate->factory()->then_string()); 1375 Callable getproperty_callable = CodeFactory::GetProperty(isolate); 1376 Node* const then = 1377 CallStub(getproperty_callable, context, promise, then_str); 1378 Callable call_callable = CodeFactory::Call(isolate); 1379 Node* const result = 1380 CallJS(call_callable, context, then, promise, on_resolve, on_reject); 1381 Return(result); 1382 } 1383 } 1384 1385 TF_BUILTIN(PromiseResolve, PromiseBuiltinsAssembler) { 1386 // 1. Let C be the this value. 1387 Node* receiver = Parameter(0); 1388 Node* value = Parameter(1); 1389 Node* context = Parameter(4); 1390 Isolate* isolate = this->isolate(); 1391 1392 // 2. If Type(C) is not Object, throw a TypeError exception. 1393 ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject, 1394 "PromiseResolve"); 1395 1396 Label if_valueisnativepromise(this), if_valueisnotnativepromise(this), 1397 if_valueisnotpromise(this); 1398 1399 // 3.If IsPromise(x) is true, then 1400 GotoIf(TaggedIsSmi(value), &if_valueisnotpromise); 1401 1402 // This shortcircuits the constructor lookups. 1403 GotoIfNot(HasInstanceType(value, JS_PROMISE_TYPE), &if_valueisnotpromise); 1404 1405 // This adds a fast path as non-subclassed native promises don't have 1406 // an observable constructor lookup. 1407 Node* const native_context = LoadNativeContext(context); 1408 Node* const promise_fun = 1409 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); 1410 BranchIfFastPath(native_context, promise_fun, value, &if_valueisnativepromise, 1411 &if_valueisnotnativepromise); 1412 1413 Bind(&if_valueisnativepromise); 1414 { 1415 GotoIfNot(WordEqual(promise_fun, receiver), &if_valueisnotnativepromise); 1416 Return(value); 1417 } 1418 1419 // At this point, value or/and receiver are not native promises, but 1420 // they could be of the same subclass. 1421 Bind(&if_valueisnotnativepromise); 1422 { 1423 // 3.a Let xConstructor be ? Get(x, "constructor"). 1424 // The constructor lookup is observable. 1425 Node* const constructor_str = 1426 HeapConstant(isolate->factory()->constructor_string()); 1427 Callable getproperty_callable = CodeFactory::GetProperty(isolate); 1428 Node* const constructor = 1429 CallStub(getproperty_callable, context, value, constructor_str); 1430 1431 // 3.b If SameValue(xConstructor, C) is true, return x. 1432 GotoIfNot(SameValue(constructor, receiver, context), &if_valueisnotpromise); 1433 1434 Return(value); 1435 } 1436 1437 Bind(&if_valueisnotpromise); 1438 { 1439 Label if_nativepromise(this), if_notnativepromise(this); 1440 BranchIfFastPath(context, receiver, &if_nativepromise, 1441 &if_notnativepromise); 1442 1443 // This adds a fast path for native promises that don't need to 1444 // create NewPromiseCapability. 1445 Bind(&if_nativepromise); 1446 { 1447 Label do_resolve(this); 1448 1449 Node* const result = AllocateAndInitJSPromise(context); 1450 InternalResolvePromise(context, result, value); 1451 Return(result); 1452 } 1453 1454 Bind(&if_notnativepromise); 1455 { 1456 // 4. Let promiseCapability be ? NewPromiseCapability(C). 1457 Node* const capability = NewPromiseCapability(context, receiver); 1458 1459 // 5. Perform ? Call(promiseCapability.[[Resolve]], undefined, x ). 1460 Callable call_callable = CodeFactory::Call(isolate); 1461 Node* const resolve = 1462 LoadObjectField(capability, JSPromiseCapability::kResolveOffset); 1463 CallJS(call_callable, context, resolve, UndefinedConstant(), value); 1464 1465 // 6. Return promiseCapability.[[Promise]]. 1466 Node* const result = 1467 LoadObjectField(capability, JSPromiseCapability::kPromiseOffset); 1468 Return(result); 1469 } 1470 } 1471 } 1472 1473 TF_BUILTIN(PromiseGetCapabilitiesExecutor, PromiseBuiltinsAssembler) { 1474 Node* const resolve = Parameter(1); 1475 Node* const reject = Parameter(2); 1476 Node* const context = Parameter(5); 1477 1478 Node* const capability = LoadContextElement(context, kCapabilitySlot); 1479 1480 Label if_alreadyinvoked(this, Label::kDeferred); 1481 GotoIf(WordNotEqual( 1482 LoadObjectField(capability, JSPromiseCapability::kResolveOffset), 1483 UndefinedConstant()), 1484 &if_alreadyinvoked); 1485 GotoIf(WordNotEqual( 1486 LoadObjectField(capability, JSPromiseCapability::kRejectOffset), 1487 UndefinedConstant()), 1488 &if_alreadyinvoked); 1489 1490 StoreObjectField(capability, JSPromiseCapability::kResolveOffset, resolve); 1491 StoreObjectField(capability, JSPromiseCapability::kRejectOffset, reject); 1492 1493 Return(UndefinedConstant()); 1494 1495 Bind(&if_alreadyinvoked); 1496 Node* message = SmiConstant(MessageTemplate::kPromiseExecutorAlreadyInvoked); 1497 CallRuntime(Runtime::kThrowTypeError, context, message); 1498 Unreachable(); 1499 } 1500 1501 TF_BUILTIN(NewPromiseCapability, PromiseBuiltinsAssembler) { 1502 Node* constructor = Parameter(1); 1503 Node* debug_event = Parameter(2); 1504 Node* context = Parameter(5); 1505 1506 CSA_ASSERT_JS_ARGC_EQ(this, 2); 1507 1508 Return(NewPromiseCapability(context, constructor, debug_event)); 1509 } 1510 1511 TF_BUILTIN(PromiseReject, PromiseBuiltinsAssembler) { 1512 // 1. Let C be the this value. 1513 Node* const receiver = Parameter(0); 1514 Node* const reason = Parameter(1); 1515 Node* const context = Parameter(4); 1516 1517 // 2. If Type(C) is not Object, throw a TypeError exception. 1518 ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject, 1519 "PromiseReject"); 1520 1521 Label if_nativepromise(this), if_custompromise(this, Label::kDeferred); 1522 Node* const native_context = LoadNativeContext(context); 1523 Node* const promise_fun = 1524 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); 1525 Branch(WordEqual(promise_fun, receiver), &if_nativepromise, 1526 &if_custompromise); 1527 1528 Bind(&if_nativepromise); 1529 { 1530 Node* const promise = AllocateAndSetJSPromise( 1531 context, SmiConstant(v8::Promise::kRejected), reason); 1532 CallRuntime(Runtime::kPromiseRejectEventFromStack, context, promise, 1533 reason); 1534 Return(promise); 1535 } 1536 1537 Bind(&if_custompromise); 1538 { 1539 // 3. Let promiseCapability be ? NewPromiseCapability(C). 1540 Node* const capability = NewPromiseCapability(context, receiver); 1541 1542 // 4. Perform ? Call(promiseCapability.[[Reject]], undefined, r ). 1543 Node* const reject = 1544 LoadObjectField(capability, JSPromiseCapability::kRejectOffset); 1545 Callable call_callable = CodeFactory::Call(isolate()); 1546 CallJS(call_callable, context, reject, UndefinedConstant(), reason); 1547 1548 // 5. Return promiseCapability.[[Promise]]. 1549 Node* const promise = 1550 LoadObjectField(capability, JSPromiseCapability::kPromiseOffset); 1551 Return(promise); 1552 } 1553 } 1554 1555 TF_BUILTIN(InternalPromiseReject, PromiseBuiltinsAssembler) { 1556 Node* const promise = Parameter(1); 1557 Node* const reason = Parameter(2); 1558 Node* const debug_event = Parameter(3); 1559 Node* const context = Parameter(6); 1560 1561 InternalPromiseReject(context, promise, reason, debug_event); 1562 Return(UndefinedConstant()); 1563 } 1564 1565 Node* PromiseBuiltinsAssembler::CreatePromiseFinallyContext( 1566 Node* on_finally, Node* native_context) { 1567 Node* const context = 1568 CreatePromiseContext(native_context, kOnFinallyContextLength); 1569 StoreContextElementNoWriteBarrier(context, kOnFinallySlot, on_finally); 1570 return context; 1571 } 1572 1573 std::pair<Node*, Node*> PromiseBuiltinsAssembler::CreatePromiseFinallyFunctions( 1574 Node* on_finally, Node* native_context) { 1575 Node* const promise_context = 1576 CreatePromiseFinallyContext(on_finally, native_context); 1577 Node* const map = LoadContextElement( 1578 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); 1579 Node* const then_finally_info = LoadContextElement( 1580 native_context, Context::PROMISE_THEN_FINALLY_SHARED_FUN); 1581 Node* const then_finally = AllocateFunctionWithMapAndContext( 1582 map, then_finally_info, promise_context); 1583 Node* const catch_finally_info = LoadContextElement( 1584 native_context, Context::PROMISE_CATCH_FINALLY_SHARED_FUN); 1585 Node* const catch_finally = AllocateFunctionWithMapAndContext( 1586 map, catch_finally_info, promise_context); 1587 return std::make_pair(then_finally, catch_finally); 1588 } 1589 1590 TF_BUILTIN(PromiseValueThunkFinally, PromiseBuiltinsAssembler) { 1591 Node* const context = Parameter(3); 1592 1593 Node* const value = LoadContextElement(context, kOnFinallySlot); 1594 Return(value); 1595 } 1596 1597 Node* PromiseBuiltinsAssembler::CreateValueThunkFunctionContext( 1598 Node* value, Node* native_context) { 1599 Node* const context = 1600 CreatePromiseContext(native_context, kOnFinallyContextLength); 1601 StoreContextElementNoWriteBarrier(context, kOnFinallySlot, value); 1602 return context; 1603 } 1604 1605 Node* PromiseBuiltinsAssembler::CreateValueThunkFunction(Node* value, 1606 Node* native_context) { 1607 Node* const value_thunk_context = 1608 CreateValueThunkFunctionContext(value, native_context); 1609 Node* const map = LoadContextElement( 1610 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); 1611 Node* const value_thunk_info = LoadContextElement( 1612 native_context, Context::PROMISE_VALUE_THUNK_FINALLY_SHARED_FUN); 1613 Node* const value_thunk = AllocateFunctionWithMapAndContext( 1614 map, value_thunk_info, value_thunk_context); 1615 return value_thunk; 1616 } 1617 1618 TF_BUILTIN(PromiseThenFinally, PromiseBuiltinsAssembler) { 1619 CSA_ASSERT_JS_ARGC_EQ(this, 1); 1620 1621 Node* const value = Parameter(1); 1622 Node* const context = Parameter(4); 1623 1624 Node* const on_finally = LoadContextElement(context, kOnFinallySlot); 1625 1626 // 2.a Let result be ? Call(onFinally, undefined). 1627 Callable call_callable = CodeFactory::Call(isolate()); 1628 Node* result = 1629 CallJS(call_callable, context, on_finally, UndefinedConstant()); 1630 1631 // 2.b Let promise be ! PromiseResolve( %Promise%, result). 1632 Node* const promise = AllocateAndInitJSPromise(context); 1633 InternalResolvePromise(context, promise, result); 1634 1635 // 2.c Let valueThunk be equivalent to a function that returns value. 1636 Node* native_context = LoadNativeContext(context); 1637 Node* const value_thunk = CreateValueThunkFunction(value, native_context); 1638 1639 // 2.d Let promiseCapability be ! NewPromiseCapability( %Promise%). 1640 Node* const promise_capability = AllocateAndInitJSPromise(context, promise); 1641 1642 // 2.e Return PerformPromiseThen(promise, valueThunk, undefined, 1643 // promiseCapability). 1644 InternalPerformPromiseThen(context, promise, value_thunk, UndefinedConstant(), 1645 promise_capability, UndefinedConstant(), 1646 UndefinedConstant()); 1647 Return(promise_capability); 1648 } 1649 1650 TF_BUILTIN(PromiseThrowerFinally, PromiseBuiltinsAssembler) { 1651 Node* const context = Parameter(3); 1652 1653 Node* const reason = LoadContextElement(context, kOnFinallySlot); 1654 CallRuntime(Runtime::kThrow, context, reason); 1655 Unreachable(); 1656 } 1657 1658 Node* PromiseBuiltinsAssembler::CreateThrowerFunctionContext( 1659 Node* reason, Node* native_context) { 1660 Node* const context = 1661 CreatePromiseContext(native_context, kOnFinallyContextLength); 1662 StoreContextElementNoWriteBarrier(context, kOnFinallySlot, reason); 1663 return context; 1664 } 1665 1666 Node* PromiseBuiltinsAssembler::CreateThrowerFunction(Node* reason, 1667 Node* native_context) { 1668 Node* const thrower_context = 1669 CreateThrowerFunctionContext(reason, native_context); 1670 Node* const map = LoadContextElement( 1671 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); 1672 Node* const thrower_info = LoadContextElement( 1673 native_context, Context::PROMISE_THROWER_FINALLY_SHARED_FUN); 1674 Node* const thrower = 1675 AllocateFunctionWithMapAndContext(map, thrower_info, thrower_context); 1676 return thrower; 1677 } 1678 1679 TF_BUILTIN(PromiseCatchFinally, PromiseBuiltinsAssembler) { 1680 CSA_ASSERT_JS_ARGC_EQ(this, 1); 1681 1682 Node* const reason = Parameter(1); 1683 Node* const context = Parameter(4); 1684 1685 Node* const on_finally = LoadContextElement(context, kOnFinallySlot); 1686 1687 // 2.a Let result be ? Call(onFinally, undefined). 1688 Callable call_callable = CodeFactory::Call(isolate()); 1689 Node* result = 1690 CallJS(call_callable, context, on_finally, UndefinedConstant()); 1691 1692 // 2.b Let promise be ! PromiseResolve( %Promise%, result). 1693 Node* const promise = AllocateAndInitJSPromise(context); 1694 InternalResolvePromise(context, promise, result); 1695 1696 // 2.c Let thrower be equivalent to a function that throws reason. 1697 Node* native_context = LoadNativeContext(context); 1698 Node* const thrower = CreateThrowerFunction(reason, native_context); 1699 1700 // 2.d Let promiseCapability be ! NewPromiseCapability( %Promise%). 1701 Node* const promise_capability = AllocateAndInitJSPromise(context, promise); 1702 1703 // 2.e Return PerformPromiseThen(promise, thrower, undefined, 1704 // promiseCapability). 1705 InternalPerformPromiseThen(context, promise, thrower, UndefinedConstant(), 1706 promise_capability, UndefinedConstant(), 1707 UndefinedConstant()); 1708 Return(promise_capability); 1709 } 1710 1711 TF_BUILTIN(PromiseFinally, PromiseBuiltinsAssembler) { 1712 CSA_ASSERT_JS_ARGC_EQ(this, 1); 1713 1714 // 1. Let promise be the this value. 1715 Node* const promise = Parameter(0); 1716 Node* const on_finally = Parameter(1); 1717 Node* const context = Parameter(4); 1718 1719 // 2. If IsPromise(promise) is false, throw a TypeError exception. 1720 ThrowIfNotInstanceType(context, promise, JS_PROMISE_TYPE, 1721 "Promise.prototype.finally"); 1722 1723 Variable var_then_finally(this, MachineRepresentation::kTagged), 1724 var_catch_finally(this, MachineRepresentation::kTagged); 1725 1726 Label if_notcallable(this, Label::kDeferred), perform_finally(this); 1727 1728 // 3. Let thenFinally be ! CreateThenFinally(onFinally). 1729 // 4. Let catchFinally be ! CreateCatchFinally(onFinally). 1730 GotoIf(TaggedIsSmi(on_finally), &if_notcallable); 1731 Node* const on_finally_map = LoadMap(on_finally); 1732 GotoIfNot(IsCallableMap(on_finally_map), &if_notcallable); 1733 1734 Node* const native_context = LoadNativeContext(context); 1735 Node* then_finally = nullptr; 1736 Node* catch_finally = nullptr; 1737 std::tie(then_finally, catch_finally) = 1738 CreatePromiseFinallyFunctions(on_finally, native_context); 1739 var_then_finally.Bind(then_finally); 1740 var_catch_finally.Bind(catch_finally); 1741 Goto(&perform_finally); 1742 1743 Bind(&if_notcallable); 1744 { 1745 var_then_finally.Bind(on_finally); 1746 var_catch_finally.Bind(on_finally); 1747 Goto(&perform_finally); 1748 } 1749 1750 // 5. Return PerformPromiseThen(promise, valueThunk, undefined, 1751 // promiseCapability). 1752 Bind(&perform_finally); 1753 Label if_nativepromise(this), if_custompromise(this, Label::kDeferred); 1754 BranchIfFastPath(context, promise, &if_nativepromise, &if_custompromise); 1755 1756 Bind(&if_nativepromise); 1757 { 1758 Node* deferred_promise = AllocateAndInitJSPromise(context, promise); 1759 InternalPerformPromiseThen(context, promise, var_then_finally.value(), 1760 var_catch_finally.value(), deferred_promise, 1761 UndefinedConstant(), UndefinedConstant()); 1762 Return(deferred_promise); 1763 } 1764 1765 Bind(&if_custompromise); 1766 { 1767 Isolate* isolate = this->isolate(); 1768 Node* const then_str = HeapConstant(isolate->factory()->then_string()); 1769 Callable getproperty_callable = CodeFactory::GetProperty(isolate); 1770 Node* const then = 1771 CallStub(getproperty_callable, context, promise, then_str); 1772 Callable call_callable = CodeFactory::Call(isolate); 1773 // 5. Return ? Invoke(promise, "then", thenFinally, catchFinally ). 1774 Node* const result = 1775 CallJS(call_callable, context, then, promise, var_then_finally.value(), 1776 var_catch_finally.value()); 1777 Return(result); 1778 } 1779 } 1780 1781 } // namespace internal 1782 } // namespace v8 1783