builtins-promise-gen.cc 96.2 KB
Newer Older
1 2 3
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4

5 6 7
#include "src/builtins/builtins-promise-gen.h"

#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 19 20
namespace v8 {
namespace internal {

21
using compiler::Node;
22
using IteratorRecord = IteratorBuiltinsAssembler::IteratorRecord;
23

gsathya's avatar
gsathya committed
24 25 26 27
Node* PromiseBuiltinsAssembler::AllocateJSPromise(Node* context) {
  Node* const native_context = LoadNativeContext(context);
  Node* const promise_fun =
      LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
28
  CSA_ASSERT(this, IsFunctionWithPrototypeSlotMap(LoadMap(promise_fun)));
29
  Node* const promise_map =
gsathya's avatar
gsathya committed
30
      LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
31 32 33
  Node* const promise = Allocate(JSPromise::kSizeWithEmbedderFields);
  StoreMapNoWriteBarrier(promise, promise_map);
  StoreObjectFieldRoot(promise, JSPromise::kPropertiesOrHashOffset,
34
                       RootIndex::kEmptyFixedArray);
35
  StoreObjectFieldRoot(promise, JSPromise::kElementsOffset,
36
                       RootIndex::kEmptyFixedArray);
37
  return promise;
gsathya's avatar
gsathya committed
38 39 40
}

void PromiseBuiltinsAssembler::PromiseInit(Node* promise) {
41
  STATIC_ASSERT(v8::Promise::kPending == 0);
42
  StoreObjectFieldNoWriteBarrier(promise, JSPromise::kReactionsOrResultOffset,
43
                                 SmiConstant(Smi::zero()));
44
  StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset,
45
                                 SmiConstant(Smi::zero()));
46 47
  for (int offset = JSPromise::kSize;
       offset < JSPromise::kSizeWithEmbedderFields; offset += kTaggedSize) {
48
    StoreObjectFieldNoWriteBarrier(promise, offset, SmiConstant(Smi::zero()));
49
  }
gsathya's avatar
gsathya committed
50 51 52 53 54 55 56 57
}

Node* PromiseBuiltinsAssembler::AllocateAndInitJSPromise(Node* context) {
  return AllocateAndInitJSPromise(context, UndefinedConstant());
}

Node* PromiseBuiltinsAssembler::AllocateAndInitJSPromise(Node* context,
                                                         Node* parent) {
58 59 60 61
  Node* const instance = AllocateJSPromise(context);
  PromiseInit(instance);

  Label out(this);
62
  GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &out);
63 64 65
  CallRuntime(Runtime::kPromiseHookInit, context, instance, parent);
  Goto(&out);

66
  BIND(&out);
67 68 69
  return instance;
}

70 71
Node* PromiseBuiltinsAssembler::AllocateAndSetJSPromise(
    Node* context, v8::Promise::PromiseState status, Node* result) {
72
  DCHECK_NE(Promise::kPending, status);
73

74 75 76
  Node* const instance = AllocateJSPromise(context);
  StoreObjectFieldNoWriteBarrier(instance, JSPromise::kReactionsOrResultOffset,
                                 result);
77
  STATIC_ASSERT(JSPromise::kStatusShift == 0);
gsathya's avatar
gsathya committed
78
  StoreObjectFieldNoWriteBarrier(instance, JSPromise::kFlagsOffset,
79
                                 SmiConstant(status));
80 81
  for (int offset = JSPromise::kSize;
       offset < JSPromise::kSizeWithEmbedderFields; offset += kTaggedSize) {
82
    StoreObjectFieldNoWriteBarrier(instance, offset, SmiConstant(0));
83
  }
gsathya's avatar
gsathya committed
84 85

  Label out(this);
86
  GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &out);
gsathya's avatar
gsathya committed
87 88 89 90
  CallRuntime(Runtime::kPromiseHookInit, context, instance,
              UndefinedConstant());
  Goto(&out);

91
  BIND(&out);
gsathya's avatar
gsathya committed
92 93 94
  return instance;
}

95 96
std::pair<Node*, Node*>
PromiseBuiltinsAssembler::CreatePromiseResolvingFunctions(
97
    Node* promise, Node* debug_event, Node* native_context) {
98 99 100 101
  Node* const promise_context = CreatePromiseResolvingFunctionsContext(
      promise, debug_event, native_context);
  Node* const map = LoadContextElement(
      native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
102 103 104
  Node* const resolve_info = LoadContextElement(
      native_context,
      Context::PROMISE_CAPABILITY_DEFAULT_RESOLVE_SHARED_FUN_INDEX);
105 106
  Node* const resolve =
      AllocateFunctionWithMapAndContext(map, resolve_info, promise_context);
107 108 109
  Node* const reject_info = LoadContextElement(
      native_context,
      Context::PROMISE_CAPABILITY_DEFAULT_REJECT_SHARED_FUN_INDEX);
110 111 112 113 114
  Node* const reject =
      AllocateFunctionWithMapAndContext(map, reject_info, promise_context);
  return std::make_pair(resolve, reject);
}

115
// ES #sec-newpromisecapability
116 117 118 119
TF_BUILTIN(NewPromiseCapability, PromiseBuiltinsAssembler) {
  Node* const context = Parameter(Descriptor::kContext);
  Node* const constructor = Parameter(Descriptor::kConstructor);
  Node* const debug_event = Parameter(Descriptor::kDebugEvent);
120
  TNode<Context> const native_context = LoadNativeContext(context);
121

122 123 124
  Label if_not_constructor(this, Label::kDeferred),
      if_notcallable(this, Label::kDeferred), if_fast_promise_capability(this),
      if_slow_promise_capability(this, Label::kDeferred);
125 126
  GotoIf(TaggedIsSmi(constructor), &if_not_constructor);
  GotoIfNot(IsConstructorMap(LoadMap(constructor)), &if_not_constructor);
127 128 129
  Branch(WordEqual(constructor,
                   LoadContextElement(native_context,
                                      Context::PROMISE_FUNCTION_INDEX)),
130
         &if_fast_promise_capability, &if_slow_promise_capability);
131

132
  BIND(&if_fast_promise_capability);
133
  {
134 135
    Node* promise =
        AllocateAndInitJSPromise(native_context, UndefinedConstant());
136 137 138 139 140 141

    Node* resolve = nullptr;
    Node* reject = nullptr;
    std::tie(resolve, reject) =
        CreatePromiseResolvingFunctions(promise, debug_event, native_context);

142
    Node* capability = Allocate(PromiseCapability::kSize);
143
    StoreMapNoWriteBarrier(capability, RootIndex::kPromiseCapabilityMap);
144 145 146 147 148 149 150
    StoreObjectFieldNoWriteBarrier(capability,
                                   PromiseCapability::kPromiseOffset, promise);
    StoreObjectFieldNoWriteBarrier(capability,
                                   PromiseCapability::kResolveOffset, resolve);
    StoreObjectFieldNoWriteBarrier(capability, PromiseCapability::kRejectOffset,
                                   reject);
    Return(capability);
151 152
  }

153
  BIND(&if_slow_promise_capability);
154
  {
155
    Node* capability = Allocate(PromiseCapability::kSize);
156
    StoreMapNoWriteBarrier(capability, RootIndex::kPromiseCapabilityMap);
157
    StoreObjectFieldRoot(capability, PromiseCapability::kPromiseOffset,
158
                         RootIndex::kUndefinedValue);
159
    StoreObjectFieldRoot(capability, PromiseCapability::kResolveOffset,
160
                         RootIndex::kUndefinedValue);
161
    StoreObjectFieldRoot(capability, PromiseCapability::kRejectOffset,
162
                         RootIndex::kUndefinedValue);
163

164 165 166 167 168 169
    Node* executor_context =
        CreatePromiseGetCapabilitiesExecutorContext(capability, native_context);
    Node* executor_info = LoadContextElement(
        native_context, Context::PROMISE_GET_CAPABILITIES_EXECUTOR_SHARED_FUN);
    Node* function_map = LoadContextElement(
        native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
170 171
    TNode<JSFunction> executor = CAST(AllocateFunctionWithMapAndContext(
        function_map, executor_info, executor_context));
172

173
    Node* promise = Construct(native_context, CAST(constructor), executor);
174
    StoreObjectField(capability, PromiseCapability::kPromiseOffset, promise);
175 176

    Node* resolve =
177
        LoadObjectField(capability, PromiseCapability::kResolveOffset);
178
    GotoIf(TaggedIsSmi(resolve), &if_notcallable);
179
    GotoIfNot(IsCallable(resolve), &if_notcallable);
180 181

    Node* reject =
182
        LoadObjectField(capability, PromiseCapability::kRejectOffset);
183
    GotoIf(TaggedIsSmi(reject), &if_notcallable);
184
    GotoIfNot(IsCallable(reject), &if_notcallable);
185
    Return(capability);
186 187
  }

188
  BIND(&if_not_constructor);
189
  ThrowTypeError(context, MessageTemplate::kNotConstructor, constructor);
190

191 192
  BIND(&if_notcallable);
  ThrowTypeError(context, MessageTemplate::kPromiseNonCallable);
193 194
}

195 196 197 198 199 200
Node* PromiseBuiltinsAssembler::CreatePromiseContext(Node* native_context,
                                                     int slots) {
  DCHECK_GE(slots, Context::MIN_CONTEXT_SLOTS);

  Node* const context = AllocateInNewSpace(FixedArray::SizeFor(slots));
  InitializeFunctionContext(native_context, context, slots);
201 202 203
  return context;
}

204 205 206 207 208
Node* PromiseBuiltinsAssembler::CreatePromiseAllResolveElementContext(
    Node* promise_capability, Node* native_context) {
  CSA_ASSERT(this, IsNativeContext(native_context));

  // TODO(bmeurer): Manually fold this into a single allocation.
209 210 211 212
  TNode<Map> array_map = CAST(LoadContextElement(
      native_context, Context::JS_ARRAY_PACKED_ELEMENTS_MAP_INDEX));
  TNode<JSArray> values_array = AllocateJSArray(
      PACKED_ELEMENTS, array_map, IntPtrConstant(0), SmiConstant(0));
213

214 215
  Node* const context = CreatePromiseContext(
      native_context, PromiseBuiltins::kPromiseAllResolveElementLength);
216
  StoreContextElementNoWriteBarrier(
217 218
      context, PromiseBuiltins::kPromiseAllResolveElementRemainingSlot,
      SmiConstant(1));
219
  StoreContextElementNoWriteBarrier(
220 221
      context, PromiseBuiltins::kPromiseAllResolveElementCapabilitySlot,
      promise_capability);
222
  StoreContextElementNoWriteBarrier(
223 224
      context, PromiseBuiltins::kPromiseAllResolveElementValuesArraySlot,
      values_array);
225 226 227 228 229

  return context;
}

Node* PromiseBuiltinsAssembler::CreatePromiseAllResolveElementFunction(
230
    Node* context, TNode<Smi> index, Node* native_context) {
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
  CSA_ASSERT(this, SmiGreaterThan(index, SmiConstant(0)));
  CSA_ASSERT(this, SmiLessThanOrEqual(
                       index, SmiConstant(PropertyArray::HashField::kMax)));
  CSA_ASSERT(this, IsNativeContext(native_context));

  Node* const map = LoadContextElement(
      native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
  Node* const resolve_info = LoadContextElement(
      native_context, Context::PROMISE_ALL_RESOLVE_ELEMENT_SHARED_FUN);
  Node* const resolve =
      AllocateFunctionWithMapAndContext(map, resolve_info, context);

  STATIC_ASSERT(PropertyArray::kNoHashSentinel == 0);
  StoreObjectFieldNoWriteBarrier(resolve, JSFunction::kPropertiesOrHashOffset,
                                 index);

  return resolve;
}

250 251
Node* PromiseBuiltinsAssembler::CreatePromiseResolvingFunctionsContext(
    Node* promise, Node* debug_event, Node* native_context) {
252 253 254 255 256 257 258 259
  Node* const context = CreatePromiseContext(
      native_context, PromiseBuiltins::kPromiseContextLength);
  StoreContextElementNoWriteBarrier(context, PromiseBuiltins::kPromiseSlot,
                                    promise);
  StoreContextElementNoWriteBarrier(
      context, PromiseBuiltins::kAlreadyResolvedSlot, FalseConstant());
  StoreContextElementNoWriteBarrier(context, PromiseBuiltins::kDebugEventSlot,
                                    debug_event);
260 261 262
  return context;
}

263 264
Node* PromiseBuiltinsAssembler::CreatePromiseGetCapabilitiesExecutorContext(
    Node* promise_capability, Node* native_context) {
265
  int kContextLength = PromiseBuiltins::kCapabilitiesContextLength;
266
  Node* context = CreatePromiseContext(native_context, kContextLength);
267
  StoreContextElementNoWriteBarrier(context, PromiseBuiltins::kCapabilitySlot,
268
                                    promise_capability);
269
  return context;
270 271
}

gsathya's avatar
gsathya committed
272 273 274
Node* PromiseBuiltinsAssembler::PromiseHasHandler(Node* promise) {
  Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset);
  return IsSetWord(SmiUntag(flags), 1 << JSPromise::kHasHandlerBit);
275 276
}

gsathya's avatar
gsathya committed
277
void PromiseBuiltinsAssembler::PromiseSetHasHandler(Node* promise) {
278 279 280
  TNode<Smi> const flags =
      CAST(LoadObjectField(promise, JSPromise::kFlagsOffset));
  TNode<Smi> const new_flags =
281 282
      SmiOr(flags, SmiConstant(1 << JSPromise::kHasHandlerBit));
  StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset, new_flags);
283 284
}

285 286 287 288 289 290 291
Node* PromiseBuiltinsAssembler::IsPromiseStatus(
    Node* actual, v8::Promise::PromiseState expected) {
  return Word32Equal(actual, Int32Constant(expected));
}

Node* PromiseBuiltinsAssembler::PromiseStatus(Node* promise) {
  STATIC_ASSERT(JSPromise::kStatusShift == 0);
292 293
  TNode<Smi> const flags =
      CAST(LoadObjectField(promise, JSPromise::kFlagsOffset));
294
  return Word32And(SmiToInt32(flags), Int32Constant(JSPromise::kStatusMask));
295 296 297 298 299 300
}

void PromiseBuiltinsAssembler::PromiseSetStatus(
    Node* promise, v8::Promise::PromiseState const status) {
  CSA_ASSERT(this,
             IsPromiseStatus(PromiseStatus(promise), v8::Promise::kPending));
301
  CHECK_NE(status, v8::Promise::kPending);
302

303 304 305
  TNode<Smi> mask = SmiConstant(status);
  TNode<Smi> const flags =
      CAST(LoadObjectField(promise, JSPromise::kFlagsOffset));
306 307 308 309
  StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset,
                                 SmiOr(flags, mask));
}

