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