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-gen.h"
6
7 #include "src/builtins/builtins-constructor-gen.h"
8 #include "src/builtins/builtins-iterator-gen.h"
9 #include "src/builtins/builtins-utils-gen.h"
10 #include "src/builtins/builtins.h"
11 #include "src/code-factory.h"
12 #include "src/code-stub-assembler.h"
13 #include "src/objects-inl.h"
14
15 namespace v8 {
16 namespace internal {
17
18 using compiler::Node;
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 CSA_ASSERT(this, IsFunctionWithPrototypeSlotMap(LoadMap(promise_fun)));
25 Node* const promise_map =
26 LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
27 Node* const promise = Allocate(JSPromise::kSizeWithEmbedderFields);
28 StoreMapNoWriteBarrier(promise, promise_map);
29 StoreObjectFieldRoot(promise, JSPromise::kPropertiesOrHashOffset,
30 Heap::kEmptyFixedArrayRootIndex);
31 StoreObjectFieldRoot(promise, JSPromise::kElementsOffset,
32 Heap::kEmptyFixedArrayRootIndex);
33 return promise;
34 }
35
PromiseInit(Node * promise)36 void PromiseBuiltinsAssembler::PromiseInit(Node* promise) {
37 STATIC_ASSERT(v8::Promise::kPending == 0);
38 StoreObjectFieldNoWriteBarrier(promise, JSPromise::kReactionsOrResultOffset,
39 SmiConstant(Smi::kZero));
40 StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset,
41 SmiConstant(Smi::kZero));
42 for (int i = 0; i < v8::Promise::kEmbedderFieldCount; i++) {
43 int offset = JSPromise::kSize + i * kPointerSize;
44 StoreObjectFieldNoWriteBarrier(promise, offset, SmiConstant(Smi::kZero));
45 }
46 }
47
AllocateAndInitJSPromise(Node * context)48 Node* PromiseBuiltinsAssembler::AllocateAndInitJSPromise(Node* context) {
49 return AllocateAndInitJSPromise(context, UndefinedConstant());
50 }
51
AllocateAndInitJSPromise(Node * context,Node * parent)52 Node* PromiseBuiltinsAssembler::AllocateAndInitJSPromise(Node* context,
53 Node* parent) {
54 Node* const instance = AllocateJSPromise(context);
55 PromiseInit(instance);
56
57 Label out(this);
58 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &out);
59 CallRuntime(Runtime::kPromiseHookInit, context, instance, parent);
60 Goto(&out);
61
62 BIND(&out);
63 return instance;
64 }
65
AllocateAndSetJSPromise(Node * context,v8::Promise::PromiseState status,Node * result)66 Node* PromiseBuiltinsAssembler::AllocateAndSetJSPromise(
67 Node* context, v8::Promise::PromiseState status, Node* result) {
68 DCHECK_NE(Promise::kPending, status);
69
70 Node* const instance = AllocateJSPromise(context);
71 StoreObjectFieldNoWriteBarrier(instance, JSPromise::kReactionsOrResultOffset,
72 result);
73 STATIC_ASSERT(JSPromise::kStatusShift == 0);
74 StoreObjectFieldNoWriteBarrier(instance, JSPromise::kFlagsOffset,
75 SmiConstant(status));
76 for (int i = 0; i < v8::Promise::kEmbedderFieldCount; i++) {
77 int offset = JSPromise::kSize + i * kPointerSize;
78 StoreObjectFieldNoWriteBarrier(instance, offset, SmiConstant(0));
79 }
80
81 Label out(this);
82 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &out);
83 CallRuntime(Runtime::kPromiseHookInit, context, instance,
84 UndefinedConstant());
85 Goto(&out);
86
87 BIND(&out);
88 return instance;
89 }
90
91 std::pair<Node*, Node*>
CreatePromiseResolvingFunctions(Node * promise,Node * debug_event,Node * native_context)92 PromiseBuiltinsAssembler::CreatePromiseResolvingFunctions(
93 Node* promise, Node* debug_event, Node* native_context) {
94 Node* const promise_context = CreatePromiseResolvingFunctionsContext(
95 promise, debug_event, native_context);
96 Node* const map = LoadContextElement(
97 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
98 Node* const resolve_info = LoadContextElement(
99 native_context,
100 Context::PROMISE_CAPABILITY_DEFAULT_RESOLVE_SHARED_FUN_INDEX);
101 Node* const resolve =
102 AllocateFunctionWithMapAndContext(map, resolve_info, promise_context);
103 Node* const reject_info = LoadContextElement(
104 native_context,
105 Context::PROMISE_CAPABILITY_DEFAULT_REJECT_SHARED_FUN_INDEX);
106 Node* const reject =
107 AllocateFunctionWithMapAndContext(map, reject_info, promise_context);
108 return std::make_pair(resolve, reject);
109 }
110
111 // ES #sec-newpromisecapability
TF_BUILTIN(NewPromiseCapability,PromiseBuiltinsAssembler)112 TF_BUILTIN(NewPromiseCapability, PromiseBuiltinsAssembler) {
113 Node* const context = Parameter(Descriptor::kContext);
114 Node* const constructor = Parameter(Descriptor::kConstructor);
115 Node* const debug_event = Parameter(Descriptor::kDebugEvent);
116 Node* const native_context = LoadNativeContext(context);
117
118 Label if_not_constructor(this, Label::kDeferred),
119 if_notcallable(this, Label::kDeferred), if_fast_promise_capability(this),
120 if_slow_promise_capability(this, Label::kDeferred);
121 GotoIf(TaggedIsSmi(constructor), &if_not_constructor);
122 GotoIfNot(IsConstructorMap(LoadMap(constructor)), &if_not_constructor);
123 Branch(WordEqual(constructor,
124 LoadContextElement(native_context,
125 Context::PROMISE_FUNCTION_INDEX)),
126 &if_fast_promise_capability, &if_slow_promise_capability);
127
128 BIND(&if_fast_promise_capability);
129 {
130 Node* promise =
131 AllocateAndInitJSPromise(native_context, UndefinedConstant());
132
133 Node* resolve = nullptr;
134 Node* reject = nullptr;
135 std::tie(resolve, reject) =
136 CreatePromiseResolvingFunctions(promise, debug_event, native_context);
137
138 Node* capability = Allocate(PromiseCapability::kSize);
139 StoreMapNoWriteBarrier(capability, Heap::kPromiseCapabilityMapRootIndex);
140 StoreObjectFieldNoWriteBarrier(capability,
141 PromiseCapability::kPromiseOffset, promise);
142 StoreObjectFieldNoWriteBarrier(capability,
143 PromiseCapability::kResolveOffset, resolve);
144 StoreObjectFieldNoWriteBarrier(capability, PromiseCapability::kRejectOffset,
145 reject);
146 Return(capability);
147 }
148
149 BIND(&if_slow_promise_capability);
150 {
151 Node* capability = Allocate(PromiseCapability::kSize);
152 StoreMapNoWriteBarrier(capability, Heap::kPromiseCapabilityMapRootIndex);
153 StoreObjectFieldRoot(capability, PromiseCapability::kPromiseOffset,
154 Heap::kUndefinedValueRootIndex);
155 StoreObjectFieldRoot(capability, PromiseCapability::kResolveOffset,
156 Heap::kUndefinedValueRootIndex);
157 StoreObjectFieldRoot(capability, PromiseCapability::kRejectOffset,
158 Heap::kUndefinedValueRootIndex);
159
160 Node* executor_context =
161 CreatePromiseGetCapabilitiesExecutorContext(capability, native_context);
162 Node* executor_info = LoadContextElement(
163 native_context, Context::PROMISE_GET_CAPABILITIES_EXECUTOR_SHARED_FUN);
164 Node* function_map = LoadContextElement(
165 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
166 Node* executor = AllocateFunctionWithMapAndContext(
167 function_map, executor_info, executor_context);
168
169 Node* promise = ConstructJS(CodeFactory::Construct(isolate()),
170 native_context, constructor, executor);
171 StoreObjectField(capability, PromiseCapability::kPromiseOffset, promise);
172
173 Node* resolve =
174 LoadObjectField(capability, PromiseCapability::kResolveOffset);
175 GotoIf(TaggedIsSmi(resolve), &if_notcallable);
176 GotoIfNot(IsCallable(resolve), &if_notcallable);
177
178 Node* reject =
179 LoadObjectField(capability, PromiseCapability::kRejectOffset);
180 GotoIf(TaggedIsSmi(reject), &if_notcallable);
181 GotoIfNot(IsCallable(reject), &if_notcallable);
182 Return(capability);
183 }
184
185 BIND(&if_not_constructor);
186 ThrowTypeError(context, MessageTemplate::kNotConstructor, constructor);
187
188 BIND(&if_notcallable);
189 ThrowTypeError(context, MessageTemplate::kPromiseNonCallable);
190 }
191
CreatePromiseContext(Node * native_context,int slots)192 Node* PromiseBuiltinsAssembler::CreatePromiseContext(Node* native_context,
193 int slots) {
194 DCHECK_GE(slots, Context::MIN_CONTEXT_SLOTS);
195
196 Node* const context = AllocateInNewSpace(FixedArray::SizeFor(slots));
197 InitializeFunctionContext(native_context, context, slots);
198 return context;
199 }
200
CreatePromiseAllResolveElementContext(Node * promise_capability,Node * native_context)201 Node* PromiseBuiltinsAssembler::CreatePromiseAllResolveElementContext(
202 Node* promise_capability, Node* native_context) {
203 CSA_ASSERT(this, IsNativeContext(native_context));
204
205 // TODO(bmeurer): Manually fold this into a single allocation.
206 Node* const array_map = LoadContextElement(
207 native_context, Context::JS_ARRAY_PACKED_ELEMENTS_MAP_INDEX);
208 Node* const values_array = AllocateJSArray(PACKED_ELEMENTS, array_map,
209 IntPtrConstant(0), SmiConstant(0));
210
211 Node* const context =
212 CreatePromiseContext(native_context, kPromiseAllResolveElementLength);
213 StoreContextElementNoWriteBarrier(
214 context, kPromiseAllResolveElementRemainingSlot, SmiConstant(1));
215 StoreContextElementNoWriteBarrier(
216 context, kPromiseAllResolveElementCapabilitySlot, promise_capability);
217 StoreContextElementNoWriteBarrier(
218 context, kPromiseAllResolveElementValuesArraySlot, values_array);
219
220 return context;
221 }
222
CreatePromiseAllResolveElementFunction(Node * context,TNode<Smi> index,Node * native_context)223 Node* PromiseBuiltinsAssembler::CreatePromiseAllResolveElementFunction(
224 Node* context, TNode<Smi> index, Node* native_context) {
225 CSA_ASSERT(this, SmiGreaterThan(index, SmiConstant(0)));
226 CSA_ASSERT(this, SmiLessThanOrEqual(
227 index, SmiConstant(PropertyArray::HashField::kMax)));
228 CSA_ASSERT(this, IsNativeContext(native_context));
229
230 Node* const map = LoadContextElement(
231 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
232 Node* const resolve_info = LoadContextElement(
233 native_context, Context::PROMISE_ALL_RESOLVE_ELEMENT_SHARED_FUN);
234 Node* const resolve =
235 AllocateFunctionWithMapAndContext(map, resolve_info, context);
236
237 STATIC_ASSERT(PropertyArray::kNoHashSentinel == 0);
238 StoreObjectFieldNoWriteBarrier(resolve, JSFunction::kPropertiesOrHashOffset,
239 index);
240
241 return resolve;
242 }
243
CreatePromiseResolvingFunctionsContext(Node * promise,Node * debug_event,Node * native_context)244 Node* PromiseBuiltinsAssembler::CreatePromiseResolvingFunctionsContext(
245 Node* promise, Node* debug_event, Node* native_context) {
246 Node* const context =
247 CreatePromiseContext(native_context, kPromiseContextLength);
248 StoreContextElementNoWriteBarrier(context, kPromiseSlot, promise);
249 StoreContextElementNoWriteBarrier(context, kAlreadyResolvedSlot,
250 FalseConstant());
251 StoreContextElementNoWriteBarrier(context, kDebugEventSlot, debug_event);
252 return context;
253 }
254
CreatePromiseGetCapabilitiesExecutorContext(Node * promise_capability,Node * native_context)255 Node* PromiseBuiltinsAssembler::CreatePromiseGetCapabilitiesExecutorContext(
256 Node* promise_capability, Node* native_context) {
257 int kContextLength = kCapabilitiesContextLength;
258 Node* context = CreatePromiseContext(native_context, kContextLength);
259 StoreContextElementNoWriteBarrier(context, kCapabilitySlot,
260 promise_capability);
261 return context;
262 }
263
PromiseHasHandler(Node * promise)264 Node* PromiseBuiltinsAssembler::PromiseHasHandler(Node* promise) {
265 Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset);
266 return IsSetWord(SmiUntag(flags), 1 << JSPromise::kHasHandlerBit);
267 }
268
PromiseSetHasHandler(Node * promise)269 void PromiseBuiltinsAssembler::PromiseSetHasHandler(Node* promise) {
270 TNode<Smi> const flags =
271 CAST(LoadObjectField(promise, JSPromise::kFlagsOffset));
272 TNode<Smi> const new_flags =
273 SmiOr(flags, SmiConstant(1 << JSPromise::kHasHandlerBit));
274 StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset, new_flags);
275 }
276
IsPromiseStatus(Node * actual,v8::Promise::PromiseState expected)277 Node* PromiseBuiltinsAssembler::IsPromiseStatus(
278 Node* actual, v8::Promise::PromiseState expected) {
279 return Word32Equal(actual, Int32Constant(expected));
280 }
281
PromiseStatus(Node * promise)282 Node* PromiseBuiltinsAssembler::PromiseStatus(Node* promise) {
283 STATIC_ASSERT(JSPromise::kStatusShift == 0);
284 TNode<Smi> const flags =
285 CAST(LoadObjectField(promise, JSPromise::kFlagsOffset));
286 return Word32And(SmiToInt32(flags), Int32Constant(JSPromise::kStatusMask));
287 }
288
PromiseSetStatus(Node * promise,v8::Promise::PromiseState const status)289 void PromiseBuiltinsAssembler::PromiseSetStatus(
290 Node* promise, v8::Promise::PromiseState const status) {
291 CSA_ASSERT(this,
292 IsPromiseStatus(PromiseStatus(promise), v8::Promise::kPending));
293 CHECK_NE(status, v8::Promise::kPending);
294
295 TNode<Smi> mask = SmiConstant(status);
296 TNode<Smi> const flags =
297 CAST(LoadObjectField(promise, JSPromise::kFlagsOffset));
298 StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset,
299 SmiOr(flags, mask));
300 }
301
PromiseSetHandledHint(Node * promise)302 void PromiseBuiltinsAssembler::PromiseSetHandledHint(Node* promise) {
303 TNode<Smi> const flags =
304 CAST(LoadObjectField(promise, JSPromise::kFlagsOffset));
305 TNode<Smi> const new_flags =
306 SmiOr(flags, SmiConstant(1 << JSPromise::kHandledHintBit));
307 StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset, new_flags);
308 }
309
310 // ES #sec-performpromisethen
PerformPromiseThen(Node * context,Node * promise,Node * on_fulfilled,Node * on_rejected,Node * result_promise_or_capability)311 void PromiseBuiltinsAssembler::PerformPromiseThen(
312 Node* context, Node* promise, Node* on_fulfilled, Node* on_rejected,
313 Node* result_promise_or_capability) {
314 CSA_ASSERT(this, TaggedIsNotSmi(promise));
315 CSA_ASSERT(this, IsJSPromise(promise));
316 CSA_ASSERT(this,
317 Word32Or(IsCallable(on_fulfilled), IsUndefined(on_fulfilled)));
318 CSA_ASSERT(this, Word32Or(IsCallable(on_rejected), IsUndefined(on_rejected)));
319 CSA_ASSERT(this, TaggedIsNotSmi(result_promise_or_capability));
320 CSA_ASSERT(this, Word32Or(IsJSPromise(result_promise_or_capability),
321 IsPromiseCapability(result_promise_or_capability)));
322
323 Label if_pending(this), if_notpending(this), done(this);
324 Node* const status = PromiseStatus(promise);
325 Branch(IsPromiseStatus(status, v8::Promise::kPending), &if_pending,
326 &if_notpending);
327
328 BIND(&if_pending);
329 {
330 // The {promise} is still in "Pending" state, so we just record a new
331 // PromiseReaction holding both the onFulfilled and onRejected callbacks.
332 // Once the {promise} is resolved we decide on the concrete handler to
333 // push onto the microtask queue.
334 Node* const promise_reactions =
335 LoadObjectField(promise, JSPromise::kReactionsOrResultOffset);
336 Node* const reaction =
337 AllocatePromiseReaction(promise_reactions, result_promise_or_capability,
338 on_fulfilled, on_rejected);
339 StoreObjectField(promise, JSPromise::kReactionsOrResultOffset, reaction);
340 Goto(&done);
341 }
342
343 BIND(&if_notpending);
344 {
345 VARIABLE(var_map, MachineRepresentation::kTagged);
346 VARIABLE(var_handler, MachineRepresentation::kTagged);
347 Label if_fulfilled(this), if_rejected(this, Label::kDeferred),
348 enqueue(this);
349 Branch(IsPromiseStatus(status, v8::Promise::kFulfilled), &if_fulfilled,
350 &if_rejected);
351
352 BIND(&if_fulfilled);
353 {
354 var_map.Bind(LoadRoot(Heap::kPromiseFulfillReactionJobTaskMapRootIndex));
355 var_handler.Bind(on_fulfilled);
356 Goto(&enqueue);
357 }
358
359 BIND(&if_rejected);
360 {
361 CSA_ASSERT(this, IsPromiseStatus(status, v8::Promise::kRejected));
362 var_map.Bind(LoadRoot(Heap::kPromiseRejectReactionJobTaskMapRootIndex));
363 var_handler.Bind(on_rejected);
364 GotoIf(PromiseHasHandler(promise), &enqueue);
365 CallRuntime(Runtime::kPromiseRevokeReject, context, promise);
366 Goto(&enqueue);
367 }
368
369 BIND(&enqueue);
370 Node* argument =
371 LoadObjectField(promise, JSPromise::kReactionsOrResultOffset);
372 Node* microtask = AllocatePromiseReactionJobTask(
373 var_map.value(), context, argument, var_handler.value(),
374 result_promise_or_capability);
375 CallBuiltin(Builtins::kEnqueueMicrotask, NoContextConstant(), microtask);
376 Goto(&done);
377 }
378
379 BIND(&done);
380 PromiseSetHasHandler(promise);
381 }
382
383 // ES #sec-performpromisethen
TF_BUILTIN(PerformPromiseThen,PromiseBuiltinsAssembler)384 TF_BUILTIN(PerformPromiseThen, PromiseBuiltinsAssembler) {
385 Node* const context = Parameter(Descriptor::kContext);
386 Node* const promise = Parameter(Descriptor::kPromise);
387 Node* const on_fulfilled = Parameter(Descriptor::kOnFulfilled);
388 Node* const on_rejected = Parameter(Descriptor::kOnRejected);
389 Node* const result_promise = Parameter(Descriptor::kResultPromise);
390
391 CSA_ASSERT(this, TaggedIsNotSmi(result_promise));
392 CSA_ASSERT(this, IsJSPromise(result_promise));
393
394 PerformPromiseThen(context, promise, on_fulfilled, on_rejected,
395 result_promise);
396 Return(result_promise);
397 }
398
AllocatePromiseReaction(Node * next,Node * promise_or_capability,Node * fulfill_handler,Node * reject_handler)399 Node* PromiseBuiltinsAssembler::AllocatePromiseReaction(
400 Node* next, Node* promise_or_capability, Node* fulfill_handler,
401 Node* reject_handler) {
402 Node* const reaction = Allocate(PromiseReaction::kSize);
403 StoreMapNoWriteBarrier(reaction, Heap::kPromiseReactionMapRootIndex);
404 StoreObjectFieldNoWriteBarrier(reaction, PromiseReaction::kNextOffset, next);
405 StoreObjectFieldNoWriteBarrier(reaction,
406 PromiseReaction::kPromiseOrCapabilityOffset,
407 promise_or_capability);
408 StoreObjectFieldNoWriteBarrier(
409 reaction, PromiseReaction::kFulfillHandlerOffset, fulfill_handler);
410 StoreObjectFieldNoWriteBarrier(
411 reaction, PromiseReaction::kRejectHandlerOffset, reject_handler);
412 return reaction;
413 }
414
AllocatePromiseReactionJobTask(Node * map,Node * context,Node * argument,Node * handler,Node * promise_or_capability)415 Node* PromiseBuiltinsAssembler::AllocatePromiseReactionJobTask(
416 Node* map, Node* context, Node* argument, Node* handler,
417 Node* promise_or_capability) {
418 Node* const microtask = Allocate(PromiseReactionJobTask::kSize);
419 StoreMapNoWriteBarrier(microtask, map);
420 StoreObjectFieldNoWriteBarrier(
421 microtask, PromiseReactionJobTask::kArgumentOffset, argument);
422 StoreObjectFieldNoWriteBarrier(
423 microtask, PromiseReactionJobTask::kContextOffset, context);
424 StoreObjectFieldNoWriteBarrier(
425 microtask, PromiseReactionJobTask::kHandlerOffset, handler);
426 StoreObjectFieldNoWriteBarrier(
427 microtask, PromiseReactionJobTask::kPromiseOrCapabilityOffset,
428 promise_or_capability);
429 return microtask;
430 }
431
AllocatePromiseReactionJobTask(Heap::RootListIndex map_root_index,Node * context,Node * argument,Node * handler,Node * promise_or_capability)432 Node* PromiseBuiltinsAssembler::AllocatePromiseReactionJobTask(
433 Heap::RootListIndex map_root_index, Node* context, Node* argument,
434 Node* handler, Node* promise_or_capability) {
435 DCHECK(map_root_index == Heap::kPromiseFulfillReactionJobTaskMapRootIndex ||
436 map_root_index == Heap::kPromiseRejectReactionJobTaskMapRootIndex);
437 Node* const map = LoadRoot(map_root_index);
438 return AllocatePromiseReactionJobTask(map, context, argument, handler,
439 promise_or_capability);
440 }
441
AllocatePromiseResolveThenableJobTask(Node * promise_to_resolve,Node * then,Node * thenable,Node * context)442 Node* PromiseBuiltinsAssembler::AllocatePromiseResolveThenableJobTask(
443 Node* promise_to_resolve, Node* then, Node* thenable, Node* context) {
444 Node* const microtask = Allocate(PromiseResolveThenableJobTask::kSize);
445 StoreMapNoWriteBarrier(microtask,
446 Heap::kPromiseResolveThenableJobTaskMapRootIndex);
447 StoreObjectFieldNoWriteBarrier(
448 microtask, PromiseResolveThenableJobTask::kContextOffset, context);
449 StoreObjectFieldNoWriteBarrier(
450 microtask, PromiseResolveThenableJobTask::kPromiseToResolveOffset,
451 promise_to_resolve);
452 StoreObjectFieldNoWriteBarrier(
453 microtask, PromiseResolveThenableJobTask::kThenOffset, then);
454 StoreObjectFieldNoWriteBarrier(
455 microtask, PromiseResolveThenableJobTask::kThenableOffset, thenable);
456 return microtask;
457 }
458
459 // ES #sec-triggerpromisereactions
TriggerPromiseReactions(Node * context,Node * reactions,Node * argument,PromiseReaction::Type type)460 Node* PromiseBuiltinsAssembler::TriggerPromiseReactions(
461 Node* context, Node* reactions, Node* argument,
462 PromiseReaction::Type type) {
463 // We need to reverse the {reactions} here, since we record them on the
464 // JSPromise in the reverse order.
465 {
466 VARIABLE(var_current, MachineRepresentation::kTagged, reactions);
467 VARIABLE(var_reversed, MachineRepresentation::kTagged,
468 SmiConstant(Smi::kZero));
469
470 Label loop(this, {&var_current, &var_reversed}), done_loop(this);
471 Goto(&loop);
472 BIND(&loop);
473 {
474 Node* current = var_current.value();
475 GotoIf(TaggedIsSmi(current), &done_loop);
476 var_current.Bind(LoadObjectField(current, PromiseReaction::kNextOffset));
477 StoreObjectField(current, PromiseReaction::kNextOffset,
478 var_reversed.value());
479 var_reversed.Bind(current);
480 Goto(&loop);
481 }
482 BIND(&done_loop);
483 reactions = var_reversed.value();
484 }
485
486 // Morph the {reactions} into PromiseReactionJobTasks and push them
487 // onto the microtask queue.
488 {
489 VARIABLE(var_current, MachineRepresentation::kTagged, reactions);
490
491 Label loop(this, {&var_current}), done_loop(this);
492 Goto(&loop);
493 BIND(&loop);
494 {
495 Node* current = var_current.value();
496 GotoIf(TaggedIsSmi(current), &done_loop);
497 var_current.Bind(LoadObjectField(current, PromiseReaction::kNextOffset));
498
499 // Morph {current} from a PromiseReaction into a PromiseReactionJobTask
500 // and schedule that on the microtask queue. We try to minimize the number
501 // of stores here to avoid screwing up the store buffer.
502 STATIC_ASSERT(PromiseReaction::kSize == PromiseReactionJobTask::kSize);
503 if (type == PromiseReaction::kFulfill) {
504 StoreMapNoWriteBarrier(
505 current, Heap::kPromiseFulfillReactionJobTaskMapRootIndex);
506 StoreObjectField(current, PromiseReactionJobTask::kArgumentOffset,
507 argument);
508 StoreObjectField(current, PromiseReactionJobTask::kContextOffset,
509 context);
510 STATIC_ASSERT(PromiseReaction::kFulfillHandlerOffset ==
511 PromiseReactionJobTask::kHandlerOffset);
512 STATIC_ASSERT(PromiseReaction::kPromiseOrCapabilityOffset ==
513 PromiseReactionJobTask::kPromiseOrCapabilityOffset);
514 } else {
515 Node* handler =
516 LoadObjectField(current, PromiseReaction::kRejectHandlerOffset);
517 StoreMapNoWriteBarrier(current,
518 Heap::kPromiseRejectReactionJobTaskMapRootIndex);
519 StoreObjectField(current, PromiseReactionJobTask::kArgumentOffset,
520 argument);
521 StoreObjectField(current, PromiseReactionJobTask::kContextOffset,
522 context);
523 StoreObjectField(current, PromiseReactionJobTask::kHandlerOffset,
524 handler);
525 STATIC_ASSERT(PromiseReaction::kPromiseOrCapabilityOffset ==
526 PromiseReactionJobTask::kPromiseOrCapabilityOffset);
527 }
528 CallBuiltin(Builtins::kEnqueueMicrotask, NoContextConstant(), current);
529 Goto(&loop);
530 }
531 BIND(&done_loop);
532 }
533
534 return UndefinedConstant();
535 }
536
537 template <typename... TArgs>
InvokeThen(Node * native_context,Node * receiver,TArgs...args)538 Node* PromiseBuiltinsAssembler::InvokeThen(Node* native_context, Node* receiver,
539 TArgs... args) {
540 CSA_ASSERT(this, IsNativeContext(native_context));
541
542 VARIABLE(var_result, MachineRepresentation::kTagged);
543 Label if_fast(this), if_slow(this, Label::kDeferred), done(this, &var_result);
544 GotoIf(TaggedIsSmi(receiver), &if_slow);
545 Node* const receiver_map = LoadMap(receiver);
546 // We can skip the "then" lookup on {receiver} if it's [[Prototype]]
547 // is the (initial) Promise.prototype and the Promise#then protector
548 // is intact, as that guards the lookup path for the "then" property
549 // on JSPromise instances which have the (initial) %PromisePrototype%.
550 BranchIfPromiseThenLookupChainIntact(native_context, receiver_map, &if_fast,
551 &if_slow);
552
553 BIND(&if_fast);
554 {
555 Node* const then =
556 LoadContextElement(native_context, Context::PROMISE_THEN_INDEX);
557 Node* const result =
558 CallJS(CodeFactory::CallFunction(
559 isolate(), ConvertReceiverMode::kNotNullOrUndefined),
560 native_context, then, receiver, args...);
561 var_result.Bind(result);
562 Goto(&done);
563 }
564
565 BIND(&if_slow);
566 {
567 Node* const then = GetProperty(native_context, receiver,
568 isolate()->factory()->then_string());
569 Node* const result = CallJS(
570 CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined),
571 native_context, then, receiver, args...);
572 var_result.Bind(result);
573 Goto(&done);
574 }
575
576 BIND(&done);
577 return var_result.value();
578 }
579
InvokeResolve(Node * native_context,Node * constructor,Node * value,Label * if_exception,Variable * var_exception)580 Node* PromiseBuiltinsAssembler::InvokeResolve(Node* native_context,
581 Node* constructor, Node* value,
582 Label* if_exception,
583 Variable* var_exception) {
584 CSA_ASSERT(this, IsNativeContext(native_context));
585
586 VARIABLE(var_result, MachineRepresentation::kTagged);
587 Label if_fast(this), if_slow(this, Label::kDeferred), done(this, &var_result);
588 // We can skip the "resolve" lookup on {constructor} if it's the
589 // Promise constructor and the Promise.resolve protector is intact,
590 // as that guards the lookup path for the "resolve" property on the
591 // Promise constructor.
592 BranchIfPromiseResolveLookupChainIntact(native_context, constructor, &if_fast,
593 &if_slow);
594
595 BIND(&if_fast);
596 {
597 Node* const result = CallBuiltin(Builtins::kPromiseResolve, native_context,
598 constructor, value);
599 GotoIfException(result, if_exception, var_exception);
600
601 var_result.Bind(result);
602 Goto(&done);
603 }
604
605 BIND(&if_slow);
606 {
607 Node* const resolve =
608 GetProperty(native_context, constructor, factory()->resolve_string());
609 GotoIfException(resolve, if_exception, var_exception);
610
611 Node* const result = CallJS(
612 CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined),
613 native_context, resolve, constructor, value);
614 GotoIfException(result, if_exception, var_exception);
615
616 var_result.Bind(result);
617 Goto(&done);
618 }
619
620 BIND(&done);
621 return var_result.value();
622 }
623
BranchIfPromiseResolveLookupChainIntact(Node * native_context,Node * constructor,Label * if_fast,Label * if_slow)624 void PromiseBuiltinsAssembler::BranchIfPromiseResolveLookupChainIntact(
625 Node* native_context, Node* constructor, Label* if_fast, Label* if_slow) {
626 CSA_ASSERT(this, IsNativeContext(native_context));
627
628 GotoIfForceSlowPath(if_slow);
629 Node* const promise_fun =
630 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
631 GotoIfNot(WordEqual(promise_fun, constructor), if_slow);
632 Branch(IsPromiseResolveProtectorCellInvalid(), if_slow, if_fast);
633 }
634
BranchIfPromiseSpeciesLookupChainIntact(Node * native_context,Node * promise_map,Label * if_fast,Label * if_slow)635 void PromiseBuiltinsAssembler::BranchIfPromiseSpeciesLookupChainIntact(
636 Node* native_context, Node* promise_map, Label* if_fast, Label* if_slow) {
637 CSA_ASSERT(this, IsNativeContext(native_context));
638 CSA_ASSERT(this, IsJSPromiseMap(promise_map));
639
640 Node* const promise_prototype =
641 LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
642 GotoIfForceSlowPath(if_slow);
643 GotoIfNot(WordEqual(LoadMapPrototype(promise_map), promise_prototype),
644 if_slow);
645 Branch(IsPromiseSpeciesProtectorCellInvalid(), if_slow, if_fast);
646 }
647
BranchIfPromiseThenLookupChainIntact(Node * native_context,Node * receiver_map,Label * if_fast,Label * if_slow)648 void PromiseBuiltinsAssembler::BranchIfPromiseThenLookupChainIntact(
649 Node* native_context, Node* receiver_map, Label* if_fast, Label* if_slow) {
650 CSA_ASSERT(this, IsMap(receiver_map));
651 CSA_ASSERT(this, IsNativeContext(native_context));
652
653 GotoIfForceSlowPath(if_slow);
654 GotoIfNot(IsJSPromiseMap(receiver_map), if_slow);
655 Node* const promise_prototype =
656 LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
657 GotoIfNot(WordEqual(LoadMapPrototype(receiver_map), promise_prototype),
658 if_slow);
659 Branch(IsPromiseThenProtectorCellInvalid(), if_slow, if_fast);
660 }
661
BranchIfAccessCheckFailed(Node * context,Node * native_context,Node * promise_constructor,Node * executor,Label * if_noaccess)662 void PromiseBuiltinsAssembler::BranchIfAccessCheckFailed(
663 Node* context, Node* native_context, Node* promise_constructor,
664 Node* executor, Label* if_noaccess) {
665 VARIABLE(var_executor, MachineRepresentation::kTagged);
666 var_executor.Bind(executor);
667 Label has_access(this), call_runtime(this, Label::kDeferred);
668
669 // If executor is a bound function, load the bound function until we've
670 // reached an actual function.
671 Label found_function(this), loop_over_bound_function(this, &var_executor);
672 Goto(&loop_over_bound_function);
673 BIND(&loop_over_bound_function);
674 {
675 Node* executor_type = LoadInstanceType(var_executor.value());
676 GotoIf(InstanceTypeEqual(executor_type, JS_FUNCTION_TYPE), &found_function);
677 GotoIfNot(InstanceTypeEqual(executor_type, JS_BOUND_FUNCTION_TYPE),
678 &call_runtime);
679 var_executor.Bind(LoadObjectField(
680 var_executor.value(), JSBoundFunction::kBoundTargetFunctionOffset));
681 Goto(&loop_over_bound_function);
682 }
683
684 // Load the context from the function and compare it to the Promise
685 // constructor's context. If they match, everything is fine, otherwise, bail
686 // out to the runtime.
687 BIND(&found_function);
688 {
689 Node* function_context =
690 LoadObjectField(var_executor.value(), JSFunction::kContextOffset);
691 Node* native_function_context = LoadNativeContext(function_context);
692 Branch(WordEqual(native_context, native_function_context), &has_access,
693 &call_runtime);
694 }
695
696 BIND(&call_runtime);
697 {
698 Branch(WordEqual(CallRuntime(Runtime::kAllowDynamicFunction, context,
699 promise_constructor),
700 TrueConstant()),
701 &has_access, if_noaccess);
702 }
703
704 BIND(&has_access);
705 }
706
SetForwardingHandlerIfTrue(Node * context,Node * condition,const NodeGenerator & object)707 void PromiseBuiltinsAssembler::SetForwardingHandlerIfTrue(
708 Node* context, Node* condition, const NodeGenerator& object) {
709 Label done(this);
710 GotoIfNot(condition, &done);
711 CallRuntime(Runtime::kSetProperty, context, object(),
712 HeapConstant(factory()->promise_forwarding_handler_symbol()),
713 TrueConstant(), SmiConstant(LanguageMode::kStrict));
714 Goto(&done);
715 BIND(&done);
716 }
717
SetPromiseHandledByIfTrue(Node * context,Node * condition,Node * promise,const NodeGenerator & handled_by)718 void PromiseBuiltinsAssembler::SetPromiseHandledByIfTrue(
719 Node* context, Node* condition, Node* promise,
720 const NodeGenerator& handled_by) {
721 Label done(this);
722 GotoIfNot(condition, &done);
723 GotoIf(TaggedIsSmi(promise), &done);
724 GotoIfNot(HasInstanceType(promise, JS_PROMISE_TYPE), &done);
725 CallRuntime(Runtime::kSetProperty, context, promise,
726 HeapConstant(factory()->promise_handled_by_symbol()),
727 handled_by(), SmiConstant(LanguageMode::kStrict));
728 Goto(&done);
729 BIND(&done);
730 }
731
732 // ES #sec-promise-reject-functions
TF_BUILTIN(PromiseCapabilityDefaultReject,PromiseBuiltinsAssembler)733 TF_BUILTIN(PromiseCapabilityDefaultReject, PromiseBuiltinsAssembler) {
734 Node* const reason = Parameter(Descriptor::kReason);
735 Node* const context = Parameter(Descriptor::kContext);
736
737 // 2. Let promise be F.[[Promise]].
738 Node* const promise = LoadContextElement(context, kPromiseSlot);
739
740 // 3. Let alreadyResolved be F.[[AlreadyResolved]].
741 Label if_already_resolved(this, Label::kDeferred);
742 Node* const already_resolved =
743 LoadContextElement(context, kAlreadyResolvedSlot);
744
745 // 4. If alreadyResolved.[[Value]] is true, return undefined.
746 GotoIf(IsTrue(already_resolved), &if_already_resolved);
747
748 // 5. Set alreadyResolved.[[Value]] to true.
749 StoreContextElementNoWriteBarrier(context, kAlreadyResolvedSlot,
750 TrueConstant());
751
752 // 6. Return RejectPromise(promise, reason).
753 Node* const debug_event = LoadContextElement(context, kDebugEventSlot);
754 Return(CallBuiltin(Builtins::kRejectPromise, context, promise, reason,
755 debug_event));
756
757 BIND(&if_already_resolved);
758 {
759 Return(CallRuntime(Runtime::kPromiseRejectAfterResolved, context, promise,
760 reason));
761 }
762 }
763
764 // ES #sec-promise-resolve-functions
TF_BUILTIN(PromiseCapabilityDefaultResolve,PromiseBuiltinsAssembler)765 TF_BUILTIN(PromiseCapabilityDefaultResolve, PromiseBuiltinsAssembler) {
766 Node* const resolution = Parameter(Descriptor::kResolution);
767 Node* const context = Parameter(Descriptor::kContext);
768
769 // 2. Let promise be F.[[Promise]].
770 Node* const promise = LoadContextElement(context, kPromiseSlot);
771
772 // 3. Let alreadyResolved be F.[[AlreadyResolved]].
773 Label if_already_resolved(this, Label::kDeferred);
774 Node* const already_resolved =
775 LoadContextElement(context, kAlreadyResolvedSlot);
776
777 // 4. If alreadyResolved.[[Value]] is true, return undefined.
778 GotoIf(IsTrue(already_resolved), &if_already_resolved);
779
780 // 5. Set alreadyResolved.[[Value]] to true.
781 StoreContextElementNoWriteBarrier(context, kAlreadyResolvedSlot,
782 TrueConstant());
783
784 // The rest of the logic (and the catch prediction) is
785 // encapsulated in the dedicated ResolvePromise builtin.
786 Return(CallBuiltin(Builtins::kResolvePromise, context, promise, resolution));
787
788 BIND(&if_already_resolved);
789 {
790 Return(CallRuntime(Runtime::kPromiseResolveAfterResolved, context, promise,
791 resolution));
792 }
793 }
794
TF_BUILTIN(PromiseConstructorLazyDeoptContinuation,PromiseBuiltinsAssembler)795 TF_BUILTIN(PromiseConstructorLazyDeoptContinuation, PromiseBuiltinsAssembler) {
796 Node* promise = Parameter(Descriptor::kPromise);
797 Node* reject = Parameter(Descriptor::kReject);
798 Node* exception = Parameter(Descriptor::kException);
799 Node* const context = Parameter(Descriptor::kContext);
800
801 Label finally(this);
802
803 GotoIf(IsTheHole(exception), &finally);
804 CallJS(CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined),
805 context, reject, UndefinedConstant(), exception);
806 Goto(&finally);
807
808 BIND(&finally);
809 Return(promise);
810 }
811
812 // ES6 #sec-promise-executor
TF_BUILTIN(PromiseConstructor,PromiseBuiltinsAssembler)813 TF_BUILTIN(PromiseConstructor, PromiseBuiltinsAssembler) {
814 Node* const executor = Parameter(Descriptor::kExecutor);
815 Node* const new_target = Parameter(Descriptor::kNewTarget);
816 Node* const context = Parameter(Descriptor::kContext);
817 Isolate* isolate = this->isolate();
818
819 Label if_targetisundefined(this, Label::kDeferred);
820
821 GotoIf(IsUndefined(new_target), &if_targetisundefined);
822
823 Label if_notcallable(this, Label::kDeferred);
824
825 GotoIf(TaggedIsSmi(executor), &if_notcallable);
826
827 Node* const executor_map = LoadMap(executor);
828 GotoIfNot(IsCallableMap(executor_map), &if_notcallable);
829
830 Node* const native_context = LoadNativeContext(context);
831 Node* const promise_fun =
832 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
833 Node* const is_debug_active = IsDebugActive();
834 Label if_targetisnotmodified(this),
835 if_targetismodified(this, Label::kDeferred), run_executor(this),
836 debug_push(this), if_noaccess(this, Label::kDeferred);
837
838 BranchIfAccessCheckFailed(context, native_context, promise_fun, executor,
839 &if_noaccess);
840
841 Branch(WordEqual(promise_fun, new_target), &if_targetisnotmodified,
842 &if_targetismodified);
843
844 VARIABLE(var_result, MachineRepresentation::kTagged);
845 VARIABLE(var_reject_call, MachineRepresentation::kTagged);
846 VARIABLE(var_reason, MachineRepresentation::kTagged);
847
848 BIND(&if_targetisnotmodified);
849 {
850 Node* const instance = AllocateAndInitJSPromise(context);
851 var_result.Bind(instance);
852 Goto(&debug_push);
853 }
854
855 BIND(&if_targetismodified);
856 {
857 ConstructorBuiltinsAssembler constructor_assembler(this->state());
858 Node* const instance = constructor_assembler.EmitFastNewObject(
859 context, promise_fun, new_target);
860 PromiseInit(instance);
861 var_result.Bind(instance);
862
863 GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &debug_push);
864 CallRuntime(Runtime::kPromiseHookInit, context, instance,
865 UndefinedConstant());
866 Goto(&debug_push);
867 }
868
869 BIND(&debug_push);
870 {
871 GotoIfNot(is_debug_active, &run_executor);
872 CallRuntime(Runtime::kDebugPushPromise, context, var_result.value());
873 Goto(&run_executor);
874 }
875
876 BIND(&run_executor);
877 {
878 Label out(this), if_rejectpromise(this), debug_pop(this, Label::kDeferred);
879
880 Node *resolve, *reject;
881 std::tie(resolve, reject) = CreatePromiseResolvingFunctions(
882 var_result.value(), TrueConstant(), native_context);
883
884 Node* const maybe_exception = CallJS(
885 CodeFactory::Call(isolate, ConvertReceiverMode::kNullOrUndefined),
886 context, executor, UndefinedConstant(), resolve, reject);
887
888 GotoIfException(maybe_exception, &if_rejectpromise, &var_reason);
889 Branch(is_debug_active, &debug_pop, &out);
890
891 BIND(&if_rejectpromise);
892 {
893 CallJS(CodeFactory::Call(isolate, ConvertReceiverMode::kNullOrUndefined),
894 context, reject, UndefinedConstant(), var_reason.value());
895 Branch(is_debug_active, &debug_pop, &out);
896 }
897
898 BIND(&debug_pop);
899 {
900 CallRuntime(Runtime::kDebugPopPromise, context);
901 Goto(&out);
902 }
903 BIND(&out);
904 Return(var_result.value());
905 }
906
907 // 1. If NewTarget is undefined, throw a TypeError exception.
908 BIND(&if_targetisundefined);
909 ThrowTypeError(context, MessageTemplate::kNotAPromise, new_target);
910
911 // 2. If IsCallable(executor) is false, throw a TypeError exception.
912 BIND(&if_notcallable);
913 ThrowTypeError(context, MessageTemplate::kResolverNotAFunction, executor);
914
915 // Silently fail if the stack looks fishy.
916 BIND(&if_noaccess);
917 {
918 Node* const counter_id =
919 SmiConstant(v8::Isolate::kPromiseConstructorReturnedUndefined);
920 CallRuntime(Runtime::kIncrementUseCounter, context, counter_id);
921 Return(UndefinedConstant());
922 }
923 }
924
925 // V8 Extras: v8.createPromise(parent)
TF_BUILTIN(PromiseInternalConstructor,PromiseBuiltinsAssembler)926 TF_BUILTIN(PromiseInternalConstructor, PromiseBuiltinsAssembler) {
927 Node* const parent = Parameter(Descriptor::kParent);
928 Node* const context = Parameter(Descriptor::kContext);
929 Return(AllocateAndInitJSPromise(context, parent));
930 }
931
932 // V8 Extras: v8.rejectPromise(promise, reason)
TF_BUILTIN(PromiseInternalReject,PromiseBuiltinsAssembler)933 TF_BUILTIN(PromiseInternalReject, PromiseBuiltinsAssembler) {
934 Node* const promise = Parameter(Descriptor::kPromise);
935 Node* const reason = Parameter(Descriptor::kReason);
936 Node* const context = Parameter(Descriptor::kContext);
937 // We pass true to trigger the debugger's on exception handler.
938 Return(CallBuiltin(Builtins::kRejectPromise, context, promise, reason,
939 TrueConstant()));
940 }
941
942 // V8 Extras: v8.resolvePromise(promise, resolution)
TF_BUILTIN(PromiseInternalResolve,PromiseBuiltinsAssembler)943 TF_BUILTIN(PromiseInternalResolve, PromiseBuiltinsAssembler) {
944 Node* const promise = Parameter(Descriptor::kPromise);
945 Node* const resolution = Parameter(Descriptor::kResolution);
946 Node* const context = Parameter(Descriptor::kContext);
947 Return(CallBuiltin(Builtins::kResolvePromise, context, promise, resolution));
948 }
949
950 // ES#sec-promise.prototype.then
951 // Promise.prototype.then ( onFulfilled, onRejected )
TF_BUILTIN(PromisePrototypeThen,PromiseBuiltinsAssembler)952 TF_BUILTIN(PromisePrototypeThen, PromiseBuiltinsAssembler) {
953 // 1. Let promise be the this value.
954 Node* const promise = Parameter(Descriptor::kReceiver);
955 Node* const on_fulfilled = Parameter(Descriptor::kOnFulfilled);
956 Node* const on_rejected = Parameter(Descriptor::kOnRejected);
957 Node* const context = Parameter(Descriptor::kContext);
958
959 // 2. If IsPromise(promise) is false, throw a TypeError exception.
960 ThrowIfNotInstanceType(context, promise, JS_PROMISE_TYPE,
961 "Promise.prototype.then");
962
963 // 3. Let C be ? SpeciesConstructor(promise, %Promise%).
964 Label fast_promise_capability(this), slow_constructor(this, Label::kDeferred),
965 slow_promise_capability(this, Label::kDeferred);
966 Node* const native_context = LoadNativeContext(context);
967 Node* const promise_fun =
968 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
969 Node* const promise_map = LoadMap(promise);
970 BranchIfPromiseSpeciesLookupChainIntact(
971 native_context, promise_map, &fast_promise_capability, &slow_constructor);
972
973 BIND(&slow_constructor);
974 Node* const constructor =
975 SpeciesConstructor(native_context, promise, promise_fun);
976 Branch(WordEqual(constructor, promise_fun), &fast_promise_capability,
977 &slow_promise_capability);
978
979 // 4. Let resultCapability be ? NewPromiseCapability(C).
980 Label perform_promise_then(this);
981 VARIABLE(var_result_promise, MachineRepresentation::kTagged);
982 VARIABLE(var_result_promise_or_capability, MachineRepresentation::kTagged);
983
984 BIND(&fast_promise_capability);
985 {
986 Node* const result_promise = AllocateAndInitJSPromise(context, promise);
987 var_result_promise_or_capability.Bind(result_promise);
988 var_result_promise.Bind(result_promise);
989 Goto(&perform_promise_then);
990 }
991
992 BIND(&slow_promise_capability);
993 {
994 Node* const debug_event = TrueConstant();
995 Node* const capability = CallBuiltin(Builtins::kNewPromiseCapability,
996 context, constructor, debug_event);
997 var_result_promise.Bind(
998 LoadObjectField(capability, PromiseCapability::kPromiseOffset));
999 var_result_promise_or_capability.Bind(capability);
1000 Goto(&perform_promise_then);
1001 }
1002
1003 // 5. Return PerformPromiseThen(promise, onFulfilled, onRejected,
1004 // resultCapability).
1005 BIND(&perform_promise_then);
1006 {
1007 // We do some work of the PerformPromiseThen operation here, in that
1008 // we check the handlers and turn non-callable handlers into undefined.
1009 // This is because this is the one and only callsite of PerformPromiseThen
1010 // that has to do this.
1011
1012 // 3. If IsCallable(onFulfilled) is false, then
1013 // a. Set onFulfilled to undefined.
1014 VARIABLE(var_on_fulfilled, MachineRepresentation::kTagged, on_fulfilled);
1015 Label if_fulfilled_done(this), if_fulfilled_notcallable(this);
1016 GotoIf(TaggedIsSmi(on_fulfilled), &if_fulfilled_notcallable);
1017 Branch(IsCallable(on_fulfilled), &if_fulfilled_done,
1018 &if_fulfilled_notcallable);
1019 BIND(&if_fulfilled_notcallable);
1020 var_on_fulfilled.Bind(UndefinedConstant());
1021 Goto(&if_fulfilled_done);
1022 BIND(&if_fulfilled_done);
1023
1024 // 4. If IsCallable(onRejected) is false, then
1025 // a. Set onRejected to undefined.
1026 VARIABLE(var_on_rejected, MachineRepresentation::kTagged, on_rejected);
1027 Label if_rejected_done(this), if_rejected_notcallable(this);
1028 GotoIf(TaggedIsSmi(on_rejected), &if_rejected_notcallable);
1029 Branch(IsCallable(on_rejected), &if_rejected_done,
1030 &if_rejected_notcallable);
1031 BIND(&if_rejected_notcallable);
1032 var_on_rejected.Bind(UndefinedConstant());
1033 Goto(&if_rejected_done);
1034 BIND(&if_rejected_done);
1035
1036 PerformPromiseThen(context, promise, var_on_fulfilled.value(),
1037 var_on_rejected.value(),
1038 var_result_promise_or_capability.value());
1039 Return(var_result_promise.value());
1040 }
1041 }
1042
1043 // ES#sec-promise.prototype.catch
1044 // Promise.prototype.catch ( onRejected )
TF_BUILTIN(PromisePrototypeCatch,PromiseBuiltinsAssembler)1045 TF_BUILTIN(PromisePrototypeCatch, PromiseBuiltinsAssembler) {
1046 // 1. Let promise be the this value.
1047 Node* const receiver = Parameter(Descriptor::kReceiver);
1048 Node* const on_fulfilled = UndefinedConstant();
1049 Node* const on_rejected = Parameter(Descriptor::kOnRejected);
1050 Node* const context = Parameter(Descriptor::kContext);
1051
1052 // 2. Return ? Invoke(promise, "then", « undefined, onRejected »).
1053 Node* const native_context = LoadNativeContext(context);
1054 Return(InvokeThen(native_context, receiver, on_fulfilled, on_rejected));
1055 }
1056
1057 // ES #sec-promiseresolvethenablejob
TF_BUILTIN(PromiseResolveThenableJob,PromiseBuiltinsAssembler)1058 TF_BUILTIN(PromiseResolveThenableJob, PromiseBuiltinsAssembler) {
1059 Node* const native_context = Parameter(Descriptor::kContext);
1060 Node* const promise_to_resolve = Parameter(Descriptor::kPromiseToResolve);
1061 Node* const thenable = Parameter(Descriptor::kThenable);
1062 Node* const then = Parameter(Descriptor::kThen);
1063
1064 CSA_ASSERT(this, TaggedIsNotSmi(thenable));
1065 CSA_ASSERT(this, IsJSReceiver(thenable));
1066 CSA_ASSERT(this, IsJSPromise(promise_to_resolve));
1067 CSA_ASSERT(this, IsNativeContext(native_context));
1068
1069 // We can use a simple optimization here if we know that {then} is the initial
1070 // Promise.prototype.then method, and {thenable} is a JSPromise whose
1071 // @@species lookup chain is intact: We can connect {thenable} and
1072 // {promise_to_resolve} directly in that case and avoid the allocation of a
1073 // temporary JSPromise and the closures plus context.
1074 //
1075 // We take the generic (slow-)path if a PromiseHook is enabled or the debugger
1076 // is active, to make sure we expose spec compliant behavior.
1077 Label if_fast(this), if_slow(this, Label::kDeferred);
1078 Node* const promise_then =
1079 LoadContextElement(native_context, Context::PROMISE_THEN_INDEX);
1080 GotoIfNot(WordEqual(then, promise_then), &if_slow);
1081 Node* const thenable_map = LoadMap(thenable);
1082 GotoIfNot(IsJSPromiseMap(thenable_map), &if_slow);
1083 GotoIf(IsPromiseHookEnabledOrDebugIsActive(), &if_slow);
1084 BranchIfPromiseSpeciesLookupChainIntact(native_context, thenable_map,
1085 &if_fast, &if_slow);
1086
1087 BIND(&if_fast);
1088 {
1089 // We know that the {thenable} is a JSPromise, which doesn't require
1090 // any special treatment and that {then} corresponds to the initial
1091 // Promise.prototype.then method. So instead of allocating a temporary
1092 // JSPromise to connect the {thenable} with the {promise_to_resolve},
1093 // we can directly schedule the {promise_to_resolve} with default
1094 // handlers onto the {thenable} promise. This does not only save the
1095 // JSPromise allocation, but also avoids the allocation of the two
1096 // resolving closures and the shared context.
1097 //
1098 // What happens normally in this case is
1099 //
1100 // resolve, reject = CreateResolvingFunctions(promise_to_resolve)
1101 // result_capability = NewPromiseCapability(%Promise%)
1102 // PerformPromiseThen(thenable, resolve, reject, result_capability)
1103 //
1104 // which means that PerformPromiseThen will either schedule a new
1105 // PromiseReaction with resolve and reject or a PromiseReactionJob
1106 // with resolve or reject based on the state of {thenable}. And
1107 // resolve or reject will just invoke the default [[Resolve]] or
1108 // [[Reject]] functions on the {promise_to_resolve}.
1109 //
1110 // This is the same as just doing
1111 //
1112 // PerformPromiseThen(thenable, undefined, undefined, promise_to_resolve)
1113 //
1114 // which performs exactly the same (observable) steps.
1115 TailCallBuiltin(Builtins::kPerformPromiseThen, native_context, thenable,
1116 UndefinedConstant(), UndefinedConstant(),
1117 promise_to_resolve);
1118 }
1119
1120 BIND(&if_slow);
1121 {
1122 Node* resolve = nullptr;
1123 Node* reject = nullptr;
1124 std::tie(resolve, reject) = CreatePromiseResolvingFunctions(
1125 promise_to_resolve, FalseConstant(), native_context);
1126
1127 Label if_exception(this, Label::kDeferred);
1128 VARIABLE(var_exception, MachineRepresentation::kTagged, TheHoleConstant());
1129 Node* const result = CallJS(
1130 CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined),
1131 native_context, then, thenable, resolve, reject);
1132 GotoIfException(result, &if_exception, &var_exception);
1133 Return(result);
1134
1135 BIND(&if_exception);
1136 {
1137 // We need to reject the {thenable}.
1138 Node* const result = CallJS(
1139 CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
1140 native_context, reject, UndefinedConstant(), var_exception.value());
1141 Return(result);
1142 }
1143 }
1144 }
1145
1146 // ES #sec-promisereactionjob
PromiseReactionJob(Node * context,Node * argument,Node * handler,Node * promise_or_capability,PromiseReaction::Type type)1147 void PromiseBuiltinsAssembler::PromiseReactionJob(Node* context, Node* argument,
1148 Node* handler,
1149 Node* promise_or_capability,
1150 PromiseReaction::Type type) {
1151 CSA_ASSERT(this, TaggedIsNotSmi(handler));
1152 CSA_ASSERT(this, Word32Or(IsUndefined(handler), IsCallable(handler)));
1153 CSA_ASSERT(this, TaggedIsNotSmi(promise_or_capability));
1154 CSA_ASSERT(this, Word32Or(IsJSPromise(promise_or_capability),
1155 IsPromiseCapability(promise_or_capability)));
1156
1157 VARIABLE(var_handler_result, MachineRepresentation::kTagged, argument);
1158 Label if_handler_callable(this), if_fulfill(this), if_reject(this);
1159 Branch(IsUndefined(handler),
1160 type == PromiseReaction::kFulfill ? &if_fulfill : &if_reject,
1161 &if_handler_callable);
1162
1163 BIND(&if_handler_callable);
1164 {
1165 Node* const result = CallJS(
1166 CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
1167 context, handler, UndefinedConstant(), argument);
1168 GotoIfException(result, &if_reject, &var_handler_result);
1169 var_handler_result.Bind(result);
1170 Goto(&if_fulfill);
1171 }
1172
1173 BIND(&if_fulfill);
1174 {
1175 Label if_promise(this), if_promise_capability(this, Label::kDeferred);
1176 Node* const value = var_handler_result.value();
1177 Branch(IsPromiseCapability(promise_or_capability), &if_promise_capability,
1178 &if_promise);
1179
1180 BIND(&if_promise);
1181 {
1182 // For fast native promises we can skip the indirection
1183 // via the promiseCapability.[[Resolve]] function and
1184 // run the resolve logic directly from here.
1185 TailCallBuiltin(Builtins::kResolvePromise, context, promise_or_capability,
1186 value);
1187 }
1188
1189 BIND(&if_promise_capability);
1190 {
1191 // In the general case we need to call the (user provided)
1192 // promiseCapability.[[Resolve]] function.
1193 Node* const resolve = LoadObjectField(promise_or_capability,
1194 PromiseCapability::kResolveOffset);
1195 Node* const result = CallJS(
1196 CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
1197 context, resolve, UndefinedConstant(), value);
1198 GotoIfException(result, &if_reject, &var_handler_result);
1199 Return(result);
1200 }
1201 }
1202
1203 BIND(&if_reject);
1204 if (type == PromiseReaction::kReject) {
1205 Label if_promise(this), if_promise_capability(this, Label::kDeferred);
1206 Node* const reason = var_handler_result.value();
1207 Branch(IsPromiseCapability(promise_or_capability), &if_promise_capability,
1208 &if_promise);
1209
1210 BIND(&if_promise);
1211 {
1212 // For fast native promises we can skip the indirection
1213 // via the promiseCapability.[[Reject]] function and
1214 // run the resolve logic directly from here.
1215 TailCallBuiltin(Builtins::kRejectPromise, context, promise_or_capability,
1216 reason, FalseConstant());
1217 }
1218
1219 BIND(&if_promise_capability);
1220 {
1221 // In the general case we need to call the (user provided)
1222 // promiseCapability.[[Reject]] function.
1223 Label if_exception(this, Label::kDeferred);
1224 VARIABLE(var_exception, MachineRepresentation::kTagged,
1225 TheHoleConstant());
1226 Node* const reject = LoadObjectField(promise_or_capability,
1227 PromiseCapability::kRejectOffset);
1228 Node* const result = CallJS(
1229 CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
1230 context, reject, UndefinedConstant(), reason);
1231 GotoIfException(result, &if_exception, &var_exception);
1232 Return(result);
1233
1234 // Swallow the exception here.
1235 BIND(&if_exception);
1236 TailCallRuntime(Runtime::kReportMessage, context, var_exception.value());
1237 }
1238 } else {
1239 // We have to call out to the dedicated PromiseRejectReactionJob builtin
1240 // here, instead of just doing the work inline, as otherwise the catch
1241 // predictions in the debugger will be wrong, which just walks the stack
1242 // and checks for certain builtins.
1243 TailCallBuiltin(Builtins::kPromiseRejectReactionJob, context,
1244 var_handler_result.value(), UndefinedConstant(),
1245 promise_or_capability);
1246 }
1247 }
1248
1249 // ES #sec-promisereactionjob
TF_BUILTIN(PromiseFulfillReactionJob,PromiseBuiltinsAssembler)1250 TF_BUILTIN(PromiseFulfillReactionJob, PromiseBuiltinsAssembler) {
1251 Node* const context = Parameter(Descriptor::kContext);
1252 Node* const value = Parameter(Descriptor::kValue);
1253 Node* const handler = Parameter(Descriptor::kHandler);
1254 Node* const promise_or_capability =
1255 Parameter(Descriptor::kPromiseOrCapability);
1256
1257 PromiseReactionJob(context, value, handler, promise_or_capability,
1258 PromiseReaction::kFulfill);
1259 }
1260
1261 // ES #sec-promisereactionjob
TF_BUILTIN(PromiseRejectReactionJob,PromiseBuiltinsAssembler)1262 TF_BUILTIN(PromiseRejectReactionJob, PromiseBuiltinsAssembler) {
1263 Node* const context = Parameter(Descriptor::kContext);
1264 Node* const reason = Parameter(Descriptor::kReason);
1265 Node* const handler = Parameter(Descriptor::kHandler);
1266 Node* const promise_or_capability =
1267 Parameter(Descriptor::kPromiseOrCapability);
1268
1269 PromiseReactionJob(context, reason, handler, promise_or_capability,
1270 PromiseReaction::kReject);
1271 }
1272
TF_BUILTIN(PromiseResolveTrampoline,PromiseBuiltinsAssembler)1273 TF_BUILTIN(PromiseResolveTrampoline, PromiseBuiltinsAssembler) {
1274 // 1. Let C be the this value.
1275 Node* receiver = Parameter(Descriptor::kReceiver);
1276 Node* value = Parameter(Descriptor::kValue);
1277 Node* context = Parameter(Descriptor::kContext);
1278
1279 // 2. If Type(C) is not Object, throw a TypeError exception.
1280 ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject,
1281 "PromiseResolve");
1282
1283 // 3. Return ? PromiseResolve(C, x).
1284 Return(CallBuiltin(Builtins::kPromiseResolve, context, receiver, value));
1285 }
1286
TF_BUILTIN(PromiseResolve,PromiseBuiltinsAssembler)1287 TF_BUILTIN(PromiseResolve, PromiseBuiltinsAssembler) {
1288 Node* constructor = Parameter(Descriptor::kConstructor);
1289 Node* value = Parameter(Descriptor::kValue);
1290 Node* context = Parameter(Descriptor::kContext);
1291
1292 CSA_ASSERT(this, IsJSReceiver(constructor));
1293
1294 Node* const native_context = LoadNativeContext(context);
1295 Node* const promise_fun =
1296 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
1297
1298 Label if_slow_constructor(this, Label::kDeferred), if_need_to_allocate(this);
1299
1300 // Check if {value} is a JSPromise.
1301 GotoIf(TaggedIsSmi(value), &if_need_to_allocate);
1302 Node* const value_map = LoadMap(value);
1303 GotoIfNot(IsJSPromiseMap(value_map), &if_need_to_allocate);
1304
1305 // We can skip the "constructor" lookup on {value} if it's [[Prototype]]
1306 // is the (initial) Promise.prototype and the @@species protector is
1307 // intact, as that guards the lookup path for "constructor" on
1308 // JSPromise instances which have the (initial) Promise.prototype.
1309 Node* const promise_prototype =
1310 LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
1311 GotoIfNot(WordEqual(LoadMapPrototype(value_map), promise_prototype),
1312 &if_slow_constructor);
1313 GotoIf(IsPromiseSpeciesProtectorCellInvalid(), &if_slow_constructor);
1314
1315 // If the {constructor} is the Promise function, we just immediately
1316 // return the {value} here and don't bother wrapping it into a
1317 // native Promise.
1318 GotoIfNot(WordEqual(promise_fun, constructor), &if_slow_constructor);
1319 Return(value);
1320
1321 // At this point, value or/and constructor are not native promises, but
1322 // they could be of the same subclass.
1323 BIND(&if_slow_constructor);
1324 {
1325 Node* const value_constructor =
1326 GetProperty(context, value, isolate()->factory()->constructor_string());
1327 GotoIfNot(WordEqual(value_constructor, constructor), &if_need_to_allocate);
1328 Return(value);
1329 }
1330
1331 BIND(&if_need_to_allocate);
1332 {
1333 Label if_nativepromise(this), if_notnativepromise(this, Label::kDeferred);
1334 Branch(WordEqual(promise_fun, constructor), &if_nativepromise,
1335 &if_notnativepromise);
1336
1337 // This adds a fast path for native promises that don't need to
1338 // create NewPromiseCapability.
1339 BIND(&if_nativepromise);
1340 {
1341 Node* const result = AllocateAndInitJSPromise(context);
1342 CallBuiltin(Builtins::kResolvePromise, context, result, value);
1343 Return(result);
1344 }
1345
1346 BIND(&if_notnativepromise);
1347 {
1348 Node* const debug_event = TrueConstant();
1349 Node* const capability = CallBuiltin(Builtins::kNewPromiseCapability,
1350 context, constructor, debug_event);
1351
1352 Node* const resolve =
1353 LoadObjectField(capability, PromiseCapability::kResolveOffset);
1354 CallJS(
1355 CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
1356 context, resolve, UndefinedConstant(), value);
1357
1358 Node* const result =
1359 LoadObjectField(capability, PromiseCapability::kPromiseOffset);
1360 Return(result);
1361 }
1362 }
1363 }
1364
1365 // ES6 #sec-getcapabilitiesexecutor-functions
TF_BUILTIN(PromiseGetCapabilitiesExecutor,PromiseBuiltinsAssembler)1366 TF_BUILTIN(PromiseGetCapabilitiesExecutor, PromiseBuiltinsAssembler) {
1367 Node* const resolve = Parameter(Descriptor::kResolve);
1368 Node* const reject = Parameter(Descriptor::kReject);
1369 Node* const context = Parameter(Descriptor::kContext);
1370
1371 Node* const capability = LoadContextElement(context, kCapabilitySlot);
1372
1373 Label if_alreadyinvoked(this, Label::kDeferred);
1374 GotoIfNot(IsUndefined(
1375 LoadObjectField(capability, PromiseCapability::kResolveOffset)),
1376 &if_alreadyinvoked);
1377 GotoIfNot(IsUndefined(
1378 LoadObjectField(capability, PromiseCapability::kRejectOffset)),
1379 &if_alreadyinvoked);
1380
1381 StoreObjectField(capability, PromiseCapability::kResolveOffset, resolve);
1382 StoreObjectField(capability, PromiseCapability::kRejectOffset, reject);
1383
1384 Return(UndefinedConstant());
1385
1386 BIND(&if_alreadyinvoked);
1387 ThrowTypeError(context, MessageTemplate::kPromiseExecutorAlreadyInvoked);
1388 }
1389
TF_BUILTIN(PromiseReject,PromiseBuiltinsAssembler)1390 TF_BUILTIN(PromiseReject, PromiseBuiltinsAssembler) {
1391 // 1. Let C be the this value.
1392 Node* const receiver = Parameter(Descriptor::kReceiver);
1393 Node* const reason = Parameter(Descriptor::kReason);
1394 Node* const context = Parameter(Descriptor::kContext);
1395
1396 // 2. If Type(C) is not Object, throw a TypeError exception.
1397 ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject,
1398 "PromiseReject");
1399
1400 Label if_nativepromise(this), if_custompromise(this, Label::kDeferred);
1401 Node* const native_context = LoadNativeContext(context);
1402
1403 Node* const promise_fun =
1404 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
1405 Branch(WordEqual(promise_fun, receiver), &if_nativepromise,
1406 &if_custompromise);
1407
1408 BIND(&if_nativepromise);
1409 {
1410 Node* const promise =
1411 AllocateAndSetJSPromise(context, v8::Promise::kRejected, reason);
1412 CallRuntime(Runtime::kPromiseRejectEventFromStack, context, promise,
1413 reason);
1414 Return(promise);
1415 }
1416
1417 BIND(&if_custompromise);
1418 {
1419 // 3. Let promiseCapability be ? NewPromiseCapability(C).
1420 Node* const debug_event = TrueConstant();
1421 Node* const capability = CallBuiltin(Builtins::kNewPromiseCapability,
1422 context, receiver, debug_event);
1423
1424 // 4. Perform ? Call(promiseCapability.[[Reject]], undefined, « r »).
1425 Node* const reject =
1426 LoadObjectField(capability, PromiseCapability::kRejectOffset);
1427 CallJS(CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
1428 context, reject, UndefinedConstant(), reason);
1429
1430 // 5. Return promiseCapability.[[Promise]].
1431 Node* const promise =
1432 LoadObjectField(capability, PromiseCapability::kPromiseOffset);
1433 Return(promise);
1434 }
1435 }
1436
CreatePromiseFinallyFunctions(Node * on_finally,Node * constructor,Node * native_context)1437 std::pair<Node*, Node*> PromiseBuiltinsAssembler::CreatePromiseFinallyFunctions(
1438 Node* on_finally, Node* constructor, Node* native_context) {
1439 Node* const promise_context =
1440 CreatePromiseContext(native_context, kPromiseFinallyContextLength);
1441 StoreContextElementNoWriteBarrier(promise_context, kOnFinallySlot,
1442 on_finally);
1443 StoreContextElementNoWriteBarrier(promise_context, kConstructorSlot,
1444 constructor);
1445 Node* const map = LoadContextElement(
1446 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
1447 Node* const then_finally_info = LoadContextElement(
1448 native_context, Context::PROMISE_THEN_FINALLY_SHARED_FUN);
1449 Node* const then_finally = AllocateFunctionWithMapAndContext(
1450 map, then_finally_info, promise_context);
1451 Node* const catch_finally_info = LoadContextElement(
1452 native_context, Context::PROMISE_CATCH_FINALLY_SHARED_FUN);
1453 Node* const catch_finally = AllocateFunctionWithMapAndContext(
1454 map, catch_finally_info, promise_context);
1455 return std::make_pair(then_finally, catch_finally);
1456 }
1457
TF_BUILTIN(PromiseValueThunkFinally,PromiseBuiltinsAssembler)1458 TF_BUILTIN(PromiseValueThunkFinally, PromiseBuiltinsAssembler) {
1459 Node* const context = Parameter(Descriptor::kContext);
1460
1461 Node* const value = LoadContextElement(context, kValueSlot);
1462 Return(value);
1463 }
1464
CreateValueThunkFunction(Node * value,Node * native_context)1465 Node* PromiseBuiltinsAssembler::CreateValueThunkFunction(Node* value,
1466 Node* native_context) {
1467 Node* const value_thunk_context = CreatePromiseContext(
1468 native_context, kPromiseValueThunkOrReasonContextLength);
1469 StoreContextElementNoWriteBarrier(value_thunk_context, kValueSlot, value);
1470 Node* const map = LoadContextElement(
1471 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
1472 Node* const value_thunk_info = LoadContextElement(
1473 native_context, Context::PROMISE_VALUE_THUNK_FINALLY_SHARED_FUN);
1474 Node* const value_thunk = AllocateFunctionWithMapAndContext(
1475 map, value_thunk_info, value_thunk_context);
1476 return value_thunk;
1477 }
1478
TF_BUILTIN(PromiseThenFinally,PromiseBuiltinsAssembler)1479 TF_BUILTIN(PromiseThenFinally, PromiseBuiltinsAssembler) {
1480 CSA_ASSERT_JS_ARGC_EQ(this, 1);
1481
1482 Node* const value = Parameter(Descriptor::kValue);
1483 Node* const context = Parameter(Descriptor::kContext);
1484
1485 // 1. Let onFinally be F.[[OnFinally]].
1486 Node* const on_finally = LoadContextElement(context, kOnFinallySlot);
1487
1488 // 2. Assert: IsCallable(onFinally) is true.
1489 CSA_ASSERT(this, IsCallable(on_finally));
1490
1491 // 3. Let result be ? Call(onFinally).
1492 Node* const result = CallJS(
1493 CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
1494 context, on_finally, UndefinedConstant());
1495
1496 // 4. Let C be F.[[Constructor]].
1497 Node* const constructor = LoadContextElement(context, kConstructorSlot);
1498
1499 // 5. Assert: IsConstructor(C) is true.
1500 CSA_ASSERT(this, IsConstructor(constructor));
1501
1502 // 6. Let promise be ? PromiseResolve(C, result).
1503 Node* const promise =
1504 CallBuiltin(Builtins::kPromiseResolve, context, constructor, result);
1505
1506 // 7. Let valueThunk be equivalent to a function that returns value.
1507 Node* const native_context = LoadNativeContext(context);
1508 Node* const value_thunk = CreateValueThunkFunction(value, native_context);
1509
1510 // 8. Return ? Invoke(promise, "then", « valueThunk »).
1511 Return(InvokeThen(native_context, promise, value_thunk));
1512 }
1513
TF_BUILTIN(PromiseThrowerFinally,PromiseBuiltinsAssembler)1514 TF_BUILTIN(PromiseThrowerFinally, PromiseBuiltinsAssembler) {
1515 Node* const context = Parameter(Descriptor::kContext);
1516
1517 Node* const reason = LoadContextElement(context, kValueSlot);
1518 CallRuntime(Runtime::kThrow, context, reason);
1519 Unreachable();
1520 }
1521
CreateThrowerFunction(Node * reason,Node * native_context)1522 Node* PromiseBuiltinsAssembler::CreateThrowerFunction(Node* reason,
1523 Node* native_context) {
1524 Node* const thrower_context = CreatePromiseContext(
1525 native_context, kPromiseValueThunkOrReasonContextLength);
1526 StoreContextElementNoWriteBarrier(thrower_context, kValueSlot, reason);
1527 Node* const map = LoadContextElement(
1528 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
1529 Node* const thrower_info = LoadContextElement(
1530 native_context, Context::PROMISE_THROWER_FINALLY_SHARED_FUN);
1531 Node* const thrower =
1532 AllocateFunctionWithMapAndContext(map, thrower_info, thrower_context);
1533 return thrower;
1534 }
1535
TF_BUILTIN(PromiseCatchFinally,PromiseBuiltinsAssembler)1536 TF_BUILTIN(PromiseCatchFinally, PromiseBuiltinsAssembler) {
1537 CSA_ASSERT_JS_ARGC_EQ(this, 1);
1538
1539 Node* const reason = Parameter(Descriptor::kReason);
1540 Node* const context = Parameter(Descriptor::kContext);
1541
1542 // 1. Let onFinally be F.[[OnFinally]].
1543 Node* const on_finally = LoadContextElement(context, kOnFinallySlot);
1544
1545 // 2. Assert: IsCallable(onFinally) is true.
1546 CSA_ASSERT(this, IsCallable(on_finally));
1547
1548 // 3. Let result be ? Call(onFinally).
1549 Node* result = CallJS(
1550 CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
1551 context, on_finally, UndefinedConstant());
1552
1553 // 4. Let C be F.[[Constructor]].
1554 Node* const constructor = LoadContextElement(context, kConstructorSlot);
1555
1556 // 5. Assert: IsConstructor(C) is true.
1557 CSA_ASSERT(this, IsConstructor(constructor));
1558
1559 // 6. Let promise be ? PromiseResolve(C, result).
1560 Node* const promise =
1561 CallBuiltin(Builtins::kPromiseResolve, context, constructor, result);
1562
1563 // 7. Let thrower be equivalent to a function that throws reason.
1564 Node* const native_context = LoadNativeContext(context);
1565 Node* const thrower = CreateThrowerFunction(reason, native_context);
1566
1567 // 8. Return ? Invoke(promise, "then", « thrower »).
1568 Return(InvokeThen(native_context, promise, thrower));
1569 }
1570
TF_BUILTIN(PromisePrototypeFinally,PromiseBuiltinsAssembler)1571 TF_BUILTIN(PromisePrototypeFinally, PromiseBuiltinsAssembler) {
1572 CSA_ASSERT_JS_ARGC_EQ(this, 1);
1573
1574 // 1. Let promise be the this value.
1575 Node* const receiver = Parameter(Descriptor::kReceiver);
1576 Node* const on_finally = Parameter(Descriptor::kOnFinally);
1577 Node* const context = Parameter(Descriptor::kContext);
1578
1579 // 2. If Type(promise) is not Object, throw a TypeError exception.
1580 ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject,
1581 "Promise.prototype.finally");
1582
1583 // 3. Let C be ? SpeciesConstructor(promise, %Promise%).
1584 Node* const native_context = LoadNativeContext(context);
1585 Node* const promise_fun =
1586 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
1587 VARIABLE(var_constructor, MachineRepresentation::kTagged, promise_fun);
1588 Label slow_constructor(this, Label::kDeferred), done_constructor(this);
1589 Node* const receiver_map = LoadMap(receiver);
1590 GotoIfNot(IsJSPromiseMap(receiver_map), &slow_constructor);
1591 BranchIfPromiseSpeciesLookupChainIntact(native_context, receiver_map,
1592 &done_constructor, &slow_constructor);
1593 BIND(&slow_constructor);
1594 {
1595 Node* const constructor =
1596 SpeciesConstructor(context, receiver, promise_fun);
1597 var_constructor.Bind(constructor);
1598 Goto(&done_constructor);
1599 }
1600 BIND(&done_constructor);
1601 Node* const constructor = var_constructor.value();
1602
1603 // 4. Assert: IsConstructor(C) is true.
1604 CSA_ASSERT(this, IsConstructor(constructor));
1605
1606 VARIABLE(var_then_finally, MachineRepresentation::kTagged);
1607 VARIABLE(var_catch_finally, MachineRepresentation::kTagged);
1608
1609 Label if_notcallable(this, Label::kDeferred), perform_finally(this);
1610
1611 GotoIf(TaggedIsSmi(on_finally), &if_notcallable);
1612 GotoIfNot(IsCallable(on_finally), &if_notcallable);
1613
1614 // 6. Else,
1615 // a. Let thenFinally be a new built-in function object as defined
1616 // in ThenFinally Function.
1617 // b. Let catchFinally be a new built-in function object as
1618 // defined in CatchFinally Function.
1619 // c. Set thenFinally and catchFinally's [[Constructor]] internal
1620 // slots to C.
1621 // d. Set thenFinally and catchFinally's [[OnFinally]] internal
1622 // slots to onFinally.
1623 Node* then_finally = nullptr;
1624 Node* catch_finally = nullptr;
1625 std::tie(then_finally, catch_finally) =
1626 CreatePromiseFinallyFunctions(on_finally, constructor, native_context);
1627 var_then_finally.Bind(then_finally);
1628 var_catch_finally.Bind(catch_finally);
1629 Goto(&perform_finally);
1630
1631 // 5. If IsCallable(onFinally) is not true,
1632 // a. Let thenFinally be onFinally.
1633 // b. Let catchFinally be onFinally.
1634 BIND(&if_notcallable);
1635 {
1636 var_then_finally.Bind(on_finally);
1637 var_catch_finally.Bind(on_finally);
1638 Goto(&perform_finally);
1639 }
1640
1641 // 7. Return ? Invoke(promise, "then", « thenFinally, catchFinally »).
1642 BIND(&perform_finally);
1643 Return(InvokeThen(native_context, receiver, var_then_finally.value(),
1644 var_catch_finally.value()));
1645 }
1646
1647 // ES #sec-fulfillpromise
TF_BUILTIN(FulfillPromise,PromiseBuiltinsAssembler)1648 TF_BUILTIN(FulfillPromise, PromiseBuiltinsAssembler) {
1649 Node* const promise = Parameter(Descriptor::kPromise);
1650 Node* const value = Parameter(Descriptor::kValue);
1651 Node* const context = Parameter(Descriptor::kContext);
1652
1653 CSA_ASSERT(this, TaggedIsNotSmi(promise));
1654 CSA_ASSERT(this, IsJSPromise(promise));
1655
1656 // 2. Let reactions be promise.[[PromiseFulfillReactions]].
1657 Node* const reactions =
1658 LoadObjectField(promise, JSPromise::kReactionsOrResultOffset);
1659
1660 // 3. Set promise.[[PromiseResult]] to value.
1661 // 4. Set promise.[[PromiseFulfillReactions]] to undefined.
1662 // 5. Set promise.[[PromiseRejectReactions]] to undefined.
1663 StoreObjectField(promise, JSPromise::kReactionsOrResultOffset, value);
1664
1665 // 6. Set promise.[[PromiseState]] to "fulfilled".
1666 PromiseSetStatus(promise, Promise::kFulfilled);
1667
1668 // 7. Return TriggerPromiseReactions(reactions, value).
1669 Return(TriggerPromiseReactions(context, reactions, value,
1670 PromiseReaction::kFulfill));
1671 }
1672
1673 // ES #sec-rejectpromise
TF_BUILTIN(RejectPromise,PromiseBuiltinsAssembler)1674 TF_BUILTIN(RejectPromise, PromiseBuiltinsAssembler) {
1675 Node* const promise = Parameter(Descriptor::kPromise);
1676 Node* const reason = Parameter(Descriptor::kReason);
1677 Node* const debug_event = Parameter(Descriptor::kDebugEvent);
1678 Node* const context = Parameter(Descriptor::kContext);
1679
1680 CSA_ASSERT(this, TaggedIsNotSmi(promise));
1681 CSA_ASSERT(this, IsJSPromise(promise));
1682 CSA_ASSERT(this, IsBoolean(debug_event));
1683 Label if_runtime(this, Label::kDeferred);
1684
1685 // If promise hook is enabled or the debugger is active, let
1686 // the runtime handle this operation, which greatly reduces
1687 // the complexity here and also avoids a couple of back and
1688 // forth between JavaScript and C++ land.
1689 GotoIf(IsPromiseHookEnabledOrDebugIsActive(), &if_runtime);
1690
1691 // 7. If promise.[[PromiseIsHandled]] is false, perform
1692 // HostPromiseRejectionTracker(promise, "reject").
1693 // We don't try to handle rejecting {promise} without handler
1694 // here, but we let the C++ code take care of this completely.
1695 GotoIfNot(PromiseHasHandler(promise), &if_runtime);
1696
1697 // 2. Let reactions be promise.[[PromiseRejectReactions]].
1698 Node* reactions =
1699 LoadObjectField(promise, JSPromise::kReactionsOrResultOffset);
1700
1701 // 3. Set promise.[[PromiseResult]] to reason.
1702 // 4. Set promise.[[PromiseFulfillReactions]] to undefined.
1703 // 5. Set promise.[[PromiseRejectReactions]] to undefined.
1704 StoreObjectField(promise, JSPromise::kReactionsOrResultOffset, reason);
1705
1706 // 6. Set promise.[[PromiseState]] to "rejected".
1707 PromiseSetStatus(promise, Promise::kRejected);
1708
1709 // 7. Return TriggerPromiseReactions(reactions, reason).
1710 Return(TriggerPromiseReactions(context, reactions, reason,
1711 PromiseReaction::kReject));
1712
1713 BIND(&if_runtime);
1714 TailCallRuntime(Runtime::kRejectPromise, context, promise, reason,
1715 debug_event);
1716 }
1717
1718 // ES #sec-promise-resolve-functions
TF_BUILTIN(ResolvePromise,PromiseBuiltinsAssembler)1719 TF_BUILTIN(ResolvePromise, PromiseBuiltinsAssembler) {
1720 Node* const promise = Parameter(Descriptor::kPromise);
1721 Node* const resolution = Parameter(Descriptor::kResolution);
1722 Node* const context = Parameter(Descriptor::kContext);
1723
1724 CSA_ASSERT(this, TaggedIsNotSmi(promise));
1725 CSA_ASSERT(this, IsJSPromise(promise));
1726
1727 Label do_enqueue(this), if_fulfill(this), if_reject(this, Label::kDeferred),
1728 if_runtime(this, Label::kDeferred);
1729 VARIABLE(var_reason, MachineRepresentation::kTagged);
1730 VARIABLE(var_then, MachineRepresentation::kTagged);
1731
1732 // If promise hook is enabled or the debugger is active, let
1733 // the runtime handle this operation, which greatly reduces
1734 // the complexity here and also avoids a couple of back and
1735 // forth between JavaScript and C++ land.
1736 GotoIf(IsPromiseHookEnabledOrDebugIsActive(), &if_runtime);
1737
1738 // 6. If SameValue(resolution, promise) is true, then
1739 // We can use pointer comparison here, since the {promise} is guaranteed
1740 // to be a JSPromise inside this function and thus is reference comparable.
1741 GotoIf(WordEqual(promise, resolution), &if_runtime);
1742
1743 // 7. If Type(resolution) is not Object, then
1744 GotoIf(TaggedIsSmi(resolution), &if_fulfill);
1745 Node* const result_map = LoadMap(resolution);
1746 GotoIfNot(IsJSReceiverMap(result_map), &if_fulfill);
1747
1748 // We can skip the "then" lookup on {resolution} if its [[Prototype]]
1749 // is the (initial) Promise.prototype and the Promise#then protector
1750 // is intact, as that guards the lookup path for the "then" property
1751 // on JSPromise instances which have the (initial) %PromisePrototype%.
1752 Label if_fast(this), if_slow(this, Label::kDeferred);
1753 Node* const native_context = LoadNativeContext(context);
1754 BranchIfPromiseThenLookupChainIntact(native_context, result_map, &if_fast,
1755 &if_slow);
1756
1757 // Resolution is a native promise and if it's already resolved or
1758 // rejected, shortcircuit the resolution procedure by directly
1759 // reusing the value from the promise.
1760 BIND(&if_fast);
1761 {
1762 Node* const then =
1763 LoadContextElement(native_context, Context::PROMISE_THEN_INDEX);
1764 var_then.Bind(then);
1765 Goto(&do_enqueue);
1766 }
1767
1768 BIND(&if_slow);
1769 {
1770 // 8. Let then be Get(resolution, "then").
1771 Node* const then =
1772 GetProperty(context, resolution, isolate()->factory()->then_string());
1773
1774 // 9. If then is an abrupt completion, then
1775 GotoIfException(then, &if_reject, &var_reason);
1776
1777 // 11. If IsCallable(thenAction) is false, then
1778 GotoIf(TaggedIsSmi(then), &if_fulfill);
1779 Node* const then_map = LoadMap(then);
1780 GotoIfNot(IsCallableMap(then_map), &if_fulfill);
1781 var_then.Bind(then);
1782 Goto(&do_enqueue);
1783 }
1784
1785 BIND(&do_enqueue);
1786 {
1787 // 12. Perform EnqueueJob("PromiseJobs", PromiseResolveThenableJob,
1788 // «promise, resolution, thenAction»).
1789 Node* const task = AllocatePromiseResolveThenableJobTask(
1790 promise, var_then.value(), resolution, native_context);
1791 TailCallBuiltin(Builtins::kEnqueueMicrotask, native_context, task);
1792 }
1793
1794 BIND(&if_fulfill);
1795 {
1796 // 7.b Return FulfillPromise(promise, resolution).
1797 TailCallBuiltin(Builtins::kFulfillPromise, context, promise, resolution);
1798 }
1799
1800 BIND(&if_runtime);
1801 Return(CallRuntime(Runtime::kResolvePromise, context, promise, resolution));
1802
1803 BIND(&if_reject);
1804 {
1805 // 9.a Return RejectPromise(promise, then.[[Value]]).
1806 TailCallBuiltin(Builtins::kRejectPromise, context, promise,
1807 var_reason.value(), FalseConstant());
1808 }
1809 }
1810
PerformPromiseAll(Node * context,Node * constructor,Node * capability,const IteratorRecord & iterator,Label * if_exception,Variable * var_exception)1811 Node* PromiseBuiltinsAssembler::PerformPromiseAll(
1812 Node* context, Node* constructor, Node* capability,
1813 const IteratorRecord& iterator, Label* if_exception,
1814 Variable* var_exception) {
1815 IteratorBuiltinsAssembler iter_assembler(state());
1816
1817 Node* const instrumenting = IsDebugActive();
1818 Node* const native_context = LoadNativeContext(context);
1819
1820 // For catch prediction, don't treat the .then calls as handling it;
1821 // instead, recurse outwards.
1822 SetForwardingHandlerIfTrue(
1823 native_context, instrumenting,
1824 LoadObjectField(capability, PromiseCapability::kRejectOffset));
1825
1826 Node* const resolve_element_context =
1827 CreatePromiseAllResolveElementContext(capability, native_context);
1828
1829 TVARIABLE(Smi, var_index, SmiConstant(1));
1830 Label loop(this, &var_index), done_loop(this),
1831 too_many_elements(this, Label::kDeferred),
1832 close_iterator(this, Label::kDeferred);
1833 Goto(&loop);
1834 BIND(&loop);
1835 {
1836 // Let next be IteratorStep(iteratorRecord.[[Iterator]]).
1837 // If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
1838 // ReturnIfAbrupt(next).
1839 Node* const fast_iterator_result_map =
1840 LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
1841 Node* const next = iter_assembler.IteratorStep(
1842 native_context, iterator, &done_loop, fast_iterator_result_map,
1843 if_exception, var_exception);
1844
1845 // Let nextValue be IteratorValue(next).
1846 // If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to
1847 // true.
1848 // ReturnIfAbrupt(nextValue).
1849 Node* const next_value = iter_assembler.IteratorValue(
1850 native_context, next, fast_iterator_result_map, if_exception,
1851 var_exception);
1852
1853 // Let nextPromise be ? Invoke(constructor, "resolve", « nextValue »).
1854 Node* const next_promise =
1855 InvokeResolve(native_context, constructor, next_value, &close_iterator,
1856 var_exception);
1857
1858 // Check if we reached the limit.
1859 TNode<Smi> const index = var_index.value();
1860 GotoIf(SmiEqual(index, SmiConstant(PropertyArray::HashField::kMax)),
1861 &too_many_elements);
1862
1863 // Set remainingElementsCount.[[Value]] to
1864 // remainingElementsCount.[[Value]] + 1.
1865 TNode<Smi> const remaining_elements_count = CAST(LoadContextElement(
1866 resolve_element_context, kPromiseAllResolveElementRemainingSlot));
1867 StoreContextElementNoWriteBarrier(
1868 resolve_element_context, kPromiseAllResolveElementRemainingSlot,
1869 SmiAdd(remaining_elements_count, SmiConstant(1)));
1870
1871 // Let resolveElement be CreateBuiltinFunction(steps,
1872 // « [[AlreadyCalled]],
1873 // [[Index]],
1874 // [[Values]],
1875 // [[Capability]],
1876 // [[RemainingElements]] »).
1877 // Set resolveElement.[[AlreadyCalled]] to a Record { [[Value]]: false }.
1878 // Set resolveElement.[[Index]] to index.
1879 // Set resolveElement.[[Values]] to values.
1880 // Set resolveElement.[[Capability]] to resultCapability.
1881 // Set resolveElement.[[RemainingElements]] to remainingElementsCount.
1882 Node* const resolve_element_fun = CreatePromiseAllResolveElementFunction(
1883 resolve_element_context, index, native_context);
1884
1885 // Perform ? Invoke(nextPromise, "then", « resolveElement,
1886 // resultCapability.[[Reject]] »).
1887 Node* const then =
1888 GetProperty(native_context, next_promise, factory()->then_string());
1889 GotoIfException(then, &close_iterator, var_exception);
1890
1891 Node* const then_call = CallJS(
1892 CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined),
1893 native_context, then, next_promise, resolve_element_fun,
1894 LoadObjectField(capability, PromiseCapability::kRejectOffset));
1895 GotoIfException(then_call, &close_iterator, var_exception);
1896
1897 // For catch prediction, mark that rejections here are semantically
1898 // handled by the combined Promise.
1899 SetPromiseHandledByIfTrue(native_context, instrumenting, then_call, [=]() {
1900 // Load promiseCapability.[[Promise]]
1901 return LoadObjectField(capability, PromiseCapability::kPromiseOffset);
1902 });
1903
1904 // Set index to index + 1.
1905 var_index = SmiAdd(index, SmiConstant(1));
1906 Goto(&loop);
1907 }
1908
1909 BIND(&too_many_elements);
1910 {
1911 // If there are too many elements (currently more than 2**21-1), raise a
1912 // RangeError here (which is caught directly and turned into a rejection)
1913 // of the resulting promise. We could gracefully handle this case as well
1914 // and support more than this number of elements by going to a separate
1915 // function and pass the larger indices via a separate context, but it
1916 // doesn't seem likely that we need this, and it's unclear how the rest
1917 // of the system deals with 2**21 live Promises anyways.
1918 Node* const result =
1919 CallRuntime(Runtime::kThrowRangeError, native_context,
1920 SmiConstant(MessageTemplate::kTooManyElementsInPromiseAll));
1921 GotoIfException(result, &close_iterator, var_exception);
1922 Unreachable();
1923 }
1924
1925 BIND(&close_iterator);
1926 {
1927 // Exception must be bound to a JS value.
1928 CSA_ASSERT(this, IsNotTheHole(var_exception->value()));
1929 iter_assembler.IteratorCloseOnException(native_context, iterator,
1930 if_exception, var_exception);
1931 }
1932
1933 BIND(&done_loop);
1934 {
1935 Label resolve_promise(this, Label::kDeferred), return_promise(this);
1936 // Set iteratorRecord.[[Done]] to true.
1937 // Set remainingElementsCount.[[Value]] to
1938 // remainingElementsCount.[[Value]] - 1.
1939 TNode<Smi> remaining_elements_count = CAST(LoadContextElement(
1940 resolve_element_context, kPromiseAllResolveElementRemainingSlot));
1941 remaining_elements_count = SmiSub(remaining_elements_count, SmiConstant(1));
1942 StoreContextElementNoWriteBarrier(resolve_element_context,
1943 kPromiseAllResolveElementRemainingSlot,
1944 remaining_elements_count);
1945 GotoIf(SmiEqual(remaining_elements_count, SmiConstant(0)),
1946 &resolve_promise);
1947
1948 // Pre-allocate the backing store for the {values_array} to the desired
1949 // capacity here. We may already have elements here in case of some
1950 // fancy Thenable that calls the resolve callback immediately, so we need
1951 // to handle that correctly here.
1952 Node* const values_array = LoadContextElement(
1953 resolve_element_context, kPromiseAllResolveElementValuesArraySlot);
1954 Node* const old_elements = LoadElements(values_array);
1955 TNode<Smi> const old_capacity = LoadFixedArrayBaseLength(old_elements);
1956 TNode<Smi> const new_capacity = var_index.value();
1957 GotoIf(SmiGreaterThanOrEqual(old_capacity, new_capacity), &return_promise);
1958 Node* const new_elements =
1959 AllocateFixedArray(PACKED_ELEMENTS, new_capacity, SMI_PARAMETERS,
1960 AllocationFlag::kAllowLargeObjectAllocation);
1961 CopyFixedArrayElements(PACKED_ELEMENTS, old_elements, PACKED_ELEMENTS,
1962 new_elements, SmiConstant(0), old_capacity,
1963 new_capacity, UPDATE_WRITE_BARRIER, SMI_PARAMETERS);
1964 StoreObjectField(values_array, JSArray::kElementsOffset, new_elements);
1965 Goto(&return_promise);
1966
1967 // If remainingElementsCount.[[Value]] is 0, then
1968 // Let valuesArray be CreateArrayFromList(values).
1969 // Perform ? Call(resultCapability.[[Resolve]], undefined,
1970 // « valuesArray »).
1971 BIND(&resolve_promise);
1972 {
1973 Node* const resolve =
1974 LoadObjectField(capability, PromiseCapability::kResolveOffset);
1975 Node* const values_array = LoadContextElement(
1976 resolve_element_context, kPromiseAllResolveElementValuesArraySlot);
1977 Node* const resolve_call = CallJS(
1978 CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
1979 native_context, resolve, UndefinedConstant(), values_array);
1980 GotoIfException(resolve_call, if_exception, var_exception);
1981 Goto(&return_promise);
1982 }
1983
1984 // Return resultCapability.[[Promise]].
1985 BIND(&return_promise);
1986 }
1987
1988 Node* const promise =
1989 LoadObjectField(capability, PromiseCapability::kPromiseOffset);
1990 return promise;
1991 }
1992
1993 // ES#sec-promise.all
1994 // Promise.all ( iterable )
TF_BUILTIN(PromiseAll,PromiseBuiltinsAssembler)1995 TF_BUILTIN(PromiseAll, PromiseBuiltinsAssembler) {
1996 IteratorBuiltinsAssembler iter_assembler(state());
1997
1998 // Let C be the this value.
1999 // If Type(C) is not Object, throw a TypeError exception.
2000 Node* const receiver = Parameter(Descriptor::kReceiver);
2001 Node* const context = Parameter(Descriptor::kContext);
2002 ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject,
2003 "Promise.all");
2004
2005 // Let promiseCapability be ? NewPromiseCapability(C).
2006 // Don't fire debugEvent so that forwarding the rejection through all does not
2007 // trigger redundant ExceptionEvents
2008 Node* const debug_event = FalseConstant();
2009 Node* const capability = CallBuiltin(Builtins::kNewPromiseCapability, context,
2010 receiver, debug_event);
2011
2012 VARIABLE(var_exception, MachineRepresentation::kTagged, TheHoleConstant());
2013 Label reject_promise(this, &var_exception, Label::kDeferred);
2014
2015 // Let iterator be GetIterator(iterable).
2016 // IfAbruptRejectPromise(iterator, promiseCapability).
2017 Node* const iterable = Parameter(Descriptor::kIterable);
2018 IteratorRecord iterator = iter_assembler.GetIterator(
2019 context, iterable, &reject_promise, &var_exception);
2020
2021 // Let result be PerformPromiseAll(iteratorRecord, C, promiseCapability).
2022 // If result is an abrupt completion, then
2023 // If iteratorRecord.[[Done]] is false, let result be
2024 // IteratorClose(iterator, result).
2025 // IfAbruptRejectPromise(result, promiseCapability).
2026 Node* const result = PerformPromiseAll(
2027 context, receiver, capability, iterator, &reject_promise, &var_exception);
2028
2029 Return(result);
2030
2031 BIND(&reject_promise);
2032 {
2033 // Exception must be bound to a JS value.
2034 CSA_SLOW_ASSERT(this, IsNotTheHole(var_exception.value()));
2035 Node* const reject =
2036 LoadObjectField(capability, PromiseCapability::kRejectOffset);
2037 CallJS(CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
2038 context, reject, UndefinedConstant(), var_exception.value());
2039
2040 Node* const promise =
2041 LoadObjectField(capability, PromiseCapability::kPromiseOffset);
2042 Return(promise);
2043 }
2044 }
2045
TF_BUILTIN(PromiseAllResolveElementClosure,PromiseBuiltinsAssembler)2046 TF_BUILTIN(PromiseAllResolveElementClosure, PromiseBuiltinsAssembler) {
2047 Node* const value = Parameter(Descriptor::kValue);
2048 Node* const context = Parameter(Descriptor::kContext);
2049 Node* const function = LoadFromFrame(StandardFrameConstants::kFunctionOffset);
2050
2051 Label already_called(this, Label::kDeferred), resolve_promise(this);
2052
2053 // We use the {function}s context as the marker to remember whether this
2054 // resolve element closure was already called. It points to the resolve
2055 // element context (which is a FunctionContext) until it was called the
2056 // first time, in which case we make it point to the native context here
2057 // to mark this resolve element closure as done.
2058 GotoIf(IsNativeContext(context), &already_called);
2059 CSA_ASSERT(this, SmiEqual(LoadFixedArrayBaseLength(context),
2060 SmiConstant(kPromiseAllResolveElementLength)));
2061 Node* const native_context = LoadNativeContext(context);
2062 StoreObjectField(function, JSFunction::kContextOffset, native_context);
2063
2064 // Determine the index from the {function}.
2065 Label unreachable(this, Label::kDeferred);
2066 STATIC_ASSERT(PropertyArray::kNoHashSentinel == 0);
2067 Node* const identity_hash =
2068 LoadJSReceiverIdentityHash(function, &unreachable);
2069 CSA_ASSERT(this, IntPtrGreaterThan(identity_hash, IntPtrConstant(0)));
2070 Node* const index = IntPtrSub(identity_hash, IntPtrConstant(1));
2071
2072 // Check if we need to grow the [[ValuesArray]] to store {value} at {index}.
2073 Node* const values_array =
2074 LoadContextElement(context, kPromiseAllResolveElementValuesArraySlot);
2075 Node* const elements = LoadElements(values_array);
2076 Node* const values_length =
2077 LoadAndUntagObjectField(values_array, JSArray::kLengthOffset);
2078 Label if_inbounds(this), if_outofbounds(this), done(this);
2079 Branch(IntPtrLessThan(index, values_length), &if_inbounds, &if_outofbounds);
2080
2081 BIND(&if_outofbounds);
2082 {
2083 // Check if we need to grow the backing store.
2084 Node* const new_length = IntPtrAdd(index, IntPtrConstant(1));
2085 Node* const elements_length =
2086 LoadAndUntagObjectField(elements, FixedArray::kLengthOffset);
2087 Label if_grow(this, Label::kDeferred), if_nogrow(this);
2088 Branch(IntPtrLessThan(index, elements_length), &if_nogrow, &if_grow);
2089
2090 BIND(&if_grow);
2091 {
2092 // We need to grow the backing store to fit the {index} as well.
2093 Node* const new_elements_length =
2094 IntPtrMin(CalculateNewElementsCapacity(new_length),
2095 IntPtrConstant(PropertyArray::HashField::kMax + 1));
2096 CSA_ASSERT(this, IntPtrLessThan(index, new_elements_length));
2097 CSA_ASSERT(this, IntPtrLessThan(elements_length, new_elements_length));
2098 Node* const new_elements = AllocateFixedArray(
2099 PACKED_ELEMENTS, new_elements_length, INTPTR_PARAMETERS,
2100 AllocationFlag::kAllowLargeObjectAllocation);
2101 CopyFixedArrayElements(PACKED_ELEMENTS, elements, PACKED_ELEMENTS,
2102 new_elements, elements_length,
2103 new_elements_length);
2104 StoreFixedArrayElement(new_elements, index, value);
2105
2106 // Update backing store and "length" on {values_array}.
2107 StoreObjectField(values_array, JSArray::kElementsOffset, new_elements);
2108 StoreObjectFieldNoWriteBarrier(values_array, JSArray::kLengthOffset,
2109 SmiTag(new_length));
2110 Goto(&done);
2111 }
2112
2113 BIND(&if_nogrow);
2114 {
2115 // The {index} is within bounds of the {elements} backing store, so
2116 // just store the {value} and update the "length" of the {values_array}.
2117 StoreObjectFieldNoWriteBarrier(values_array, JSArray::kLengthOffset,
2118 SmiTag(new_length));
2119 StoreFixedArrayElement(elements, index, value);
2120 Goto(&done);
2121 }
2122 }
2123
2124 BIND(&if_inbounds);
2125 {
2126 // The {index} is in bounds of the {values_array},
2127 // just store the {value} and continue.
2128 StoreFixedArrayElement(elements, index, value);
2129 Goto(&done);
2130 }
2131
2132 BIND(&done);
2133 TNode<Smi> remaining_elements_count =
2134 CAST(LoadContextElement(context, kPromiseAllResolveElementRemainingSlot));
2135 remaining_elements_count = SmiSub(remaining_elements_count, SmiConstant(1));
2136 StoreContextElement(context, kPromiseAllResolveElementRemainingSlot,
2137 remaining_elements_count);
2138 GotoIf(SmiEqual(remaining_elements_count, SmiConstant(0)), &resolve_promise);
2139 Return(UndefinedConstant());
2140
2141 BIND(&resolve_promise);
2142 Node* const capability =
2143 LoadContextElement(context, kPromiseAllResolveElementCapabilitySlot);
2144 Node* const resolve =
2145 LoadObjectField(capability, PromiseCapability::kResolveOffset);
2146 CallJS(CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
2147 context, resolve, UndefinedConstant(), values_array);
2148 Return(UndefinedConstant());
2149
2150 BIND(&already_called);
2151 Return(UndefinedConstant());
2152
2153 BIND(&unreachable);
2154 Unreachable();
2155 }
2156
2157 // ES#sec-promise.race
2158 // Promise.race ( iterable )
TF_BUILTIN(PromiseRace,PromiseBuiltinsAssembler)2159 TF_BUILTIN(PromiseRace, PromiseBuiltinsAssembler) {
2160 IteratorBuiltinsAssembler iter_assembler(state());
2161 VARIABLE(var_exception, MachineRepresentation::kTagged, TheHoleConstant());
2162
2163 Node* const receiver = Parameter(Descriptor::kReceiver);
2164 Node* const context = Parameter(Descriptor::kContext);
2165 ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject,
2166 "Promise.race");
2167
2168 // Let promiseCapability be ? NewPromiseCapability(C).
2169 // Don't fire debugEvent so that forwarding the rejection through all does not
2170 // trigger redundant ExceptionEvents
2171 Node* const debug_event = FalseConstant();
2172 Node* const capability = CallBuiltin(Builtins::kNewPromiseCapability, context,
2173 receiver, debug_event);
2174
2175 Node* const resolve =
2176 LoadObjectField(capability, PromiseCapability::kResolveOffset);
2177 Node* const reject =
2178 LoadObjectField(capability, PromiseCapability::kRejectOffset);
2179
2180 Node* const instrumenting = IsDebugActive();
2181
2182 Label close_iterator(this, Label::kDeferred);
2183 Label reject_promise(this, Label::kDeferred);
2184
2185 // For catch prediction, don't treat the .then calls as handling it;
2186 // instead, recurse outwards.
2187 SetForwardingHandlerIfTrue(context, instrumenting, reject);
2188
2189 // Let iterator be GetIterator(iterable).
2190 // IfAbruptRejectPromise(iterator, promiseCapability).
2191 Node* const iterable = Parameter(Descriptor::kIterable);
2192 IteratorRecord iterator = iter_assembler.GetIterator(
2193 context, iterable, &reject_promise, &var_exception);
2194
2195 // Let result be PerformPromiseRace(iteratorRecord, C, promiseCapability).
2196 {
2197 Label loop(this), break_loop(this);
2198 Goto(&loop);
2199 BIND(&loop);
2200 {
2201 Node* const native_context = LoadNativeContext(context);
2202 Node* const fast_iterator_result_map = LoadContextElement(
2203 native_context, Context::ITERATOR_RESULT_MAP_INDEX);
2204
2205 // Let next be IteratorStep(iteratorRecord.[[Iterator]]).
2206 // If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
2207 // ReturnIfAbrupt(next).
2208 Node* const next = iter_assembler.IteratorStep(
2209 context, iterator, &break_loop, fast_iterator_result_map,
2210 &reject_promise, &var_exception);
2211
2212 // Let nextValue be IteratorValue(next).
2213 // If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to
2214 // true.
2215 // ReturnIfAbrupt(nextValue).
2216 Node* const next_value =
2217 iter_assembler.IteratorValue(context, next, fast_iterator_result_map,
2218 &reject_promise, &var_exception);
2219
2220 // Let nextPromise be ? Invoke(constructor, "resolve", « nextValue »).
2221 Node* const next_promise =
2222 InvokeResolve(native_context, receiver, next_value, &close_iterator,
2223 &var_exception);
2224
2225 // Perform ? Invoke(nextPromise, "then", « resolveElement,
2226 // resultCapability.[[Reject]] »).
2227 Node* const then =
2228 GetProperty(context, next_promise, factory()->then_string());
2229 GotoIfException(then, &close_iterator, &var_exception);
2230
2231 Node* const then_call =
2232 CallJS(CodeFactory::Call(isolate(),
2233 ConvertReceiverMode::kNotNullOrUndefined),
2234 context, then, next_promise, resolve, reject);
2235 GotoIfException(then_call, &close_iterator, &var_exception);
2236
2237 // For catch prediction, mark that rejections here are semantically
2238 // handled by the combined Promise.
2239 SetPromiseHandledByIfTrue(context, instrumenting, then_call, [=]() {
2240 // Load promiseCapability.[[Promise]]
2241 return LoadObjectField(capability, PromiseCapability::kPromiseOffset);
2242 });
2243 Goto(&loop);
2244 }
2245
2246 BIND(&break_loop);
2247 Return(LoadObjectField(capability, PromiseCapability::kPromiseOffset));
2248 }
2249
2250 BIND(&close_iterator);
2251 {
2252 CSA_ASSERT(this, IsNotTheHole(var_exception.value()));
2253 iter_assembler.IteratorCloseOnException(context, iterator, &reject_promise,
2254 &var_exception);
2255 }
2256
2257 BIND(&reject_promise);
2258 {
2259 Node* const reject =
2260 LoadObjectField(capability, PromiseCapability::kRejectOffset);
2261 CallJS(CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
2262 context, reject, UndefinedConstant(), var_exception.value());
2263
2264 Node* const promise =
2265 LoadObjectField(capability, PromiseCapability::kPromiseOffset);
2266 Return(promise);
2267 }
2268 }
2269
2270 } // namespace internal
2271 } // namespace v8
2272