310
void PromiseBuiltinsAssembler::PromiseSetHandledHint(Node* promise) {
311 312 313
  TNode<Smi> const flags =
      CAST(LoadObjectField(promise, JSPromise::kFlagsOffset));
  TNode<Smi> const new_flags =
314 315 316 317
      SmiOr(flags, SmiConstant(1 << JSPromise::kHandledHintBit));
  StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset, new_flags);
}

318
// ES #sec-performpromisethen
319 320 321 322 323 324 325 326 327
void PromiseBuiltinsAssembler::PerformPromiseThen(
    Node* context, Node* promise, Node* on_fulfilled, Node* on_rejected,
    Node* result_promise_or_capability) {
  CSA_ASSERT(this, TaggedIsNotSmi(promise));
  CSA_ASSERT(this, IsJSPromise(promise));
  CSA_ASSERT(this,
             Word32Or(IsCallable(on_fulfilled), IsUndefined(on_fulfilled)));
  CSA_ASSERT(this, Word32Or(IsCallable(on_rejected), IsUndefined(on_rejected)));
  CSA_ASSERT(this, TaggedIsNotSmi(result_promise_or_capability));
328 329 330 331 332
  CSA_ASSERT(
      this,
      Word32Or(Word32Or(IsJSPromise(result_promise_or_capability),
                        IsPromiseCapability(result_promise_or_capability)),
               IsUndefined(result_promise_or_capability)));
333 334 335 336 337 338 339

  Label if_pending(this), if_notpending(this), done(this);
  Node* const status = PromiseStatus(promise);
  Branch(IsPromiseStatus(status, v8::Promise::kPending), &if_pending,
         &if_notpending);

  BIND(&if_pending);
340
  {
341 342 343 344 345 346 347 348 349 350 351
    // The {promise} is still in "Pending" state, so we just record a new
    // PromiseReaction holding both the onFulfilled and onRejected callbacks.
    // Once the {promise} is resolved we decide on the concrete handler to
    // push onto the microtask queue.
    Node* const promise_reactions =
        LoadObjectField(promise, JSPromise::kReactionsOrResultOffset);
    Node* const reaction =
        AllocatePromiseReaction(promise_reactions, result_promise_or_capability,
                                on_fulfilled, on_rejected);
    StoreObjectField(promise, JSPromise::kReactionsOrResultOffset, reaction);
    Goto(&done);
352 353
  }

354
  BIND(&if_notpending);
355
  {
356 357 358 359 360 361 362 363
    VARIABLE(var_map, MachineRepresentation::kTagged);
    VARIABLE(var_handler, MachineRepresentation::kTagged);
    Label if_fulfilled(this), if_rejected(this, Label::kDeferred),
        enqueue(this);
    Branch(IsPromiseStatus(status, v8::Promise::kFulfilled), &if_fulfilled,
           &if_rejected);

    BIND(&if_fulfilled);
364
    {
365
      var_map.Bind(LoadRoot(RootIndex::kPromiseFulfillReactionJobTaskMap));
366 367
      var_handler.Bind(on_fulfilled);
      Goto(&enqueue);
368 369
    }

370
    BIND(&if_rejected);
371
    {
372
      CSA_ASSERT(this, IsPromiseStatus(status, v8::Promise::kRejected));
373
      var_map.Bind(LoadRoot(RootIndex::kPromiseRejectReactionJobTaskMap));
374 375 376 377
      var_handler.Bind(on_rejected);
      GotoIf(PromiseHasHandler(promise), &enqueue);
      CallRuntime(Runtime::kPromiseRevokeReject, context, promise);
      Goto(&enqueue);
378 379
    }

380 381 382 383 384 385
    BIND(&enqueue);
    Node* argument =
        LoadObjectField(promise, JSPromise::kReactionsOrResultOffset);
    Node* microtask = AllocatePromiseReactionJobTask(
        var_map.value(), context, argument, var_handler.value(),
        result_promise_or_capability);
386
    CallBuiltin(Builtins::kEnqueueMicrotask, context, microtask);
387
    Goto(&done);
388 389
  }

390
  BIND(&done);
gsathya's avatar
gsathya committed
391
  PromiseSetHasHandler(promise);
392
}
393

394
// ES #sec-performpromisethen
395 396 397 398 399 400
TF_BUILTIN(PerformPromiseThen, PromiseBuiltinsAssembler) {
  Node* const context = Parameter(Descriptor::kContext);
  Node* const promise = Parameter(Descriptor::kPromise);
  Node* const on_fulfilled = Parameter(Descriptor::kOnFulfilled);
  Node* const on_rejected = Parameter(Descriptor::kOnRejected);
  Node* const result_promise = Parameter(Descriptor::kResultPromise);
401

402
  CSA_ASSERT(this, TaggedIsNotSmi(result_promise));
403 404
  CSA_ASSERT(
      this, Word32Or(IsJSPromise(result_promise), IsUndefined(result_promise)));
405

406 407 408 409
  PerformPromiseThen(context, promise, on_fulfilled, on_rejected,
                     result_promise);
  Return(result_promise);
}
410

411 412 413
Node* PromiseBuiltinsAssembler::AllocatePromiseReaction(
    Node* next, Node* promise_or_capability, Node* fulfill_handler,
    Node* reject_handler) {
414
  Node* const reaction = Allocate(PromiseReaction::kSize);
415
  StoreMapNoWriteBarrier(reaction, RootIndex::kPromiseReactionMap);
416
  StoreObjectFieldNoWriteBarrier(reaction, PromiseReaction::kNextOffset, next);
417 418 419
  StoreObjectFieldNoWriteBarrier(reaction,
                                 PromiseReaction::kPromiseOrCapabilityOffset,
                                 promise_or_capability);
420 421 422 423 424 425
  StoreObjectFieldNoWriteBarrier(
      reaction, PromiseReaction::kFulfillHandlerOffset, fulfill_handler);
  StoreObjectFieldNoWriteBarrier(
      reaction, PromiseReaction::kRejectHandlerOffset, reject_handler);
  return reaction;
}
426

427
Node* PromiseBuiltinsAssembler::AllocatePromiseReactionJobTask(
428 429
    Node* map, Node* context, Node* argument, Node* handler,
    Node* promise_or_capability) {
430 431 432 433 434 435 436 437 438
  Node* const microtask = Allocate(PromiseReactionJobTask::kSize);
  StoreMapNoWriteBarrier(microtask, map);
  StoreObjectFieldNoWriteBarrier(
      microtask, PromiseReactionJobTask::kArgumentOffset, argument);
  StoreObjectFieldNoWriteBarrier(
      microtask, PromiseReactionJobTask::kContextOffset, context);
  StoreObjectFieldNoWriteBarrier(
      microtask, PromiseReactionJobTask::kHandlerOffset, handler);
  StoreObjectFieldNoWriteBarrier(
439 440
      microtask, PromiseReactionJobTask::kPromiseOrCapabilityOffset,
      promise_or_capability);
441 442
  return microtask;
}
443

444
Node* PromiseBuiltinsAssembler::AllocatePromiseReactionJobTask(
445 446 447 448
    RootIndex map_root_index, Node* context, Node* argument, Node* handler,
    Node* promise_or_capability) {
  DCHECK(map_root_index == RootIndex::kPromiseFulfillReactionJobTaskMap ||
         map_root_index == RootIndex::kPromiseRejectReactionJobTaskMap);
449 450
  Node* const map = LoadRoot(map_root_index);
  return AllocatePromiseReactionJobTask(map, context, argument, handler,
451
                                        promise_or_capability);
452 453
}

454 455 456 457
Node* PromiseBuiltinsAssembler::AllocatePromiseResolveThenableJobTask(
    Node* promise_to_resolve, Node* then, Node* thenable, Node* context) {
  Node* const microtask = Allocate(PromiseResolveThenableJobTask::kSize);
  StoreMapNoWriteBarrier(microtask,
458
                         RootIndex::kPromiseResolveThenableJobTaskMap);
459
  StoreObjectFieldNoWriteBarrier(
460
      microtask, PromiseResolveThenableJobTask::kContextOffset, context);
461
  StoreObjectFieldNoWriteBarrier(
462 463
      microtask, PromiseResolveThenableJobTask::kPromiseToResolveOffset,
      promise_to_resolve);
464
  StoreObjectFieldNoWriteBarrier(
465
      microtask, PromiseResolveThenableJobTask::kThenOffset, then);
466
  StoreObjectFieldNoWriteBarrier(
467 468
      microtask, PromiseResolveThenableJobTask::kThenableOffset, thenable);
  return microtask;
469 470
}

471 472 473 474 475 476
// ES #sec-triggerpromisereactions
Node* PromiseBuiltinsAssembler::TriggerPromiseReactions(
    Node* context, Node* reactions, Node* argument,
    PromiseReaction::Type type) {
  // We need to reverse the {reactions} here, since we record them on the
  // JSPromise in the reverse order.
477
  {
478
    VARIABLE(var_current, MachineRepresentation::kTagged, reactions);
479
    VARIABLE(var_reversed, MachineRepresentation::kTagged,
480
             SmiConstant(Smi::zero()));
481

482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
    Label loop(this, {&var_current, &var_reversed}), done_loop(this);
    Goto(&loop);
    BIND(&loop);
    {
      Node* current = var_current.value();
      GotoIf(TaggedIsSmi(current), &done_loop);
      var_current.Bind(LoadObjectField(current, PromiseReaction::kNextOffset));
      StoreObjectField(current, PromiseReaction::kNextOffset,
                       var_reversed.value());
      var_reversed.Bind(current);
      Goto(&loop);
    }
    BIND(&done_loop);
    reactions = var_reversed.value();
  }
497

498 499 500 501
  // Morph the {reactions} into PromiseReactionJobTasks and push them
  // onto the microtask queue.
  {
    VARIABLE(var_current, MachineRepresentation::kTagged, reactions);
502

503 504 505 506 507 508 509 510 511 512 513
    Label loop(this, {&var_current}), done_loop(this);
    Goto(&loop);
    BIND(&loop);
    {
      Node* current = var_current.value();
      GotoIf(TaggedIsSmi(current), &done_loop);
      var_current.Bind(LoadObjectField(current, PromiseReaction::kNextOffset));

      // Morph {current} from a PromiseReaction into a PromiseReactionJobTask
      // and schedule that on the microtask queue. We try to minimize the number
      // of stores here to avoid screwing up the store buffer.
514 515
      STATIC_ASSERT(static_cast<int>(PromiseReaction::kSize) ==
                    static_cast<int>(PromiseReactionJobTask::kSize));
516
      if (type == PromiseReaction::kFulfill) {
517 518
        StoreMapNoWriteBarrier(current,
                               RootIndex::kPromiseFulfillReactionJobTaskMap);
519
        StoreObjectField(current, PromiseReactionJobTask::kArgumentOffset,
520
                         argument);
521 522
        StoreObjectField(current, PromiseReactionJobTask::kContextOffset,
                         context);
523 524 525 526 527 528 529
        STATIC_ASSERT(
            static_cast<int>(PromiseReaction::kFulfillHandlerOffset) ==
            static_cast<int>(PromiseReactionJobTask::kHandlerOffset));
        STATIC_ASSERT(
            static_cast<int>(PromiseReaction::kPromiseOrCapabilityOffset) ==
            static_cast<int>(
                PromiseReactionJobTask::kPromiseOrCapabilityOffset));
530 531 532 533
      } else {
        Node* handler =
            LoadObjectField(current, PromiseReaction::kRejectHandlerOffset);
        StoreMapNoWriteBarrier(current,
534
                               RootIndex::kPromiseRejectReactionJobTaskMap);
535
        StoreObjectField(current, PromiseReactionJobTask::kArgumentOffset,
536
                         argument);
537 538 539 540
        StoreObjectField(current, PromiseReactionJobTask::kContextOffset,
                         context);
        StoreObjectField(current, PromiseReactionJobTask::kHandlerOffset,
                         handler);
541 542 543 544
        STATIC_ASSERT(
            static_cast<int>(PromiseReaction::kPromiseOrCapabilityOffset) ==
            static_cast<int>(
                PromiseReactionJobTask::kPromiseOrCapabilityOffset));
545
      }
546
      CallBuiltin(Builtins::kEnqueueMicrotask, context, current);
547 548 549 550
      Goto(&loop);
    }
    BIND(&done_loop);
  }
551 552

  return UndefinedConstant();
553
}
554

555 556 557 558
template <typename... TArgs>
Node* PromiseBuiltinsAssembler::InvokeThen(Node* native_context, Node* receiver,
                                           TArgs... args) {
  CSA_ASSERT(this, IsNativeContext(native_context));
559

560 561 562 563 564 565 566 567 568 569 570 571
  VARIABLE(var_result, MachineRepresentation::kTagged);
  Label if_fast(this), if_slow(this, Label::kDeferred), done(this, &var_result);
  GotoIf(TaggedIsSmi(receiver), &if_slow);
  Node* const receiver_map = LoadMap(receiver);
  // We can skip the "then" lookup on {receiver} if it's [[Prototype]]
  // is the (initial) Promise.prototype and the Promise#then protector
  // is intact, as that guards the lookup path for the "then" property
  // on JSPromise instances which have the (initial) %PromisePrototype%.
  BranchIfPromiseThenLookupChainIntact(native_context, receiver_map, &if_fast,
                                       &if_slow);

  BIND(&if_fast);
572
  {
573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591
    Node* const then =
        LoadContextElement(native_context, Context::PROMISE_THEN_INDEX);
    Node* const result =
        CallJS(CodeFactory::CallFunction(
                   isolate(), ConvertReceiverMode::kNotNullOrUndefined),
               native_context, then, receiver, args...);
    var_result.Bind(result);
    Goto(&done);
  }

  BIND(&if_slow);
  {
    Node* const then = GetProperty(native_context, receiver,
                                   isolate()->factory()->then_string());
    Node* const result = CallJS(
        CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined),
        native_context, then, receiver, args...);
    var_result.Bind(result);
    Goto(&done);
592
  }
