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