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