593 594 595 596 597

  BIND(&done);
  return var_result.value();
}

598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652
Node* PromiseBuiltinsAssembler::InvokeResolve(Node* native_context,
                                              Node* constructor, Node* value,
                                              Label* if_exception,
                                              Variable* var_exception) {
  CSA_ASSERT(this, IsNativeContext(native_context));

  VARIABLE(var_result, MachineRepresentation::kTagged);
  Label if_fast(this), if_slow(this, Label::kDeferred), done(this, &var_result);
  // We can skip the "resolve" lookup on {constructor} if it's the
  // Promise constructor and the Promise.resolve protector is intact,
  // as that guards the lookup path for the "resolve" property on the
  // Promise constructor.
  BranchIfPromiseResolveLookupChainIntact(native_context, constructor, &if_fast,
                                          &if_slow);

  BIND(&if_fast);
  {
    Node* const result = CallBuiltin(Builtins::kPromiseResolve, native_context,
                                     constructor, value);
    GotoIfException(result, if_exception, var_exception);

    var_result.Bind(result);
    Goto(&done);
  }

  BIND(&if_slow);
  {
    Node* const resolve =
        GetProperty(native_context, constructor, factory()->resolve_string());
    GotoIfException(resolve, if_exception, var_exception);

    Node* const result = CallJS(
        CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined),
        native_context, resolve, constructor, value);
    GotoIfException(result, if_exception, var_exception);

    var_result.Bind(result);
    Goto(&done);
  }

  BIND(&done);
  return var_result.value();
}

void PromiseBuiltinsAssembler::BranchIfPromiseResolveLookupChainIntact(
    Node* native_context, Node* constructor, Label* if_fast, Label* if_slow) {
  CSA_ASSERT(this, IsNativeContext(native_context));

  GotoIfForceSlowPath(if_slow);
  Node* const promise_fun =
      LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
  GotoIfNot(WordEqual(promise_fun, constructor), if_slow);
  Branch(IsPromiseResolveProtectorCellInvalid(), if_slow, if_fast);
}

653 654 655 656 657 658 659 660
void PromiseBuiltinsAssembler::GotoIfNotPromiseResolveLookupChainIntact(
    Node* native_context, Node* constructor, Label* if_slow) {
  Label if_fast(this);
  BranchIfPromiseResolveLookupChainIntact(native_context, constructor, &if_fast,
                                          if_slow);
  BIND(&if_fast);
}

661 662 663 664 665 666 667 668 669 670
void PromiseBuiltinsAssembler::BranchIfPromiseSpeciesLookupChainIntact(
    Node* native_context, Node* promise_map, Label* if_fast, Label* if_slow) {
  CSA_ASSERT(this, IsNativeContext(native_context));
  CSA_ASSERT(this, IsJSPromiseMap(promise_map));

  Node* const promise_prototype =
      LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
  GotoIfForceSlowPath(if_slow);
  GotoIfNot(WordEqual(LoadMapPrototype(promise_map), promise_prototype),
            if_slow);
671
  Branch(IsPromiseSpeciesProtectorCellInvalid(), if_slow, if_fast);
672 673 674 675 676 677 678 679 680 681 682 683 684 685
}

void PromiseBuiltinsAssembler::BranchIfPromiseThenLookupChainIntact(
    Node* native_context, Node* receiver_map, Label* if_fast, Label* if_slow) {
  CSA_ASSERT(this, IsMap(receiver_map));
  CSA_ASSERT(this, IsNativeContext(native_context));

  GotoIfForceSlowPath(if_slow);
  GotoIfNot(IsJSPromiseMap(receiver_map), if_slow);
  Node* const promise_prototype =
      LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
  GotoIfNot(WordEqual(LoadMapPrototype(receiver_map), promise_prototype),
            if_slow);
  Branch(IsPromiseThenProtectorCellInvalid(), if_slow, if_fast);
686 687
}

688 689 690
void PromiseBuiltinsAssembler::BranchIfAccessCheckFailed(
    Node* context, Node* native_context, Node* promise_constructor,
    Node* executor, Label* if_noaccess) {
691
  VARIABLE(var_executor, MachineRepresentation::kTagged);
692 693 694 695 696 697 698
  var_executor.Bind(executor);
  Label has_access(this), call_runtime(this, Label::kDeferred);

  // If executor is a bound function, load the bound function until we've
  // reached an actual function.
  Label found_function(this), loop_over_bound_function(this, &var_executor);
  Goto(&loop_over_bound_function);
699
  BIND(&loop_over_bound_function);
700 701 702
  {
    Node* executor_type = LoadInstanceType(var_executor.value());
    GotoIf(InstanceTypeEqual(executor_type, JS_FUNCTION_TYPE), &found_function);
703 704
    GotoIfNot(InstanceTypeEqual(executor_type, JS_BOUND_FUNCTION_TYPE),
              &call_runtime);
705 706 707 708 709 710 711 712
    var_executor.Bind(LoadObjectField(
        var_executor.value(), JSBoundFunction::kBoundTargetFunctionOffset));
    Goto(&loop_over_bound_function);
  }

  // Load the context from the function and compare it to the Promise
  // constructor's context. If they match, everything is fine, otherwise, bail
  // out to the runtime.
713
  BIND(&found_function);
714 715 716 717 718 719 720 721
  {
    Node* function_context =
        LoadObjectField(var_executor.value(), JSFunction::kContextOffset);
    Node* native_function_context = LoadNativeContext(function_context);
    Branch(WordEqual(native_context, native_function_context), &has_access,
           &call_runtime);
  }

722
  BIND(&call_runtime);
723 724 725
  {
    Branch(WordEqual(CallRuntime(Runtime::kAllowDynamicFunction, context,
                                 promise_constructor),
726
                     TrueConstant()),
727 728 729
           &has_access, if_noaccess);
  }

730
  BIND(&has_access);
731 732
}

733 734 735 736
void PromiseBuiltinsAssembler::SetForwardingHandlerIfTrue(
    Node* context, Node* condition, const NodeGenerator& object) {
  Label done(this);
  GotoIfNot(condition, &done);
737 738 739 740
  SetPropertyStrict(
      CAST(context), CAST(object()),
      HeapConstant(factory()->promise_forwarding_handler_symbol()),
      TrueConstant());
741 742 743 744 745 746 747 748 749 750 751
  Goto(&done);
  BIND(&done);
}

void PromiseBuiltinsAssembler::SetPromiseHandledByIfTrue(
    Node* context, Node* condition, Node* promise,
    const NodeGenerator& handled_by) {
  Label done(this);
  GotoIfNot(condition, &done);
  GotoIf(TaggedIsSmi(promise), &done);
  GotoIfNot(HasInstanceType(promise, JS_PROMISE_TYPE), &done);
752 753 754
  SetPropertyStrict(CAST(context), CAST(promise),
                    HeapConstant(factory()->promise_handled_by_symbol()),
                    CAST(handled_by()));
755 756 757 758
  Goto(&done);
  BIND(&done);
}

759 760 761 762
// ES #sec-promise-reject-functions
TF_BUILTIN(PromiseCapabilityDefaultReject, PromiseBuiltinsAssembler) {
  Node* const reason = Parameter(Descriptor::kReason);
  Node* const context = Parameter(Descriptor::kContext);
gsathya's avatar
gsathya committed
763

764
  // 2. Let promise be F.[[Promise]].
765 766
  Node* const promise =
      LoadContextElement(context, PromiseBuiltins::kPromiseSlot);
767

768
  // 3. Let alreadyResolved be F.[[AlreadyResolved]].
769 770
  Label if_already_resolved(this, Label::kDeferred);
  Node* const already_resolved =
771
      LoadContextElement(context, PromiseBuiltins::kAlreadyResolvedSlot);
772

773
  // 4. If alreadyResolved.[[Value]] is true, return undefined.
774
  GotoIf(IsTrue(already_resolved), &if_already_resolved);
775

776
  // 5. Set alreadyResolved.[[Value]] to true.
777 778
  StoreContextElementNoWriteBarrier(
      context, PromiseBuiltins::kAlreadyResolvedSlot, TrueConstant());
gsathya's avatar
gsathya committed
779

780
  // 6. Return RejectPromise(promise, reason).
781 782
  Node* const debug_event =
      LoadContextElement(context, PromiseBuiltins::kDebugEventSlot);
783 784
  Return(CallBuiltin(Builtins::kRejectPromise, context, promise, reason,
                     debug_event));
785 786 787 788 789 790

  BIND(&if_already_resolved);
  {
    Return(CallRuntime(Runtime::kPromiseRejectAfterResolved, context, promise,
                       reason));
  }
791 792
}

793 794 795
// ES #sec-promise-resolve-functions
TF_BUILTIN(PromiseCapabilityDefaultResolve, PromiseBuiltinsAssembler) {
  Node* const resolution = Parameter(Descriptor::kResolution);
796 797
  Node* const context = Parameter(Descriptor::kContext);

798
  // 2. Let promise be F.[[Promise]].
799 800
  Node* const promise =
      LoadContextElement(context, PromiseBuiltins::kPromiseSlot);
801 802

  // 3. Let alreadyResolved be F.[[AlreadyResolved]].
803 804
  Label if_already_resolved(this, Label::kDeferred);
  Node* const already_resolved =
805
      LoadContextElement(context, PromiseBuiltins::kAlreadyResolvedSlot);
806

807
  // 4. If alreadyResolved.[[Value]] is true, return undefined.
808
  GotoIf(IsTrue(already_resolved), &if_already_resolved);
809 810

  // 5. Set alreadyResolved.[[Value]] to true.
811 812
  StoreContextElementNoWriteBarrier(
      context, PromiseBuiltins::kAlreadyResolvedSlot, TrueConstant());
813 814 815 816

  // The rest of the logic (and the catch prediction) is
  // encapsulated in the dedicated ResolvePromise builtin.
  Return(CallBuiltin(Builtins::kResolvePromise, context, promise, resolution));
817 818 819 820 821 822

  BIND(&if_already_resolved);
  {
    Return(CallRuntime(Runtime::kPromiseResolveAfterResolved, context, promise,
                       resolution));
  }
gsathya's avatar
gsathya committed
823 824
}

825 826
TF_BUILTIN(PromiseConstructorLazyDeoptContinuation, PromiseBuiltinsAssembler) {
  Node* promise = Parameter(Descriptor::kPromise);
827 828 829 830 831 832 833 834 835 836 837 838
  Node* reject = Parameter(Descriptor::kReject);
  Node* exception = Parameter(Descriptor::kException);
  Node* const context = Parameter(Descriptor::kContext);

  Label finally(this);

  GotoIf(IsTheHole(exception), &finally);
  CallJS(CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined),
         context, reject, UndefinedConstant(), exception);
  Goto(&finally);

  BIND(&finally);
839 840 841
  Return(promise);
}

842
// ES6 #sec-promise-executor
gsathya's avatar
gsathya committed
843
TF_BUILTIN(PromiseConstructor, PromiseBuiltinsAssembler) {
844
  Node* const executor = Parameter(Descriptor::kExecutor);
845
  Node* const new_target = Parameter(Descriptor::kJSNewTarget);
846
  Node* const context = Parameter(Descriptor::kContext);
gsathya's avatar
gsathya committed
847 848 849 850 851 852 853 854 855 856 857
  Isolate* isolate = this->isolate();

  Label if_targetisundefined(this, Label::kDeferred);

  GotoIf(IsUndefined(new_target), &if_targetisundefined);

  Label if_notcallable(this, Label::kDeferred);

  GotoIf(TaggedIsSmi(executor), &if_notcallable);

  Node* const executor_map = LoadMap(executor);
858
  GotoIfNot(IsCallableMap(executor_map), &if_notcallable);
gsathya's avatar
gsathya committed
859 860 861 862 863 864 865

  Node* const native_context = LoadNativeContext(context);
  Node* const promise_fun =
      LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
  Node* const is_debug_active = IsDebugActive();
  Label if_targetisnotmodified(this),
      if_targetismodified(this, Label::kDeferred), run_executor(this),
866 867 868 869
      debug_push(this), if_noaccess(this, Label::kDeferred);

  BranchIfAccessCheckFailed(context, native_context, promise_fun, executor,
                            &if_noaccess);
gsathya's avatar
gsathya committed
870 871 872 873

  Branch(WordEqual(promise_fun, new_target), &if_targetisnotmodified,
         &if_targetismodified);

874 875 876
  VARIABLE(var_result, MachineRepresentation::kTagged);
  VARIABLE(var_reject_call, MachineRepresentation::kTagged);
  VARIABLE(var_reason, MachineRepresentation::kTagged);
gsathya's avatar
gsathya committed
877

878
  BIND(&if_targetisnotmodified);
gsathya's avatar
gsathya committed
879
  {
gsathya's avatar
gsathya committed
880
    Node* const instance = AllocateAndInitJSPromise(context);
gsathya's avatar
gsathya committed
881
    var_result.Bind(instance);
gsathya's avatar
gsathya committed
882
    Goto(&debug_push);
gsathya's avatar
gsathya committed
883 884
  }

885
  BIND(&if_targetismodified);
gsathya's avatar
gsathya committed
886
  {
887 888 889
    ConstructorBuiltinsAssembler constructor_assembler(this->state());
    Node* const instance = constructor_assembler.EmitFastNewObject(
        context, promise_fun, new_target);
gsathya's avatar
gsathya committed
890
    PromiseInit(instance);
gsathya's avatar
gsathya committed
891 892
    var_result.Bind(instance);

893
    GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &debug_push);
gsathya's avatar
gsathya committed
894
    CallRuntime(Runtime::kPromiseHookInit, context, instance,
895 896
                UndefinedConstant());
    Goto(&debug_push);
gsathya's avatar
gsathya committed
897 898
  }

