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