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