899
  BIND(&debug_push);
gsathya's avatar
gsathya committed
900
  {
901
    GotoIfNot(is_debug_active, &run_executor);
gsathya's avatar
gsathya committed
902 903 904 905
    CallRuntime(Runtime::kDebugPushPromise, context, var_result.value());
    Goto(&run_executor);
  }

906
  BIND(&run_executor);
gsathya's avatar
gsathya committed
907 908 909
  {
    Label out(this), if_rejectpromise(this), debug_pop(this, Label::kDeferred);

910 911 912
    Node *resolve, *reject;
    std::tie(resolve, reject) = CreatePromiseResolvingFunctions(
        var_result.value(), TrueConstant(), native_context);
gsathya's avatar
gsathya committed
913

914 915 916
    Node* const maybe_exception = CallJS(
        CodeFactory::Call(isolate, ConvertReceiverMode::kNullOrUndefined),
        context, executor, UndefinedConstant(), resolve, reject);
gsathya's avatar
gsathya committed
917 918 919 920

    GotoIfException(maybe_exception, &if_rejectpromise, &var_reason);
    Branch(is_debug_active, &debug_pop, &out);

921
    BIND(&if_rejectpromise);
gsathya's avatar
gsathya committed
922
    {
923 924
      CallJS(CodeFactory::Call(isolate, ConvertReceiverMode::kNullOrUndefined),
             context, reject, UndefinedConstant(), var_reason.value());
gsathya's avatar
gsathya committed
925 926 927
      Branch(is_debug_active, &debug_pop, &out);
    }

928
    BIND(&debug_pop);
gsathya's avatar
gsathya committed
929 930 931 932
    {
      CallRuntime(Runtime::kDebugPopPromise, context);
      Goto(&out);
    }
933
    BIND(&out);
gsathya's avatar
gsathya committed
934 935 936 937
    Return(var_result.value());
  }

  // 1. If NewTarget is undefined, throw a TypeError exception.
938
  BIND(&if_targetisundefined);
939
  ThrowTypeError(context, MessageTemplate::kNotAPromise, new_target);
gsathya's avatar
gsathya committed
940 941

  // 2. If IsCallable(executor) is false, throw a TypeError exception.
942
  BIND(&if_notcallable);
943
  ThrowTypeError(context, MessageTemplate::kResolverNotAFunction, executor);
944 945

  // Silently fail if the stack looks fishy.
946
  BIND(&if_noaccess);
947 948 949 950 951 952
  {
    Node* const counter_id =
        SmiConstant(v8::Isolate::kPromiseConstructorReturnedUndefined);
    CallRuntime(Runtime::kIncrementUseCounter, context, counter_id);
    Return(UndefinedConstant());
  }
gsathya's avatar
gsathya committed
953 954
}

955
// V8 Extras: v8.createPromise(parent)
gsathya's avatar
gsathya committed
956
TF_BUILTIN(PromiseInternalConstructor, PromiseBuiltinsAssembler) {
957 958
  Node* const parent = Parameter(Descriptor::kParent);
  Node* const context = Parameter(Descriptor::kContext);
gsathya's avatar
gsathya committed
959
  Return(AllocateAndInitJSPromise(context, parent));
gsathya's avatar
gsathya committed
960 961
}

962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979
// V8 Extras: v8.rejectPromise(promise, reason)
TF_BUILTIN(PromiseInternalReject, PromiseBuiltinsAssembler) {
  Node* const promise = Parameter(Descriptor::kPromise);
  Node* const reason = Parameter(Descriptor::kReason);
  Node* const context = Parameter(Descriptor::kContext);
  // We pass true to trigger the debugger's on exception handler.
  Return(CallBuiltin(Builtins::kRejectPromise, context, promise, reason,
                     TrueConstant()));
}

// V8 Extras: v8.resolvePromise(promise, resolution)
TF_BUILTIN(PromiseInternalResolve, PromiseBuiltinsAssembler) {
  Node* const promise = Parameter(Descriptor::kPromise);
  Node* const resolution = Parameter(Descriptor::kResolution);
  Node* const context = Parameter(Descriptor::kContext);
  Return(CallBuiltin(Builtins::kResolvePromise, context, promise, resolution));
}

980
// ES#sec-promise.prototype.then
981 982
// Promise.prototype.then ( onFulfilled, onRejected )
TF_BUILTIN(PromisePrototypeThen, PromiseBuiltinsAssembler) {
gsathya's avatar
gsathya committed
983
  // 1. Let promise be the this value.
984
  Node* const promise = Parameter(Descriptor::kReceiver);
985 986
  Node* const on_fulfilled = Parameter(Descriptor::kOnFulfilled);
  Node* const on_rejected = Parameter(Descriptor::kOnRejected);
987
  Node* const context = Parameter(Descriptor::kContext);
gsathya's avatar
gsathya committed
988

989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070
  // 2. If IsPromise(promise) is false, throw a TypeError exception.
  ThrowIfNotInstanceType(context, promise, JS_PROMISE_TYPE,
                         "Promise.prototype.then");

  // 3. Let C be ? SpeciesConstructor(promise, %Promise%).
  Label fast_promise_capability(this), slow_constructor(this, Label::kDeferred),
      slow_promise_capability(this, Label::kDeferred);
  Node* const native_context = LoadNativeContext(context);
  Node* const promise_fun =
      LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
  Node* const promise_map = LoadMap(promise);
  BranchIfPromiseSpeciesLookupChainIntact(
      native_context, promise_map, &fast_promise_capability, &slow_constructor);

  BIND(&slow_constructor);
  Node* const constructor =
      SpeciesConstructor(native_context, promise, promise_fun);
  Branch(WordEqual(constructor, promise_fun), &fast_promise_capability,
         &slow_promise_capability);

  // 4. Let resultCapability be ? NewPromiseCapability(C).
  Label perform_promise_then(this);
  VARIABLE(var_result_promise, MachineRepresentation::kTagged);
  VARIABLE(var_result_promise_or_capability, MachineRepresentation::kTagged);

  BIND(&fast_promise_capability);
  {
    Node* const result_promise = AllocateAndInitJSPromise(context, promise);
    var_result_promise_or_capability.Bind(result_promise);
    var_result_promise.Bind(result_promise);
    Goto(&perform_promise_then);
  }

  BIND(&slow_promise_capability);
  {
    Node* const debug_event = TrueConstant();
    Node* const capability = CallBuiltin(Builtins::kNewPromiseCapability,
                                         context, constructor, debug_event);
    var_result_promise.Bind(
        LoadObjectField(capability, PromiseCapability::kPromiseOffset));
    var_result_promise_or_capability.Bind(capability);
    Goto(&perform_promise_then);
  }

  // 5. Return PerformPromiseThen(promise, onFulfilled, onRejected,
  //    resultCapability).
  BIND(&perform_promise_then);
  {
    // We do some work of the PerformPromiseThen operation here, in that
    // we check the handlers and turn non-callable handlers into undefined.
    // This is because this is the one and only callsite of PerformPromiseThen
    // that has to do this.

    // 3. If IsCallable(onFulfilled) is false, then
    //    a. Set onFulfilled to undefined.
    VARIABLE(var_on_fulfilled, MachineRepresentation::kTagged, on_fulfilled);
    Label if_fulfilled_done(this), if_fulfilled_notcallable(this);
    GotoIf(TaggedIsSmi(on_fulfilled), &if_fulfilled_notcallable);
    Branch(IsCallable(on_fulfilled), &if_fulfilled_done,
           &if_fulfilled_notcallable);
    BIND(&if_fulfilled_notcallable);
    var_on_fulfilled.Bind(UndefinedConstant());
    Goto(&if_fulfilled_done);
    BIND(&if_fulfilled_done);

    // 4. If IsCallable(onRejected) is false, then
    //    a. Set onRejected to undefined.
    VARIABLE(var_on_rejected, MachineRepresentation::kTagged, on_rejected);
    Label if_rejected_done(this), if_rejected_notcallable(this);
    GotoIf(TaggedIsSmi(on_rejected), &if_rejected_notcallable);
    Branch(IsCallable(on_rejected), &if_rejected_done,
           &if_rejected_notcallable);
    BIND(&if_rejected_notcallable);
    var_on_rejected.Bind(UndefinedConstant());
    Goto(&if_rejected_done);
    BIND(&if_rejected_done);

    PerformPromiseThen(context, promise, var_on_fulfilled.value(),
                       var_on_rejected.value(),
                       var_result_promise_or_capability.value());
    Return(var_result_promise.value());
  }
gsathya's avatar
gsathya committed
1071
}
1072

1073 1074 1075 1076 1077 1078 1079
// ES#sec-promise.prototype.catch
// Promise.prototype.catch ( onRejected )
TF_BUILTIN(PromisePrototypeCatch, PromiseBuiltinsAssembler) {
  // 1. Let promise be the this value.
  Node* const receiver = Parameter(Descriptor::kReceiver);
  Node* const on_fulfilled = UndefinedConstant();
  Node* const on_rejected = Parameter(Descriptor::kOnRejected);
gsathya's avatar
gsathya committed
1080
  Node* const context = Parameter(Descriptor::kContext);
gsathya's avatar
gsathya committed
1081

1082 1083 1084 1085 1086
  // 2. Return ? Invoke(promise, "then", « undefined, onRejected »).
  Node* const native_context = LoadNativeContext(context);
  Return(InvokeThen(native_context, receiver, on_fulfilled, on_rejected));
}

1087
// ES #sec-promiseresolvethenablejob
1088
TF_BUILTIN(PromiseResolveThenableJob, PromiseBuiltinsAssembler) {
1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112
  Node* const native_context = Parameter(Descriptor::kContext);
  Node* const promise_to_resolve = Parameter(Descriptor::kPromiseToResolve);
  Node* const thenable = Parameter(Descriptor::kThenable);
  Node* const then = Parameter(Descriptor::kThen);

  CSA_ASSERT(this, TaggedIsNotSmi(thenable));
  CSA_ASSERT(this, IsJSReceiver(thenable));
  CSA_ASSERT(this, IsJSPromise(promise_to_resolve));
  CSA_ASSERT(this, IsNativeContext(native_context));

  // We can use a simple optimization here if we know that {then} is the initial
  // Promise.prototype.then method, and {thenable} is a JSPromise whose
  // @@species lookup chain is intact: We can connect {thenable} and
  // {promise_to_resolve} directly in that case and avoid the allocation of a
  // temporary JSPromise and the closures plus context.
  //
  // We take the generic (slow-)path if a PromiseHook is enabled or the debugger
  // is active, to make sure we expose spec compliant behavior.
  Label if_fast(this), if_slow(this, Label::kDeferred);
  Node* const promise_then =
      LoadContextElement(native_context, Context::PROMISE_THEN_INDEX);
  GotoIfNot(WordEqual(then, promise_then), &if_slow);
  Node* const thenable_map = LoadMap(thenable);
  GotoIfNot(IsJSPromiseMap(thenable_map), &if_slow);
1113 1114
  GotoIf(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(),
         &if_slow);
1115 1116 1117 1118
  BranchIfPromiseSpeciesLookupChainIntact(native_context, thenable_map,
                                          &if_fast, &if_slow);

  BIND(&if_fast);
gsathya's avatar
gsathya committed
1119
  {
1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163
    // We know that the {thenable} is a JSPromise, which doesn't require
    // any special treatment and that {then} corresponds to the initial
    // Promise.prototype.then method. So instead of allocating a temporary
    // JSPromise to connect the {thenable} with the {promise_to_resolve},
    // we can directly schedule the {promise_to_resolve} with default
    // handlers onto the {thenable} promise. This does not only save the
    // JSPromise allocation, but also avoids the allocation of the two
    // resolving closures and the shared context.
    //
    // What happens normally in this case is
    //
    //   resolve, reject = CreateResolvingFunctions(promise_to_resolve)
    //   result_capability = NewPromiseCapability(%Promise%)
    //   PerformPromiseThen(thenable, resolve, reject, result_capability)
    //
    // which means that PerformPromiseThen will either schedule a new
    // PromiseReaction with resolve and reject or a PromiseReactionJob
    // with resolve or reject based on the state of {thenable}. And
    // resolve or reject will just invoke the default [[Resolve]] or
    // [[Reject]] functions on the {promise_to_resolve}.
    //
    // This is the same as just doing
    //
    //   PerformPromiseThen(thenable, undefined, undefined, promise_to_resolve)
    //
    // which performs exactly the same (observable) steps.
    TailCallBuiltin(Builtins::kPerformPromiseThen, native_context, thenable,
                    UndefinedConstant(), UndefinedConstant(),
                    promise_to_resolve);
  }

  BIND(&if_slow);
  {
    Node* resolve = nullptr;
    Node* reject = nullptr;
    std::tie(resolve, reject) = CreatePromiseResolvingFunctions(
        promise_to_resolve, FalseConstant(), native_context);

    Label if_exception(this, Label::kDeferred);
    VARIABLE(var_exception, MachineRepresentation::kTagged, TheHoleConstant());
    Node* const result = CallJS(
        CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined),
        native_context, then, thenable, resolve, reject);
    GotoIfException(result, &if_exception, &var_exception);
1164
    Return(result);
1165 1166 1167 1168 1169 1170

    BIND(&if_exception);
    {
      // We need to reject the {thenable}.
      Node* const result = CallJS(
          CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
1171
          native_context, reject, UndefinedConstant(), var_exception.value());
1172 1173
      Return(result);
    }
gsathya's avatar
gsathya committed
1174
  }
1175
}
gsathya's avatar
gsathya committed
1176

