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