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 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
CreatePromiseContext(Node * native_context,int slots)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
CreatePromiseResolvingFunctionsContext(Node * promise,Node * debug_event,Node * native_context)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
CreatePromiseGetCapabilitiesExecutorContext(Node * promise_capability,Node * native_context)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
ThrowIfNotJSReceiver(Node * context,Node * value,MessageTemplate::Template msg_template,const char * method_name)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
PromiseHasHandler(Node * promise)261 Node* PromiseBuiltinsAssembler::PromiseHasHandler(Node* promise) {
262 Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset);
263 return IsSetWord(SmiUntag(flags), 1 << JSPromise::kHasHandlerBit);
264 }
265
PromiseSetHasHandler(Node * promise)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
PromiseSetHandledHint(Node * promise)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
SpeciesConstructor(Node * context,Node * object,Node * default_constructor)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
AppendPromiseCallback(int offset,Node * promise,Node * value)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
InternalPromiseThen(Node * context,Node * promise,Node * on_resolve,Node * on_reject)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
InternalPerformPromiseThen(Node * context,Node * promise,Node * on_resolve,Node * on_reject,Node * deferred_promise,Node * deferred_on_resolve,Node * deferred_on_reject)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
BranchIfFastPath(Node * context,Node * promise,Label * if_isunmodified,Label * if_ismodified)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
BranchIfFastPath(Node * native_context,Node * promise_fun,Node * promise,Label * if_isunmodified,Label * if_ismodified)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
AllocatePromiseResolveThenableJobInfo(Node * thenable,Node * then,Node * resolve,Node * reject,Node * context)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
InternalResolvePromise(Node * context,Node * promise,Node * result)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
PromiseFulfill(Node * context,Node * promise,Node * result,v8::Promise::PromiseState status)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
BranchIfAccessCheckFailed(Node * context,Node * native_context,Node * promise_constructor,Node * executor,Label * if_noaccess)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
InternalPromiseReject(Node * context,Node * promise,Node * value,Node * debug_event)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
InternalPromiseReject(Node * context,Node * promise,Node * value,bool debug_event)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
TF_BUILTIN(PromiseRejectClosure,PromiseBuiltinsAssembler)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
TF_BUILTIN(PromiseConstructor,PromiseBuiltinsAssembler)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
TF_BUILTIN(PromiseInternalConstructor,PromiseBuiltinsAssembler)1139 TF_BUILTIN(PromiseInternalConstructor, PromiseBuiltinsAssembler) {
1140 Node* const parent = Parameter(1);
1141 Node* const context = Parameter(4);
1142 Return(AllocateAndInitJSPromise(context, parent));
1143 }
1144
TF_BUILTIN(IsPromise,PromiseBuiltinsAssembler)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 )
TF_BUILTIN(PromiseThen,PromiseBuiltinsAssembler)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
TF_BUILTIN(PromiseResolveClosure,PromiseBuiltinsAssembler)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
TF_BUILTIN(ResolvePromise,PromiseBuiltinsAssembler)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
TF_BUILTIN(PromiseHandleReject,PromiseBuiltinsAssembler)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
TF_BUILTIN(PromiseHandle,PromiseBuiltinsAssembler)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 )
TF_BUILTIN(PromiseCatch,PromiseBuiltinsAssembler)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
TF_BUILTIN(PromiseResolve,PromiseBuiltinsAssembler)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
TF_BUILTIN(PromiseGetCapabilitiesExecutor,PromiseBuiltinsAssembler)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
TF_BUILTIN(NewPromiseCapability,PromiseBuiltinsAssembler)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
TF_BUILTIN(PromiseReject,PromiseBuiltinsAssembler)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
TF_BUILTIN(InternalPromiseReject,PromiseBuiltinsAssembler)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
CreatePromiseFinallyContext(Node * on_finally,Node * native_context)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
CreatePromiseFinallyFunctions(Node * on_finally,Node * native_context)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
TF_BUILTIN(PromiseValueThunkFinally,PromiseBuiltinsAssembler)1590 TF_BUILTIN(PromiseValueThunkFinally, PromiseBuiltinsAssembler) {
1591 Node* const context = Parameter(3);
1592
1593 Node* const value = LoadContextElement(context, kOnFinallySlot);
1594 Return(value);
1595 }
1596
CreateValueThunkFunctionContext(Node * value,Node * native_context)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
CreateValueThunkFunction(Node * value,Node * native_context)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
TF_BUILTIN(PromiseThenFinally,PromiseBuiltinsAssembler)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
TF_BUILTIN(PromiseThrowerFinally,PromiseBuiltinsAssembler)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
CreateThrowerFunctionContext(Node * reason,Node * native_context)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
CreateThrowerFunction(Node * reason,Node * native_context)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
TF_BUILTIN(PromiseCatchFinally,PromiseBuiltinsAssembler)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
TF_BUILTIN(PromiseFinally,PromiseBuiltinsAssembler)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