1177
// ES #sec-promisereactionjob
1178
void PromiseBuiltinsAssembler::PromiseReactionJob(Node* context, Node* argument,
1179 1180
                                                  Node* handler,
                                                  Node* promise_or_capability,
1181 1182
                                                  PromiseReaction::Type type) {
  CSA_ASSERT(this, TaggedIsNotSmi(handler));
1183 1184
  CSA_ASSERT(this, Word32Or(IsUndefined(handler), IsCallable(handler)));
  CSA_ASSERT(this, TaggedIsNotSmi(promise_or_capability));
1185 1186 1187 1188
  CSA_ASSERT(this,
             Word32Or(Word32Or(IsJSPromise(promise_or_capability),
                               IsPromiseCapability(promise_or_capability)),
                      IsUndefined(promise_or_capability)));
1189 1190

  VARIABLE(var_handler_result, MachineRepresentation::kTagged, argument);
1191 1192
  Label if_handler_callable(this), if_fulfill(this), if_reject(this),
      if_internal(this);
1193 1194 1195
  Branch(IsUndefined(handler),
         type == PromiseReaction::kFulfill ? &if_fulfill : &if_reject,
         &if_handler_callable);
1196 1197

  BIND(&if_handler_callable);
gsathya's avatar
gsathya committed
1198
  {
1199 1200 1201 1202 1203
    Node* const result = CallJS(
        CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
        context, handler, UndefinedConstant(), argument);
    GotoIfException(result, &if_reject, &var_handler_result);
    var_handler_result.Bind(result);
1204 1205 1206 1207 1208 1209 1210 1211 1212 1213
    Branch(IsUndefined(promise_or_capability), &if_internal, &if_fulfill);
  }

  BIND(&if_internal);
  {
    // There's no [[Capability]] for this promise reaction job, which
    // means that this is a specification-internal operation (aka await)
    // where the result does not matter (see the specification change in
    // https://github.com/tc39/ecma262/pull/1146 for details).
    Return(UndefinedConstant());
1214
  }
1215

1216
  BIND(&if_fulfill);
1217
  {
1218 1219
    Label if_promise(this), if_promise_capability(this, Label::kDeferred);
    Node* const value = var_handler_result.value();
1220 1221
    Branch(IsPromiseCapability(promise_or_capability), &if_promise_capability,
           &if_promise);
1222

1223
    BIND(&if_promise);
1224
    {
1225 1226 1227
      // For fast native promises we can skip the indirection
      // via the promiseCapability.[[Resolve]] function and
      // run the resolve logic directly from here.
1228 1229
      TailCallBuiltin(Builtins::kResolvePromise, context, promise_or_capability,
                      value);
1230
    }
gsathya's avatar
gsathya committed
1231

1232
    BIND(&if_promise_capability);
1233
    {
1234 1235
      // In the general case we need to call the (user provided)
      // promiseCapability.[[Resolve]] function.
1236 1237
      Node* const resolve = LoadObjectField(promise_or_capability,
                                            PromiseCapability::kResolveOffset);
1238
      Node* const result = CallJS(
1239 1240 1241
          CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
          context, resolve, UndefinedConstant(), value);
      GotoIfException(result, &if_reject, &var_handler_result);
1242
      Return(result);
1243
    }
1244
  }
gsathya's avatar
gsathya committed
1245

1246 1247 1248 1249
  BIND(&if_reject);
  if (type == PromiseReaction::kReject) {
    Label if_promise(this), if_promise_capability(this, Label::kDeferred);
    Node* const reason = var_handler_result.value();
1250 1251
    Branch(IsPromiseCapability(promise_or_capability), &if_promise_capability,
           &if_promise);
gsathya's avatar
gsathya committed
1252

1253
    BIND(&if_promise);
gsathya's avatar
gsathya committed
1254
    {
1255 1256 1257
      // For fast native promises we can skip the indirection
      // via the promiseCapability.[[Reject]] function and
      // run the resolve logic directly from here.
1258 1259
      TailCallBuiltin(Builtins::kRejectPromise, context, promise_or_capability,
                      reason, FalseConstant());
gsathya's avatar
gsathya committed
1260 1261
    }

1262 1263 1264 1265 1266 1267 1268
    BIND(&if_promise_capability);
    {
      // In the general case we need to call the (user provided)
      // promiseCapability.[[Reject]] function.
      Label if_exception(this, Label::kDeferred);
      VARIABLE(var_exception, MachineRepresentation::kTagged,
               TheHoleConstant());
1269 1270
      Node* const reject = LoadObjectField(promise_or_capability,
                                           PromiseCapability::kRejectOffset);
1271 1272 1273 1274
      Node* const result = CallJS(
          CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
          context, reject, UndefinedConstant(), reason);
      GotoIfException(result, &if_exception, &var_exception);
1275
      Return(result);
1276 1277 1278

      // Swallow the exception here.
      BIND(&if_exception);
1279
      TailCallRuntime(Runtime::kReportMessage, context, var_exception.value());
1280 1281 1282 1283 1284 1285
    }
  } else {
    // We have to call out to the dedicated PromiseRejectReactionJob builtin
    // here, instead of just doing the work inline, as otherwise the catch
    // predictions in the debugger will be wrong, which just walks the stack
    // and checks for certain builtins.
1286
    TailCallBuiltin(Builtins::kPromiseRejectReactionJob, context,
1287 1288
                    var_handler_result.value(), UndefinedConstant(),
                    promise_or_capability);
1289
  }
1290
}
1291

1292
// ES #sec-promisereactionjob
1293
TF_BUILTIN(PromiseFulfillReactionJob, PromiseBuiltinsAssembler) {
1294
  Node* const context = Parameter(Descriptor::kContext);
1295 1296
  Node* const value = Parameter(Descriptor::kValue);
  Node* const handler = Parameter(Descriptor::kHandler);
1297 1298
  Node* const promise_or_capability =
      Parameter(Descriptor::kPromiseOrCapability);
1299

1300
  PromiseReactionJob(context, value, handler, promise_or_capability,
1301 1302
                     PromiseReaction::kFulfill);
}
1303

1304
// ES #sec-promisereactionjob
1305 1306 1307 1308
TF_BUILTIN(PromiseRejectReactionJob, PromiseBuiltinsAssembler) {
  Node* const context = Parameter(Descriptor::kContext);
  Node* const reason = Parameter(Descriptor::kReason);
  Node* const handler = Parameter(Descriptor::kHandler);
1309 1310
  Node* const promise_or_capability =
      Parameter(Descriptor::kPromiseOrCapability);
1311

1312
  PromiseReactionJob(context, reason, handler, promise_or_capability,
1313
                     PromiseReaction::kReject);
1314 1315
}

1316
TF_BUILTIN(PromiseResolveTrampoline, PromiseBuiltinsAssembler) {
1317
  //  1. Let C be the this value.
1318 1319 1320
  Node* receiver = Parameter(Descriptor::kReceiver);
  Node* value = Parameter(Descriptor::kValue);
  Node* context = Parameter(Descriptor::kContext);
1321 1322 1323 1324 1325

  // 2. If Type(C) is not Object, throw a TypeError exception.
  ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject,
                       "PromiseResolve");

1326 1327 1328 1329 1330 1331 1332 1333
  // 3. Return ? PromiseResolve(C, x).
  Return(CallBuiltin(Builtins::kPromiseResolve, context, receiver, value));
}

TF_BUILTIN(PromiseResolve, PromiseBuiltinsAssembler) {
  Node* constructor = Parameter(Descriptor::kConstructor);
  Node* value = Parameter(Descriptor::kValue);
  Node* context = Parameter(Descriptor::kContext);
1334 1335

  CSA_ASSERT(this, IsJSReceiver(constructor));
1336

1337 1338 1339 1340
  Node* const native_context = LoadNativeContext(context);
  Node* const promise_fun =
      LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);

1341
  Label if_slow_constructor(this, Label::kDeferred), if_need_to_allocate(this);
1342

1343
  // Check if {value} is a JSPromise.
1344
  GotoIf(TaggedIsSmi(value), &if_need_to_allocate);
1345 1346
  Node* const value_map = LoadMap(value);
  GotoIfNot(IsJSPromiseMap(value_map), &if_need_to_allocate);
1347

1348 1349 1350 1351 1352
  // We can skip the "constructor" lookup on {value} if it's [[Prototype]]
  // is the (initial) Promise.prototype and the @@species protector is
  // intact, as that guards the lookup path for "constructor" on
  // JSPromise instances which have the (initial) Promise.prototype.
  Node* const promise_prototype =
1353
      LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
1354 1355
  GotoIfNot(WordEqual(LoadMapPrototype(value_map), promise_prototype),
            &if_slow_constructor);
1356
  GotoIf(IsPromiseSpeciesProtectorCellInvalid(), &if_slow_constructor);
1357

1358 1359 1360 1361 1362
  // If the {constructor} is the Promise function, we just immediately
  // return the {value} here and don't bother wrapping it into a
  // native Promise.
  GotoIfNot(WordEqual(promise_fun, constructor), &if_slow_constructor);
  Return(value);
1363

1364
  // At this point, value or/and constructor are not native promises, but
1365
  // they could be of the same subclass.
1366
  BIND(&if_slow_constructor);
1367
  {
1368 1369 1370
    Node* const value_constructor =
        GetProperty(context, value, isolate()->factory()->constructor_string());
    GotoIfNot(WordEqual(value_constructor, constructor), &if_need_to_allocate);
1371 1372 1373
    Return(value);
  }

1374
  BIND(&if_need_to_allocate);
1375
  {
1376
    Label if_nativepromise(this), if_notnativepromise(this, Label::kDeferred);
1377
    Branch(WordEqual(promise_fun, constructor), &if_nativepromise,
1378
           &if_notnativepromise);
1379 1380 1381

    // This adds a fast path for native promises that don't need to
    // create NewPromiseCapability.
1382
    BIND(&if_nativepromise);
1383 1384
    {
      Node* const result = AllocateAndInitJSPromise(context);
1385
      CallBuiltin(Builtins::kResolvePromise, context, result, value);
1386 1387 1388
      Return(result);
    }

1389
    BIND(&if_notnativepromise);
1390
    {
1391 1392 1393
      Node* const debug_event = TrueConstant();
      Node* const capability = CallBuiltin(Builtins::kNewPromiseCapability,
                                           context, constructor, debug_event);
1394 1395

      Node* const resolve =
1396
          LoadObjectField(capability, PromiseCapability::kResolveOffset);
1397 1398 1399
      CallJS(
          CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
          context, resolve, UndefinedConstant(), value);
1400 1401

      Node* const result =
1402
          LoadObjectField(capability, PromiseCapability::kPromiseOffset);
1403 1404 1405 1406 1407
      Return(result);
    }
  }
}

1408
// ES6 #sec-getcapabilitiesexecutor-functions
1409
TF_BUILTIN(PromiseGetCapabilitiesExecutor, PromiseBuiltinsAssembler) {
1410 1411 1412
  Node* const resolve = Parameter(Descriptor::kResolve);
  Node* const reject = Parameter(Descriptor::kReject);
  Node* const context = Parameter(Descriptor::kContext);
1413

1414 1415
  Node* const capability =
      LoadContextElement(context, PromiseBuiltins::kCapabilitySlot);
1416 1417

  Label if_alreadyinvoked(this, Label::kDeferred);
1418 1419 1420 1421 1422 1423
  GotoIfNot(IsUndefined(
                LoadObjectField(capability, PromiseCapability::kResolveOffset)),
            &if_alreadyinvoked);
  GotoIfNot(IsUndefined(
                LoadObjectField(capability, PromiseCapability::kRejectOffset)),
            &if_alreadyinvoked);
1424

1425 1426
  StoreObjectField(capability, PromiseCapability::kResolveOffset, resolve);
  StoreObjectField(capability, PromiseCapability::kRejectOffset, reject);
1427 1428 1429

  Return(UndefinedConstant());

1430
  BIND(&if_alreadyinvoked);
1431
  ThrowTypeError(context, MessageTemplate::kPromiseExecutorAlreadyInvoked);
1432 1433
}

1434 1435
TF_BUILTIN(PromiseReject, PromiseBuiltinsAssembler) {
  // 1. Let C be the this value.
1436 1437 1438
  Node* const receiver = Parameter(Descriptor::kReceiver);
  Node* const reason = Parameter(Descriptor::kReason);
  Node* const context = Parameter(Descriptor::kContext);
1439 1440 1441 1442 1443 1444 1445

  // 2. If Type(C) is not Object, throw a TypeError exception.
  ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject,
                       "PromiseReject");

  Label if_nativepromise(this), if_custompromise(this, Label::kDeferred);
  Node* const native_context = LoadNativeContext(context);
1446

1447 1448 1449 1450 1451
  Node* const promise_fun =
      LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
  Branch(WordEqual(promise_fun, receiver), &if_nativepromise,
         &if_custompromise);

1452
  BIND(&if_nativepromise);
1453
  {
1454 1455
    Node* const promise =
        AllocateAndSetJSPromise(context, v8::Promise::kRejected, reason);
1456 1457 1458 1459 1460
    CallRuntime(Runtime::kPromiseRejectEventFromStack, context, promise,
                reason);
    Return(promise);
  }

1461
  BIND(&if_custompromise);
1462 1463
  {
    // 3. Let promiseCapability be ? NewPromiseCapability(C).
1464 1465 1466
    Node* const debug_event = TrueConstant();
    Node* const capability = CallBuiltin(Builtins::kNewPromiseCapability,
                                         context, receiver, debug_event);
1467 1468 1469

    // 4. Perform ? Call(promiseCapability.[[Reject]], undefined, « r »).
    Node* const reject =
1470
        LoadObjectField(capability, PromiseCapability::kRejectOffset);
1471 1472
    CallJS(CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
           context, reject, UndefinedConstant(), reason);
1473 1474 1475

    // 5. Return promiseCapability.[[Promise]].
    Node* const promise =
1476
        LoadObjectField(capability, PromiseCapability::kPromiseOffset);
1477 1478 1479 1480
    Return(promise);
  }
}

1481
std::pair<Node*, Node*> PromiseBuiltinsAssembler::CreatePromiseFinallyFunctions(
1482
    Node* on_finally, Node* constructor, Node* native_context) {
1483 1484 1485 1486 1487 1488
  Node* const promise_context = CreatePromiseContext(
      native_context, PromiseBuiltins::kPromiseFinallyContextLength);
  StoreContextElementNoWriteBarrier(
      promise_context, PromiseBuiltins::kOnFinallySlot, on_finally);
  StoreContextElementNoWriteBarrier(
      promise_context, PromiseBuiltins::kConstructorSlot, constructor);
1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502
  Node* const map = LoadContextElement(
      native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
  Node* const then_finally_info = LoadContextElement(
      native_context, Context::PROMISE_THEN_FINALLY_SHARED_FUN);
  Node* const then_finally = AllocateFunctionWithMapAndContext(
      map, then_finally_info, promise_context);
  Node* const catch_finally_info = LoadContextElement(
      native_context, Context::PROMISE_CATCH_FINALLY_SHARED_FUN);
  Node* const catch_finally = AllocateFunctionWithMapAndContext(
      map, catch_finally_info, promise_context);
  return std::make_pair(then_finally, catch_finally);
}

TF_BUILTIN(PromiseValueThunkFinally, PromiseBuiltinsAssembler) {
1503
  Node* const context = Parameter(Descriptor::kContext);
1504

1505
  Node* const value = LoadContextElement(context, PromiseBuiltins::kValueSlot);
1506 1507 1508 1509 1510
  Return(value);
}

Node* PromiseBuiltinsAssembler::CreateValueThunkFunction(Node* value,
                                                         Node* native_context) {
1511
  Node* const value_thunk_context = CreatePromiseContext(
1512 1513 1514
      native_context, PromiseBuiltins::kPromiseValueThunkOrReasonContextLength);
  StoreContextElementNoWriteBarrier(value_thunk_context,
                                    PromiseBuiltins::kValueSlot, value);
1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526
  Node* const map = LoadContextElement(
      native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
  Node* const value_thunk_info = LoadContextElement(
      native_context, Context::PROMISE_VALUE_THUNK_FINALLY_SHARED_FUN);
  Node* const value_thunk = AllocateFunctionWithMapAndContext(
      map, value_thunk_info, value_thunk_context);
  return value_thunk;
}

TF_BUILTIN(PromiseThenFinally, PromiseBuiltinsAssembler) {
  CSA_ASSERT_JS_ARGC_EQ(this, 1);

1527 1528
  Node* const value = Parameter(Descriptor::kValue);
  Node* const context = Parameter(Descriptor::kContext);
1529

1530
  // 1. Let onFinally be F.[[OnFinally]].
1531 1532
  Node* const on_finally =
      LoadContextElement(context, PromiseBuiltins::kOnFinallySlot);
1533

1534 1535 1536 1537
  // 2.  Assert: IsCallable(onFinally) is true.
  CSA_ASSERT(this, IsCallable(on_finally));

  // 3. Let result be ?  Call(onFinally).
1538 1539 1540
  Node* const result = CallJS(
      CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
      context, on_finally, UndefinedConstant());
1541

1542
  // 4. Let C be F.[[Constructor]].
1543 1544
  Node* const constructor =
      LoadContextElement(context, PromiseBuiltins::kConstructorSlot);
1545 1546 1547

  // 5. Assert: IsConstructor(C) is true.
  CSA_ASSERT(this, IsConstructor(constructor));
1548

1549 1550 1551 1552 1553
  // 6. Let promise be ? PromiseResolve(C, result).
  Node* const promise =
    CallBuiltin(Builtins::kPromiseResolve, context, constructor, result);

  // 7. Let valueThunk be equivalent to a function that returns value.
1554
  Node* const native_context = LoadNativeContext(context);
1555 1556
  Node* const value_thunk = CreateValueThunkFunction(value, native_context);

1557
  // 8. Return ? Invoke(promise, "then", « valueThunk »).
1558
  Return(InvokeThen(native_context, promise, value_thunk));
1559 1560 1561
}

TF_BUILTIN(PromiseThrowerFinally, PromiseBuiltinsAssembler) {
1562
  Node* const context = Parameter(Descriptor::kContext);
1563

1564
  Node* const reason = LoadContextElement(context, PromiseBuiltins::kValueSlot);
1565
  CallRuntime(Runtime::kThrow, context, reason);
1566
  Unreachable();
1567 1568 1569 1570
}

Node* PromiseBuiltinsAssembler::CreateThrowerFunction(Node* reason,
                                                      Node* native_context) {
1571
  Node* const thrower_context = CreatePromiseContext(
1572 1573 1574
      native_context, PromiseBuiltins::kPromiseValueThunkOrReasonContextLength);
  StoreContextElementNoWriteBarrier(thrower_context,
                                    PromiseBuiltins::kValueSlot, reason);
1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586
  Node* const map = LoadContextElement(
      native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
  Node* const thrower_info = LoadContextElement(
      native_context, Context::PROMISE_THROWER_FINALLY_SHARED_FUN);
  Node* const thrower =
      AllocateFunctionWithMapAndContext(map, thrower_info, thrower_context);
  return thrower;
}

TF_BUILTIN(PromiseCatchFinally, PromiseBuiltinsAssembler) {
  CSA_ASSERT_JS_ARGC_EQ(this, 1);

1587 1588
  Node* const reason = Parameter(Descriptor::kReason);
  Node* const context = Parameter(Descriptor::kContext);
1589

1590
  // 1. Let onFinally be F.[[OnFinally]].
1591 1592
  Node* const on_finally =
      LoadContextElement(context, PromiseBuiltins::kOnFinallySlot);
1593

1594 1595 1596 1597
  // 2. Assert: IsCallable(onFinally) is true.
  CSA_ASSERT(this, IsCallable(on_finally));

  // 3. Let result be ? Call(onFinally).
1598 1599 1600
  Node* result = CallJS(
      CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
      context, on_finally, UndefinedConstant());
1601

1602
  // 4. Let C be F.[[Constructor]].
1603 1604
  Node* const constructor =
      LoadContextElement(context, PromiseBuiltins::kConstructorSlot);
1605 1606 1607 1608 1609 1610 1611

  // 5. Assert: IsConstructor(C) is true.
  CSA_ASSERT(this, IsConstructor(constructor));

  // 6. Let promise be ? PromiseResolve(C, result).
  Node* const promise =
    CallBuiltin(Builtins::kPromiseResolve, context, constructor, result);
1612

1613
  // 7. Let thrower be equivalent to a function that throws reason.
1614
  Node* const native_context = LoadNativeContext(context);
1615 1616
  Node* const thrower = CreateThrowerFunction(reason, native_context);

1617
  // 8. Return ? Invoke(promise, "then", « thrower »).
1618
  Return(InvokeThen(native_context, promise, thrower));
1619 1620
}

1621
TF_BUILTIN(PromisePrototypeFinally, PromiseBuiltinsAssembler) {
1622 1623 1624
  CSA_ASSERT_JS_ARGC_EQ(this, 1);

  // 1.  Let promise be the this value.
1625
  Node* const receiver = Parameter(Descriptor::kReceiver);
1626 1627
  Node* const on_finally = Parameter(Descriptor::kOnFinally);
  Node* const context = Parameter(Descriptor::kContext);
1628

1629
  // 2. If Type(promise) is not Object, throw a TypeError exception.
1630
  ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject,
1631
                       "Promise.prototype.finally");
1632

1633 1634 1635 1636
  // 3. Let C be ? SpeciesConstructor(promise, %Promise%).
  Node* const native_context = LoadNativeContext(context);
  Node* const promise_fun =
      LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651
  VARIABLE(var_constructor, MachineRepresentation::kTagged, promise_fun);
  Label slow_constructor(this, Label::kDeferred), done_constructor(this);
  Node* const receiver_map = LoadMap(receiver);
  GotoIfNot(IsJSPromiseMap(receiver_map), &slow_constructor);
  BranchIfPromiseSpeciesLookupChainIntact(native_context, receiver_map,
                                          &done_constructor, &slow_constructor);
  BIND(&slow_constructor);
  {
    Node* const constructor =
        SpeciesConstructor(context, receiver, promise_fun);
    var_constructor.Bind(constructor);
    Goto(&done_constructor);
  }
  BIND(&done_constructor);
  Node* const constructor = var_constructor.value();
1652 1653 1654 1655

  // 4. Assert: IsConstructor(C) is true.
  CSA_ASSERT(this, IsConstructor(constructor));

1656 1657
  VARIABLE(var_then_finally, MachineRepresentation::kTagged);
  VARIABLE(var_catch_finally, MachineRepresentation::kTagged);
1658 1659 1660 1661

  Label if_notcallable(this, Label::kDeferred), perform_finally(this);

  GotoIf(TaggedIsSmi(on_finally), &if_notcallable);
1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672
  GotoIfNot(IsCallable(on_finally), &if_notcallable);

  // 6. Else,
  //   a. Let thenFinally be a new built-in function object as defined
  //   in ThenFinally Function.
  //   b. Let catchFinally be a new built-in function object as
  //   defined in CatchFinally Function.
  //   c. Set thenFinally and catchFinally's [[Constructor]] internal
  //   slots to C.
  //   d. Set thenFinally and catchFinally's [[OnFinally]] internal
  //   slots to onFinally.
1673 1674 1675
  Node* then_finally = nullptr;
  Node* catch_finally = nullptr;
  std::tie(then_finally, catch_finally) =
1676
    CreatePromiseFinallyFunctions(on_finally, constructor, native_context);
1677 1678 1679 1680
  var_then_finally.Bind(then_finally);
  var_catch_finally.Bind(catch_finally);
  Goto(&perform_finally);

1681 1682 1683
  // 5. If IsCallable(onFinally) is not true,
  //    a. Let thenFinally be onFinally.
  //    b. Let catchFinally be onFinally.
1684
  BIND(&if_notcallable);
1685 1686 1687 1688 1689 1690
  {
    var_then_finally.Bind(on_finally);
    var_catch_finally.Bind(on_finally);
    Goto(&perform_finally);
  }

1691
  // 7. Return ? Invoke(promise, "then", « thenFinally, catchFinally »).
1692
  BIND(&perform_finally);
1693 1694
  Return(InvokeThen(native_context, receiver, var_then_finally.value(),
                    var_catch_finally.value()));
1695 1696
}

1697 1698
// ES #sec-fulfillpromise
TF_BUILTIN(FulfillPromise, PromiseBuiltinsAssembler) {
1699 1700 1701 1702
  Node* const promise = Parameter(Descriptor::kPromise);
  Node* const value = Parameter(Descriptor::kValue);
  Node* const context = Parameter(Descriptor::kContext);

1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720
  CSA_ASSERT(this, TaggedIsNotSmi(promise));
  CSA_ASSERT(this, IsJSPromise(promise));

  // 2. Let reactions be promise.[[PromiseFulfillReactions]].
  Node* const reactions =
      LoadObjectField(promise, JSPromise::kReactionsOrResultOffset);

  // 3. Set promise.[[PromiseResult]] to value.
  // 4. Set promise.[[PromiseFulfillReactions]] to undefined.
  // 5. Set promise.[[PromiseRejectReactions]] to undefined.
  StoreObjectField(promise, JSPromise::kReactionsOrResultOffset, value);

  // 6. Set promise.[[PromiseState]] to "fulfilled".
  PromiseSetStatus(promise, Promise::kFulfilled);

  // 7. Return TriggerPromiseReactions(reactions, value).
  Return(TriggerPromiseReactions(context, reactions, value,
                                 PromiseReaction::kFulfill));
1721 1722
}

1723 1724
// ES #sec-rejectpromise
TF_BUILTIN(RejectPromise, PromiseBuiltinsAssembler) {
1725
  Node* const promise = Parameter(Descriptor::kPromise);
1726
  Node* const reason = Parameter(Descriptor::kReason);
1727 1728 1729
  Node* const debug_event = Parameter(Descriptor::kDebugEvent);
  Node* const context = Parameter(Descriptor::kContext);

1730 1731
  CSA_ASSERT(this, TaggedIsNotSmi(promise));
  CSA_ASSERT(this, IsJSPromise(promise));
1732
  CSA_ASSERT(this, IsBoolean(debug_event));
1733 1734 1735 1736 1737 1738
  Label if_runtime(this, Label::kDeferred);

  // If promise hook is enabled or the debugger is active, let
  // the runtime handle this operation, which greatly reduces
  // the complexity here and also avoids a couple of back and
  // forth between JavaScript and C++ land.
1739 1740
  GotoIf(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(),
         &if_runtime);
1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786

  // 7. If promise.[[PromiseIsHandled]] is false, perform
  //    HostPromiseRejectionTracker(promise, "reject").
  // We don't try to handle rejecting {promise} without handler
  // here, but we let the C++ code take care of this completely.
  GotoIfNot(PromiseHasHandler(promise), &if_runtime);

  // 2. Let reactions be promise.[[PromiseRejectReactions]].
  Node* reactions =
      LoadObjectField(promise, JSPromise::kReactionsOrResultOffset);

  // 3. Set promise.[[PromiseResult]] to reason.
  // 4. Set promise.[[PromiseFulfillReactions]] to undefined.
  // 5. Set promise.[[PromiseRejectReactions]] to undefined.
  StoreObjectField(promise, JSPromise::kReactionsOrResultOffset, reason);

  // 6. Set promise.[[PromiseState]] to "rejected".
  PromiseSetStatus(promise, Promise::kRejected);

  // 7. Return TriggerPromiseReactions(reactions, reason).
  Return(TriggerPromiseReactions(context, reactions, reason,
                                 PromiseReaction::kReject));

  BIND(&if_runtime);
  TailCallRuntime(Runtime::kRejectPromise, context, promise, reason,
                  debug_event);
}

// ES #sec-promise-resolve-functions
TF_BUILTIN(ResolvePromise, PromiseBuiltinsAssembler) {
  Node* const promise = Parameter(Descriptor::kPromise);
  Node* const resolution = Parameter(Descriptor::kResolution);
  Node* const context = Parameter(Descriptor::kContext);

  CSA_ASSERT(this, TaggedIsNotSmi(promise));
  CSA_ASSERT(this, IsJSPromise(promise));

  Label do_enqueue(this), if_fulfill(this), if_reject(this, Label::kDeferred),
      if_runtime(this, Label::kDeferred);
  VARIABLE(var_reason, MachineRepresentation::kTagged);
  VARIABLE(var_then, MachineRepresentation::kTagged);

  // If promise hook is enabled or the debugger is active, let
  // the runtime handle this operation, which greatly reduces
  // the complexity here and also avoids a couple of back and
  // forth between JavaScript and C++ land.
1787 1788
  GotoIf(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(),
         &if_runtime);
1789 1790 1791 1792 1793 1794 1795 1796

  // 6. If SameValue(resolution, promise) is true, then
  // We can use pointer comparison here, since the {promise} is guaranteed
  // to be a JSPromise inside this function and thus is reference comparable.
  GotoIf(WordEqual(promise, resolution), &if_runtime);

  // 7. If Type(resolution) is not Object, then
  GotoIf(TaggedIsSmi(resolution), &if_fulfill);
1797 1798
  Node* const resolution_map = LoadMap(resolution);
  GotoIfNot(IsJSReceiverMap(resolution_map), &if_fulfill);
1799 1800 1801 1802 1803

  // We can skip the "then" lookup on {resolution} if its [[Prototype]]
  // is the (initial) Promise.prototype and the Promise#then protector
  // is intact, as that guards the lookup path for the "then" property
  // on JSPromise instances which have the (initial) %PromisePrototype%.
1804
  Label if_fast(this), if_receiver(this), if_slow(this, Label::kDeferred);
1805
  Node* const native_context = LoadNativeContext(context);
1806 1807 1808 1809 1810 1811 1812
  GotoIfForceSlowPath(&if_slow);
  GotoIf(IsPromiseThenProtectorCellInvalid(), &if_slow);
  GotoIfNot(IsJSPromiseMap(resolution_map), &if_receiver);
  Node* const promise_prototype =
      LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
  Branch(WordEqual(LoadMapPrototype(resolution_map), promise_prototype),
         &if_fast, &if_slow);
1813 1814 1815

  BIND(&if_fast);
  {
1816
    // The {resolution} is a native Promise in this case.
1817 1818 1819 1820 1821 1822
    Node* const then =
        LoadContextElement(native_context, Context::PROMISE_THEN_INDEX);
    var_then.Bind(then);
    Goto(&do_enqueue);
  }

1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837
  BIND(&if_receiver);
  {
    // We can skip the lookup of "then" if the {resolution} is a (newly
    // created) IterResultObject, as the Promise#then() protector also
    // ensures that the intrinsic %ObjectPrototype% doesn't contain any
    // "then" property. This helps to avoid negative lookups on iterator
    // results from async generators.
    CSA_ASSERT(this, IsJSReceiverMap(resolution_map));
    CSA_ASSERT(this, Word32BinaryNot(IsPromiseThenProtectorCellInvalid()));
    Node* const iterator_result_map =
        LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
    Branch(WordEqual(resolution_map, iterator_result_map), &if_fulfill,
           &if_slow);
  }

1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878
  BIND(&if_slow);
  {
    // 8. Let then be Get(resolution, "then").
    Node* const then =
        GetProperty(context, resolution, isolate()->factory()->then_string());

    // 9. If then is an abrupt completion, then
    GotoIfException(then, &if_reject, &var_reason);

    // 11. If IsCallable(thenAction) is false, then
    GotoIf(TaggedIsSmi(then), &if_fulfill);
    Node* const then_map = LoadMap(then);
    GotoIfNot(IsCallableMap(then_map), &if_fulfill);
    var_then.Bind(then);
    Goto(&do_enqueue);
  }

  BIND(&do_enqueue);
  {
    // 12. Perform EnqueueJob("PromiseJobs", PromiseResolveThenableJob,
    //                        «promise, resolution, thenAction»).
    Node* const task = AllocatePromiseResolveThenableJobTask(
        promise, var_then.value(), resolution, native_context);
    TailCallBuiltin(Builtins::kEnqueueMicrotask, native_context, task);
  }

  BIND(&if_fulfill);
  {
    // 7.b Return FulfillPromise(promise, resolution).
    TailCallBuiltin(Builtins::kFulfillPromise, context, promise, resolution);
  }

  BIND(&if_runtime);
  Return(CallRuntime(Runtime::kResolvePromise, context, promise, resolution));

  BIND(&if_reject);
  {
    // 9.a Return RejectPromise(promise, then.[[Value]]).
    TailCallBuiltin(Builtins::kRejectPromise, context, promise,
                    var_reason.value(), FalseConstant());
  }
1879 1880
}

1881
Node* PromiseBuiltinsAssembler::PerformPromiseAll(
1882 1883 1884
    Node* context, Node* constructor, Node* capability,
    const IteratorRecord& iterator, Label* if_exception,
    Variable* var_exception) {
1885 1886
  IteratorBuiltinsAssembler iter_assembler(state());

1887
  Node* const native_context = LoadNativeContext(context);
1888 1889 1890

  // For catch prediction, don't treat the .then calls as handling it;
  // instead, recurse outwards.
1891
  SetForwardingHandlerIfTrue(
1892
      native_context, IsDebugActive(),
1893
      LoadObjectField(capability, PromiseCapability::kRejectOffset));
1894

1895 1896
  Node* const resolve_element_context =
      CreatePromiseAllResolveElementContext(capability, native_context);
1897

1898
  TVARIABLE(Smi, var_index, SmiConstant(1));
1899 1900 1901
  Label loop(this, &var_index), done_loop(this),
      too_many_elements(this, Label::kDeferred),
      close_iterator(this, Label::kDeferred);
1902 1903 1904 1905 1906 1907 1908 1909 1910
  Goto(&loop);
  BIND(&loop);
  {
    // Let next be IteratorStep(iteratorRecord.[[Iterator]]).
    // If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
    // ReturnIfAbrupt(next).
    Node* const fast_iterator_result_map =
        LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
    Node* const next = iter_assembler.IteratorStep(
1911 1912
        native_context, iterator, &done_loop, fast_iterator_result_map,
        if_exception, var_exception);
1913 1914 1915 1916 1917 1918

    // Let nextValue be IteratorValue(next).
    // If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to
    //     true.
    // ReturnIfAbrupt(nextValue).
    Node* const next_value = iter_assembler.IteratorValue(
1919 1920
        native_context, next, fast_iterator_result_map, if_exception,
        var_exception);
1921

1922
    // Check if we reached the limit.
1923
    TNode<Smi> const index = var_index.value();
1924 1925
    GotoIf(SmiEqual(index, SmiConstant(PropertyArray::HashField::kMax)),
           &too_many_elements);
1926

1927 1928 1929
    // Set index to index + 1.
    var_index = SmiAdd(index, SmiConstant(1));

1930 1931
    // Set remainingElementsCount.[[Value]] to
    //     remainingElementsCount.[[Value]] + 1.
1932
    TNode<Smi> const remaining_elements_count = CAST(LoadContextElement(
1933 1934
        resolve_element_context,
        PromiseBuiltins::kPromiseAllResolveElementRemainingSlot));
1935
    StoreContextElementNoWriteBarrier(
1936 1937
        resolve_element_context,
        PromiseBuiltins::kPromiseAllResolveElementRemainingSlot,
1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952
        SmiAdd(remaining_elements_count, SmiConstant(1)));

    // Let resolveElement be CreateBuiltinFunction(steps,
    //                                             « [[AlreadyCalled]],
    //                                               [[Index]],
    //                                               [[Values]],
    //                                               [[Capability]],
    //                                               [[RemainingElements]] »).
    // Set resolveElement.[[AlreadyCalled]] to a Record { [[Value]]: false }.
    // Set resolveElement.[[Index]] to index.
    // Set resolveElement.[[Values]] to values.
    // Set resolveElement.[[Capability]] to resultCapability.
    // Set resolveElement.[[RemainingElements]] to remainingElementsCount.
    Node* const resolve_element_fun = CreatePromiseAllResolveElementFunction(
        resolve_element_context, index, native_context);
1953

1954 1955 1956 1957 1958 1959 1960
    // We can skip the "resolve" lookup on the {constructor} as well as the
    // "then" lookup on the result of the "resolve" call, and immediately
    // chain continuation onto the {next_value} if:
    //
    //   (a) The {constructor} is the intrinsic %Promise% function, and
    //       looking up "resolve" on {constructor} yields the initial
    //       Promise.resolve() builtin, and
1961 1962 1963 1964
    //   (b) the promise @@species protector cell is valid, meaning that
    //       no one messed with the Symbol.species property on any
    //       intrinsic promise or on the Promise.prototype, and
    //   (c) the {next_value} is a JSPromise whose [[Prototype]] field
1965
    //       contains the intrinsic %PromisePrototype%, and
1966
    //   (d) we're not running with async_hooks or DevTools enabled.
1967 1968 1969 1970 1971 1972 1973
    //
    // In that case we also don't need to allocate a chained promise for
    // the PromiseReaction (aka we can pass undefined to PerformPromiseThen),
    // since this is only necessary for DevTools and PromiseHooks.
    Label if_fast(this), if_slow(this);
    GotoIfNotPromiseResolveLookupChainIntact(native_context, constructor,
                                             &if_slow);
1974 1975
    GotoIf(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(),
           &if_slow);
1976
    GotoIf(IsPromiseSpeciesProtectorCellInvalid(), &if_slow);
1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992
    GotoIf(TaggedIsSmi(next_value), &if_slow);
    Node* const next_value_map = LoadMap(next_value);
    BranchIfPromiseThenLookupChainIntact(native_context, next_value_map,
                                         &if_fast, &if_slow);

    BIND(&if_fast);
    {
      // Register the PromiseReaction immediately on the {next_value}, not
      // passing any chained promise since neither async_hooks nor DevTools
      // are enabled, so there's no use of the resulting promise.
      PerformPromiseThen(
          native_context, next_value, resolve_element_fun,
          LoadObjectField(capability, PromiseCapability::kRejectOffset),
          UndefinedConstant());
      Goto(&loop);
    }
1993

1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016
    BIND(&if_slow);
    {
      // Let nextPromise be ? Invoke(constructor, "resolve", « nextValue »).
      Node* const next_promise =
          InvokeResolve(native_context, constructor, next_value,
                        &close_iterator, var_exception);

      // Perform ? Invoke(nextPromise, "then", « resolveElement,
      //                  resultCapability.[[Reject]] »).
      Node* const then =
          GetProperty(native_context, next_promise, factory()->then_string());
      GotoIfException(then, &close_iterator, var_exception);

      Node* const then_call =
          CallJS(CodeFactory::Call(isolate(),
                                   ConvertReceiverMode::kNotNullOrUndefined),
                 native_context, then, next_promise, resolve_element_fun,
                 LoadObjectField(capability, PromiseCapability::kRejectOffset));
      GotoIfException(then_call, &close_iterator, var_exception);

      // For catch prediction, mark that rejections here are semantically
      // handled by the combined Promise.
      SetPromiseHandledByIfTrue(
2017
          native_context, IsDebugActive(), then_call, [=]() {
2018 2019 2020 2021
            // Load promiseCapability.[[Promise]]
            return LoadObjectField(capability,
                                   PromiseCapability::kPromiseOffset);
          });
2022

2023 2024
      Goto(&loop);
    }
2025 2026
  }

2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042
  BIND(&too_many_elements);
  {
    // If there are too many elements (currently more than 2**21-1), raise a
    // RangeError here (which is caught directly and turned into a rejection)
    // of the resulting promise. We could gracefully handle this case as well
    // and support more than this number of elements by going to a separate
    // function and pass the larger indices via a separate context, but it
    // doesn't seem likely that we need this, and it's unclear how the rest
    // of the system deals with 2**21 live Promises anyways.
    Node* const result =
        CallRuntime(Runtime::kThrowRangeError, native_context,
                    SmiConstant(MessageTemplate::kTooManyElementsInPromiseAll));
    GotoIfException(result, &close_iterator, var_exception);
    Unreachable();
  }

2043 2044 2045 2046
  BIND(&close_iterator);
  {
    // Exception must be bound to a JS value.
    CSA_ASSERT(this, IsNotTheHole(var_exception->value()));
2047 2048
    iter_assembler.IteratorCloseOnException(native_context, iterator,
                                            if_exception, var_exception);
2049 2050
  }

2051
  BIND(&done_loop);
2052
  {
2053
    Label resolve_promise(this, Label::kDeferred), return_promise(this);
2054 2055 2056
    // Set iteratorRecord.[[Done]] to true.
    // Set remainingElementsCount.[[Value]] to
    //    remainingElementsCount.[[Value]] - 1.
2057
    TNode<Smi> remaining_elements_count = CAST(LoadContextElement(
2058 2059
        resolve_element_context,
        PromiseBuiltins::kPromiseAllResolveElementRemainingSlot));
2060
    remaining_elements_count = SmiSub(remaining_elements_count, SmiConstant(1));
2061 2062 2063 2064
    StoreContextElementNoWriteBarrier(
        resolve_element_context,
        PromiseBuiltins::kPromiseAllResolveElementRemainingSlot,
        remaining_elements_count);
2065 2066 2067 2068 2069 2070 2071 2072
    GotoIf(SmiEqual(remaining_elements_count, SmiConstant(0)),
           &resolve_promise);

    // Pre-allocate the backing store for the {values_array} to the desired
    // capacity here. We may already have elements here in case of some
    // fancy Thenable that calls the resolve callback immediately, so we need
    // to handle that correctly here.
    Node* const values_array = LoadContextElement(
2073 2074
        resolve_element_context,
        PromiseBuiltins::kPromiseAllResolveElementValuesArraySlot);
2075
    Node* const old_elements = LoadElements(values_array);
2076 2077
    TNode<Smi> const old_capacity = LoadFixedArrayBaseLength(old_elements);
    TNode<Smi> const new_capacity = var_index.value();
2078 2079 2080 2081 2082 2083 2084 2085 2086
    GotoIf(SmiGreaterThanOrEqual(old_capacity, new_capacity), &return_promise);
    Node* const new_elements =
        AllocateFixedArray(PACKED_ELEMENTS, new_capacity, SMI_PARAMETERS,
                           AllocationFlag::kAllowLargeObjectAllocation);
    CopyFixedArrayElements(PACKED_ELEMENTS, old_elements, PACKED_ELEMENTS,
                           new_elements, SmiConstant(0), old_capacity,
                           new_capacity, UPDATE_WRITE_BARRIER, SMI_PARAMETERS);
    StoreObjectField(values_array, JSArray::kElementsOffset, new_elements);
    Goto(&return_promise);
2087 2088 2089 2090 2091 2092

    // If remainingElementsCount.[[Value]] is 0, then
    //     Let valuesArray be CreateArrayFromList(values).
    //     Perform ? Call(resultCapability.[[Resolve]], undefined,
    //                    « valuesArray »).
    BIND(&resolve_promise);
2093 2094 2095 2096
    {
      Node* const resolve =
          LoadObjectField(capability, PromiseCapability::kResolveOffset);
      Node* const values_array = LoadContextElement(
2097 2098
          resolve_element_context,
          PromiseBuiltins::kPromiseAllResolveElementValuesArraySlot);
2099 2100 2101 2102 2103 2104
      Node* const resolve_call = CallJS(
          CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
          native_context, resolve, UndefinedConstant(), values_array);
      GotoIfException(resolve_call, if_exception, var_exception);
      Goto(&return_promise);
    }
2105 2106 2107 2108 2109 2110

    // Return resultCapability.[[Promise]].
    BIND(&return_promise);
  }

  Node* const promise =
2111
      LoadObjectField(capability, PromiseCapability::kPromiseOffset);
2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130
  return promise;
}

// ES#sec-promise.all
// Promise.all ( iterable )
TF_BUILTIN(PromiseAll, PromiseBuiltinsAssembler) {
  IteratorBuiltinsAssembler iter_assembler(state());

  // Let C be the this value.
  // If Type(C) is not Object, throw a TypeError exception.
  Node* const receiver = Parameter(Descriptor::kReceiver);
  Node* const context = Parameter(Descriptor::kContext);
  ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject,
                       "Promise.all");

  // Let promiseCapability be ? NewPromiseCapability(C).
  // Don't fire debugEvent so that forwarding the rejection through all does not
  // trigger redundant ExceptionEvents
  Node* const debug_event = FalseConstant();
2131 2132
  Node* const capability = CallBuiltin(Builtins::kNewPromiseCapability, context,
                                       receiver, debug_event);
2133 2134 2135 2136 2137 2138 2139

  VARIABLE(var_exception, MachineRepresentation::kTagged, TheHoleConstant());
  Label reject_promise(this, &var_exception, Label::kDeferred);

  // Let iterator be GetIterator(iterable).
  // IfAbruptRejectPromise(iterator, promiseCapability).
  Node* const iterable = Parameter(Descriptor::kIterable);
2140
  IteratorRecord iterator = iter_assembler.GetIterator(
2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157
      context, iterable, &reject_promise, &var_exception);

  // Let result be PerformPromiseAll(iteratorRecord, C, promiseCapability).
  // If result is an abrupt completion, then
  //   If iteratorRecord.[[Done]] is false, let result be
  //       IteratorClose(iterator, result).
  //    IfAbruptRejectPromise(result, promiseCapability).
  Node* const result = PerformPromiseAll(
      context, receiver, capability, iterator, &reject_promise, &var_exception);

  Return(result);

  BIND(&reject_promise);
  {
    // Exception must be bound to a JS value.
    CSA_SLOW_ASSERT(this, IsNotTheHole(var_exception.value()));
    Node* const reject =
2158
        LoadObjectField(capability, PromiseCapability::kRejectOffset);
2159 2160
    CallJS(CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
           context, reject, UndefinedConstant(), var_exception.value());
2161 2162

    Node* const promise =
2163
        LoadObjectField(capability, PromiseCapability::kPromiseOffset);
2164 2165 2166 2167 2168
    Return(promise);
  }
}

TF_BUILTIN(PromiseAllResolveElementClosure, PromiseBuiltinsAssembler) {
2169 2170
  TNode<Object> value = CAST(Parameter(Descriptor::kValue));
  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2171
  TNode<JSFunction> function = CAST(Parameter(Descriptor::kJSTarget));
2172 2173

  Label already_called(this, Label::kDeferred), resolve_promise(this);
2174

2175 2176 2177 2178 2179 2180
  // We use the {function}s context as the marker to remember whether this
  // resolve element closure was already called. It points to the resolve
  // element context (which is a FunctionContext) until it was called the
  // first time, in which case we make it point to the native context here
  // to mark this resolve element closure as done.
  GotoIf(IsNativeContext(context), &already_called);
2181 2182 2183 2184
  CSA_ASSERT(
      this,
      SmiEqual(LoadObjectField<Smi>(context, Context::kLengthOffset),
               SmiConstant(PromiseBuiltins::kPromiseAllResolveElementLength)));
2185
  TNode<Context> native_context = LoadNativeContext(context);
2186 2187 2188 2189 2190
  StoreObjectField(function, JSFunction::kContextOffset, native_context);

  // Determine the index from the {function}.
  Label unreachable(this, Label::kDeferred);
  STATIC_ASSERT(PropertyArray::kNoHashSentinel == 0);
2191
  TNode<IntPtrT> identity_hash =
2192 2193
      LoadJSReceiverIdentityHash(function, &unreachable);
  CSA_ASSERT(this, IntPtrGreaterThan(identity_hash, IntPtrConstant(0)));
2194
  TNode<IntPtrT> index = IntPtrSub(identity_hash, IntPtrConstant(1));
2195

2196
  // Check if we need to grow the [[ValuesArray]] to store {value} at {index}.
2197 2198
  TNode<JSArray> values_array = CAST(LoadContextElement(
      context, PromiseBuiltins::kPromiseAllResolveElementValuesArraySlot));
2199 2200
  TNode<FixedArray> elements = CAST(LoadElements(values_array));
  TNode<IntPtrT> values_length =
2201 2202 2203
      LoadAndUntagObjectField(values_array, JSArray::kLengthOffset);
  Label if_inbounds(this), if_outofbounds(this), done(this);
  Branch(IntPtrLessThan(index, values_length), &if_inbounds, &if_outofbounds);
2204

2205 2206 2207
  BIND(&if_outofbounds);
  {
    // Check if we need to grow the backing store.
2208 2209
    TNode<IntPtrT> new_length = IntPtrAdd(index, IntPtrConstant(1));
    TNode<IntPtrT> elements_length =
2210 2211 2212 2213 2214 2215 2216
        LoadAndUntagObjectField(elements, FixedArray::kLengthOffset);
    Label if_grow(this, Label::kDeferred), if_nogrow(this);
    Branch(IntPtrLessThan(index, elements_length), &if_nogrow, &if_grow);

    BIND(&if_grow);
    {
      // We need to grow the backing store to fit the {index} as well.
2217
      TNode<IntPtrT> new_elements_length =
2218 2219 2220 2221
          IntPtrMin(CalculateNewElementsCapacity(new_length),
                    IntPtrConstant(PropertyArray::HashField::kMax + 1));
      CSA_ASSERT(this, IntPtrLessThan(index, new_elements_length));
      CSA_ASSERT(this, IntPtrLessThan(elements_length, new_elements_length));
2222
      TNode<FixedArray> new_elements =
2223 2224
          CAST(AllocateFixedArray(PACKED_ELEMENTS, new_elements_length,
                                  AllocationFlag::kAllowLargeObjectAllocation));
2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235
      CopyFixedArrayElements(PACKED_ELEMENTS, elements, PACKED_ELEMENTS,
                             new_elements, elements_length,
                             new_elements_length);
      StoreFixedArrayElement(new_elements, index, value);

      // Update backing store and "length" on {values_array}.
      StoreObjectField(values_array, JSArray::kElementsOffset, new_elements);
      StoreObjectFieldNoWriteBarrier(values_array, JSArray::kLengthOffset,
                                     SmiTag(new_length));
      Goto(&done);
    }
2236

2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248
    BIND(&if_nogrow);
    {
      // The {index} is within bounds of the {elements} backing store, so
      // just store the {value} and update the "length" of the {values_array}.
      StoreObjectFieldNoWriteBarrier(values_array, JSArray::kLengthOffset,
                                     SmiTag(new_length));
      StoreFixedArrayElement(elements, index, value);
      Goto(&done);
    }
  }

  BIND(&if_inbounds);
2249
  {
2250 2251 2252 2253
    // The {index} is in bounds of the {values_array},
    // just store the {value} and continue.
    StoreFixedArrayElement(elements, index, value);
    Goto(&done);
2254
  }
2255 2256

  BIND(&done);
2257 2258
  TNode<Smi> remaining_elements_count = CAST(LoadContextElement(
      context, PromiseBuiltins::kPromiseAllResolveElementRemainingSlot));
2259
  remaining_elements_count = SmiSub(remaining_elements_count, SmiConstant(1));
2260 2261
  StoreContextElement(context,
                      PromiseBuiltins::kPromiseAllResolveElementRemainingSlot,
2262 2263
                      remaining_elements_count);
  GotoIf(SmiEqual(remaining_elements_count, SmiConstant(0)), &resolve_promise);
2264 2265 2266
  Return(UndefinedConstant());

  BIND(&resolve_promise);
2267 2268
  TNode<PromiseCapability> capability = CAST(LoadContextElement(
      context, PromiseBuiltins::kPromiseAllResolveElementCapabilitySlot));
2269
  TNode<Object> resolve =
2270
      LoadObjectField(capability, PromiseCapability::kResolveOffset);
2271 2272
  CallJS(CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
         context, resolve, UndefinedConstant(), values_array);
2273 2274 2275 2276
  Return(UndefinedConstant());

  BIND(&already_called);
  Return(UndefinedConstant());
2277 2278 2279

  BIND(&unreachable);
  Unreachable();
2280 2281
}

2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296
// ES#sec-promise.race
// Promise.race ( iterable )
TF_BUILTIN(PromiseRace, PromiseBuiltinsAssembler) {
  IteratorBuiltinsAssembler iter_assembler(state());
  VARIABLE(var_exception, MachineRepresentation::kTagged, TheHoleConstant());

  Node* const receiver = Parameter(Descriptor::kReceiver);
  Node* const context = Parameter(Descriptor::kContext);
  ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject,
                       "Promise.race");

  // Let promiseCapability be ? NewPromiseCapability(C).
  // Don't fire debugEvent so that forwarding the rejection through all does not
  // trigger redundant ExceptionEvents
  Node* const debug_event = FalseConstant();
2297 2298
  Node* const capability = CallBuiltin(Builtins::kNewPromiseCapability, context,
                                       receiver, debug_event);
2299 2300

  Node* const resolve =
2301
      LoadObjectField(capability, PromiseCapability::kResolveOffset);
2302
  Node* const reject =
2303
      LoadObjectField(capability, PromiseCapability::kRejectOffset);
2304 2305 2306 2307 2308 2309

  Label close_iterator(this, Label::kDeferred);
  Label reject_promise(this, Label::kDeferred);

  // For catch prediction, don't treat the .then calls as handling it;
  // instead, recurse outwards.
2310
  SetForwardingHandlerIfTrue(context, IsDebugActive(), reject);
2311 2312 2313 2314

  // Let iterator be GetIterator(iterable).
  // IfAbruptRejectPromise(iterator, promiseCapability).
  Node* const iterable = Parameter(Descriptor::kIterable);
2315
  IteratorRecord iterator = iter_assembler.GetIterator(
2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343
      context, iterable, &reject_promise, &var_exception);

  // Let result be PerformPromiseRace(iteratorRecord, C, promiseCapability).
  {
    Label loop(this), break_loop(this);
    Goto(&loop);
    BIND(&loop);
    {
      Node* const native_context = LoadNativeContext(context);
      Node* const fast_iterator_result_map = LoadContextElement(
          native_context, Context::ITERATOR_RESULT_MAP_INDEX);

      // Let next be IteratorStep(iteratorRecord.[[Iterator]]).
      // If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
      // ReturnIfAbrupt(next).
      Node* const next = iter_assembler.IteratorStep(
          context, iterator, &break_loop, fast_iterator_result_map,
          &reject_promise, &var_exception);

      // Let nextValue be IteratorValue(next).
      // If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to
      //     true.
      // ReturnIfAbrupt(nextValue).
      Node* const next_value =
          iter_assembler.IteratorValue(context, next, fast_iterator_result_map,
                                       &reject_promise, &var_exception);

      // Let nextPromise be ? Invoke(constructor, "resolve", « nextValue »).
2344
      Node* const next_promise =
2345 2346
          InvokeResolve(native_context, receiver, next_value, &close_iterator,
                        &var_exception);
2347 2348 2349 2350 2351 2352 2353

      // Perform ? Invoke(nextPromise, "then", « resolveElement,
      //                  resultCapability.[[Reject]] »).
      Node* const then =
          GetProperty(context, next_promise, factory()->then_string());
      GotoIfException(then, &close_iterator, &var_exception);

2354 2355 2356 2357
      Node* const then_call =
          CallJS(CodeFactory::Call(isolate(),
                                   ConvertReceiverMode::kNotNullOrUndefined),
                 context, then, next_promise, resolve, reject);
2358 2359 2360 2361
      GotoIfException(then_call, &close_iterator, &var_exception);

      // For catch prediction, mark that rejections here are semantically
      // handled by the combined Promise.
2362
      SetPromiseHandledByIfTrue(context, IsDebugActive(), then_call, [=]() {
2363
        // Load promiseCapability.[[Promise]]
2364
        return LoadObjectField(capability, PromiseCapability::kPromiseOffset);
2365 2366 2367 2368 2369
      });
      Goto(&loop);
    }

    BIND(&break_loop);
2370
    Return(LoadObjectField(capability, PromiseCapability::kPromiseOffset));
2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382
  }

  BIND(&close_iterator);
  {
    CSA_ASSERT(this, IsNotTheHole(var_exception.value()));
    iter_assembler.IteratorCloseOnException(context, iterator, &reject_promise,
                                            &var_exception);
  }

  BIND(&reject_promise);
  {
    Node* const reject =
2383
        LoadObjectField(capability, PromiseCapability::kRejectOffset);
2384 2385
    CallJS(CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
           context, reject, UndefinedConstant(), var_exception.value());
2386 2387

    Node* const promise =
2388
        LoadObjectField(capability, PromiseCapability::kPromiseOffset);
2389 2390 2391 2392
    Return(promise);
  }
}

2393 2394
}  // namespace internal
}  // namespace v8