aboutsummaryrefslogtreecommitdiff
path: root/deps/v8/src/builtins/builtins-promise-gen.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/builtins/builtins-promise-gen.cc')
-rw-r--r--deps/v8/src/builtins/builtins-promise-gen.cc350
1 files changed, 225 insertions, 125 deletions
diff --git a/deps/v8/src/builtins/builtins-promise-gen.cc b/deps/v8/src/builtins/builtins-promise-gen.cc
index 1d43217999..39d81ce9dd 100644
--- a/deps/v8/src/builtins/builtins-promise-gen.cc
+++ b/deps/v8/src/builtins/builtins-promise-gen.cc
@@ -6,17 +6,20 @@
#include "src/builtins/builtins-constructor-gen.h"
#include "src/builtins/builtins-iterator-gen.h"
+#include "src/builtins/builtins-promise.h"
#include "src/builtins/builtins-utils-gen.h"
#include "src/builtins/builtins.h"
#include "src/code-factory.h"
#include "src/code-stub-assembler.h"
#include "src/objects-inl.h"
#include "src/objects/js-promise.h"
+#include "src/objects/smi.h"
namespace v8 {
namespace internal {
using compiler::Node;
+using IteratorRecord = IteratorBuiltinsAssembler::IteratorRecord;
Node* PromiseBuiltinsAssembler::AllocateJSPromise(Node* context) {
Node* const native_context = LoadNativeContext(context);
@@ -37,12 +40,12 @@ Node* PromiseBuiltinsAssembler::AllocateJSPromise(Node* context) {
void PromiseBuiltinsAssembler::PromiseInit(Node* promise) {
STATIC_ASSERT(v8::Promise::kPending == 0);
StoreObjectFieldNoWriteBarrier(promise, JSPromise::kReactionsOrResultOffset,
- SmiConstant(Smi::kZero));
+ SmiConstant(Smi::zero()));
StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset,
- SmiConstant(Smi::kZero));
- for (int i = 0; i < v8::Promise::kEmbedderFieldCount; i++) {
- int offset = JSPromise::kSize + i * kPointerSize;
- StoreObjectFieldNoWriteBarrier(promise, offset, SmiConstant(Smi::kZero));
+ SmiConstant(Smi::zero()));
+ for (int offset = JSPromise::kSize;
+ offset < JSPromise::kSizeWithEmbedderFields; offset += kTaggedSize) {
+ StoreObjectFieldNoWriteBarrier(promise, offset, SmiConstant(Smi::zero()));
}
}
@@ -74,8 +77,8 @@ Node* PromiseBuiltinsAssembler::AllocateAndSetJSPromise(
STATIC_ASSERT(JSPromise::kStatusShift == 0);
StoreObjectFieldNoWriteBarrier(instance, JSPromise::kFlagsOffset,
SmiConstant(status));
- for (int i = 0; i < v8::Promise::kEmbedderFieldCount; i++) {
- int offset = JSPromise::kSize + i * kPointerSize;
+ for (int offset = JSPromise::kSize;
+ offset < JSPromise::kSizeWithEmbedderFields; offset += kTaggedSize) {
StoreObjectFieldNoWriteBarrier(instance, offset, SmiConstant(0));
}
@@ -114,7 +117,7 @@ TF_BUILTIN(NewPromiseCapability, PromiseBuiltinsAssembler) {
Node* const context = Parameter(Descriptor::kContext);
Node* const constructor = Parameter(Descriptor::kConstructor);
Node* const debug_event = Parameter(Descriptor::kDebugEvent);
- Node* const native_context = LoadNativeContext(context);
+ TNode<Context> const native_context = LoadNativeContext(context);
Label if_not_constructor(this, Label::kDeferred),
if_notcallable(this, Label::kDeferred), if_fast_promise_capability(this),
@@ -164,11 +167,10 @@ TF_BUILTIN(NewPromiseCapability, PromiseBuiltinsAssembler) {
native_context, Context::PROMISE_GET_CAPABILITIES_EXECUTOR_SHARED_FUN);
Node* function_map = LoadContextElement(
native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
- Node* executor = AllocateFunctionWithMapAndContext(
- function_map, executor_info, executor_context);
+ TNode<JSFunction> executor = CAST(AllocateFunctionWithMapAndContext(
+ function_map, executor_info, executor_context));
- Node* promise = ConstructJS(CodeFactory::Construct(isolate()),
- native_context, constructor, executor);
+ Node* promise = Construct(native_context, CAST(constructor), executor);
StoreObjectField(capability, PromiseCapability::kPromiseOffset, promise);
Node* resolve =
@@ -204,19 +206,22 @@ Node* PromiseBuiltinsAssembler::CreatePromiseAllResolveElementContext(
CSA_ASSERT(this, IsNativeContext(native_context));
// TODO(bmeurer): Manually fold this into a single allocation.
- Node* const array_map = LoadContextElement(
- native_context, Context::JS_ARRAY_PACKED_ELEMENTS_MAP_INDEX);
- Node* const values_array = AllocateJSArray(PACKED_ELEMENTS, array_map,
- IntPtrConstant(0), SmiConstant(0));
+ 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));
- Node* const context =
- CreatePromiseContext(native_context, kPromiseAllResolveElementLength);
+ Node* const context = CreatePromiseContext(
+ native_context, PromiseBuiltins::kPromiseAllResolveElementLength);
StoreContextElementNoWriteBarrier(
- context, kPromiseAllResolveElementRemainingSlot, SmiConstant(1));
+ context, PromiseBuiltins::kPromiseAllResolveElementRemainingSlot,
+ SmiConstant(1));
StoreContextElementNoWriteBarrier(
- context, kPromiseAllResolveElementCapabilitySlot, promise_capability);
+ context, PromiseBuiltins::kPromiseAllResolveElementCapabilitySlot,
+ promise_capability);
StoreContextElementNoWriteBarrier(
- context, kPromiseAllResolveElementValuesArraySlot, values_array);
+ context, PromiseBuiltins::kPromiseAllResolveElementValuesArraySlot,
+ values_array);
return context;
}
@@ -244,20 +249,22 @@ Node* PromiseBuiltinsAssembler::CreatePromiseAllResolveElementFunction(
Node* PromiseBuiltinsAssembler::CreatePromiseResolvingFunctionsContext(
Node* promise, Node* debug_event, Node* native_context) {
- Node* const context =
- CreatePromiseContext(native_context, kPromiseContextLength);
- StoreContextElementNoWriteBarrier(context, kPromiseSlot, promise);
- StoreContextElementNoWriteBarrier(context, kAlreadyResolvedSlot,
- FalseConstant());
- StoreContextElementNoWriteBarrier(context, kDebugEventSlot, debug_event);
+ Node* const context = CreatePromiseContext(
+ native_context, PromiseBuiltins::kPromiseContextLength);
+ StoreContextElementNoWriteBarrier(context, PromiseBuiltins::kPromiseSlot,
+ promise);
+ StoreContextElementNoWriteBarrier(
+ context, PromiseBuiltins::kAlreadyResolvedSlot, FalseConstant());
+ StoreContextElementNoWriteBarrier(context, PromiseBuiltins::kDebugEventSlot,
+ debug_event);
return context;
}
Node* PromiseBuiltinsAssembler::CreatePromiseGetCapabilitiesExecutorContext(
Node* promise_capability, Node* native_context) {
- int kContextLength = kCapabilitiesContextLength;
+ int kContextLength = PromiseBuiltins::kCapabilitiesContextLength;
Node* context = CreatePromiseContext(native_context, kContextLength);
- StoreContextElementNoWriteBarrier(context, kCapabilitySlot,
+ StoreContextElementNoWriteBarrier(context, PromiseBuiltins::kCapabilitySlot,
promise_capability);
return context;
}
@@ -318,8 +325,11 @@ void PromiseBuiltinsAssembler::PerformPromiseThen(
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));
- CSA_ASSERT(this, Word32Or(IsJSPromise(result_promise_or_capability),
- IsPromiseCapability(result_promise_or_capability)));
+ CSA_ASSERT(
+ this,
+ Word32Or(Word32Or(IsJSPromise(result_promise_or_capability),
+ IsPromiseCapability(result_promise_or_capability)),
+ IsUndefined(result_promise_or_capability)));
Label if_pending(this), if_notpending(this), done(this);
Node* const status = PromiseStatus(promise);
@@ -373,7 +383,7 @@ void PromiseBuiltinsAssembler::PerformPromiseThen(
Node* microtask = AllocatePromiseReactionJobTask(
var_map.value(), context, argument, var_handler.value(),
result_promise_or_capability);
- CallBuiltin(Builtins::kEnqueueMicrotask, NoContextConstant(), microtask);
+ CallBuiltin(Builtins::kEnqueueMicrotask, context, microtask);
Goto(&done);
}
@@ -390,7 +400,8 @@ TF_BUILTIN(PerformPromiseThen, PromiseBuiltinsAssembler) {
Node* const result_promise = Parameter(Descriptor::kResultPromise);
CSA_ASSERT(this, TaggedIsNotSmi(result_promise));
- CSA_ASSERT(this, IsJSPromise(result_promise));
+ CSA_ASSERT(
+ this, Word32Or(IsJSPromise(result_promise), IsUndefined(result_promise)));
PerformPromiseThen(context, promise, on_fulfilled, on_rejected,
result_promise);
@@ -466,7 +477,7 @@ Node* PromiseBuiltinsAssembler::TriggerPromiseReactions(
{
VARIABLE(var_current, MachineRepresentation::kTagged, reactions);
VARIABLE(var_reversed, MachineRepresentation::kTagged,
- SmiConstant(Smi::kZero));
+ SmiConstant(Smi::zero()));
Label loop(this, {&var_current, &var_reversed}), done_loop(this);
Goto(&loop);
@@ -500,7 +511,8 @@ Node* PromiseBuiltinsAssembler::TriggerPromiseReactions(
// 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.
- STATIC_ASSERT(PromiseReaction::kSize == PromiseReactionJobTask::kSize);
+ STATIC_ASSERT(static_cast<int>(PromiseReaction::kSize) ==
+ static_cast<int>(PromiseReactionJobTask::kSize));
if (type == PromiseReaction::kFulfill) {
StoreMapNoWriteBarrier(current,
RootIndex::kPromiseFulfillReactionJobTaskMap);
@@ -508,10 +520,13 @@ Node* PromiseBuiltinsAssembler::TriggerPromiseReactions(
argument);
StoreObjectField(current, PromiseReactionJobTask::kContextOffset,
context);
- STATIC_ASSERT(PromiseReaction::kFulfillHandlerOffset ==
- PromiseReactionJobTask::kHandlerOffset);
- STATIC_ASSERT(PromiseReaction::kPromiseOrCapabilityOffset ==
- PromiseReactionJobTask::kPromiseOrCapabilityOffset);
+ STATIC_ASSERT(
+ static_cast<int>(PromiseReaction::kFulfillHandlerOffset) ==
+ static_cast<int>(PromiseReactionJobTask::kHandlerOffset));
+ STATIC_ASSERT(
+ static_cast<int>(PromiseReaction::kPromiseOrCapabilityOffset) ==
+ static_cast<int>(
+ PromiseReactionJobTask::kPromiseOrCapabilityOffset));
} else {
Node* handler =
LoadObjectField(current, PromiseReaction::kRejectHandlerOffset);
@@ -523,10 +538,12 @@ Node* PromiseBuiltinsAssembler::TriggerPromiseReactions(
context);
StoreObjectField(current, PromiseReactionJobTask::kHandlerOffset,
handler);
- STATIC_ASSERT(PromiseReaction::kPromiseOrCapabilityOffset ==
- PromiseReactionJobTask::kPromiseOrCapabilityOffset);
+ STATIC_ASSERT(
+ static_cast<int>(PromiseReaction::kPromiseOrCapabilityOffset) ==
+ static_cast<int>(
+ PromiseReactionJobTask::kPromiseOrCapabilityOffset));
}
- CallBuiltin(Builtins::kEnqueueMicrotask, NoContextConstant(), current);
+ CallBuiltin(Builtins::kEnqueueMicrotask, context, current);
Goto(&loop);
}
BIND(&done_loop);
@@ -633,6 +650,14 @@ void PromiseBuiltinsAssembler::BranchIfPromiseResolveLookupChainIntact(
Branch(IsPromiseResolveProtectorCellInvalid(), if_slow, if_fast);
}
+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);
+}
+
void PromiseBuiltinsAssembler::BranchIfPromiseSpeciesLookupChainIntact(
Node* native_context, Node* promise_map, Label* if_fast, Label* if_slow) {
CSA_ASSERT(this, IsNativeContext(native_context));
@@ -737,22 +762,24 @@ TF_BUILTIN(PromiseCapabilityDefaultReject, PromiseBuiltinsAssembler) {
Node* const context = Parameter(Descriptor::kContext);
// 2. Let promise be F.[[Promise]].
- Node* const promise = LoadContextElement(context, kPromiseSlot);
+ Node* const promise =
+ LoadContextElement(context, PromiseBuiltins::kPromiseSlot);
// 3. Let alreadyResolved be F.[[AlreadyResolved]].
Label if_already_resolved(this, Label::kDeferred);
Node* const already_resolved =
- LoadContextElement(context, kAlreadyResolvedSlot);
+ LoadContextElement(context, PromiseBuiltins::kAlreadyResolvedSlot);
// 4. If alreadyResolved.[[Value]] is true, return undefined.
GotoIf(IsTrue(already_resolved), &if_already_resolved);
// 5. Set alreadyResolved.[[Value]] to true.
- StoreContextElementNoWriteBarrier(context, kAlreadyResolvedSlot,
- TrueConstant());
+ StoreContextElementNoWriteBarrier(
+ context, PromiseBuiltins::kAlreadyResolvedSlot, TrueConstant());
// 6. Return RejectPromise(promise, reason).
- Node* const debug_event = LoadContextElement(context, kDebugEventSlot);
+ Node* const debug_event =
+ LoadContextElement(context, PromiseBuiltins::kDebugEventSlot);
Return(CallBuiltin(Builtins::kRejectPromise, context, promise, reason,
debug_event));
@@ -769,19 +796,20 @@ TF_BUILTIN(PromiseCapabilityDefaultResolve, PromiseBuiltinsAssembler) {
Node* const context = Parameter(Descriptor::kContext);
// 2. Let promise be F.[[Promise]].
- Node* const promise = LoadContextElement(context, kPromiseSlot);
+ Node* const promise =
+ LoadContextElement(context, PromiseBuiltins::kPromiseSlot);
// 3. Let alreadyResolved be F.[[AlreadyResolved]].
Label if_already_resolved(this, Label::kDeferred);
Node* const already_resolved =
- LoadContextElement(context, kAlreadyResolvedSlot);
+ LoadContextElement(context, PromiseBuiltins::kAlreadyResolvedSlot);
// 4. If alreadyResolved.[[Value]] is true, return undefined.
GotoIf(IsTrue(already_resolved), &if_already_resolved);
// 5. Set alreadyResolved.[[Value]] to true.
- StoreContextElementNoWriteBarrier(context, kAlreadyResolvedSlot,
- TrueConstant());
+ StoreContextElementNoWriteBarrier(
+ context, PromiseBuiltins::kAlreadyResolvedSlot, TrueConstant());
// The rest of the logic (and the catch prediction) is
// encapsulated in the dedicated ResolvePromise builtin.
@@ -1082,8 +1110,8 @@ TF_BUILTIN(PromiseResolveThenableJob, PromiseBuiltinsAssembler) {
GotoIfNot(WordEqual(then, promise_then), &if_slow);
Node* const thenable_map = LoadMap(thenable);
GotoIfNot(IsJSPromiseMap(thenable_map), &if_slow);
- GotoIf(IsPromiseHookEnabled(), &if_slow);
- GotoIf(IsDebugActive(), &if_slow);
+ GotoIf(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(),
+ &if_slow);
BranchIfPromiseSpeciesLookupChainIntact(native_context, thenable_map,
&if_fast, &if_slow);
@@ -1154,11 +1182,14 @@ void PromiseBuiltinsAssembler::PromiseReactionJob(Node* context, Node* argument,
CSA_ASSERT(this, TaggedIsNotSmi(handler));
CSA_ASSERT(this, Word32Or(IsUndefined(handler), IsCallable(handler)));
CSA_ASSERT(this, TaggedIsNotSmi(promise_or_capability));
- CSA_ASSERT(this, Word32Or(IsJSPromise(promise_or_capability),
- IsPromiseCapability(promise_or_capability)));
+ CSA_ASSERT(this,
+ Word32Or(Word32Or(IsJSPromise(promise_or_capability),
+ IsPromiseCapability(promise_or_capability)),
+ IsUndefined(promise_or_capability)));
VARIABLE(var_handler_result, MachineRepresentation::kTagged, argument);
- Label if_handler_callable(this), if_fulfill(this), if_reject(this);
+ Label if_handler_callable(this), if_fulfill(this), if_reject(this),
+ if_internal(this);
Branch(IsUndefined(handler),
type == PromiseReaction::kFulfill ? &if_fulfill : &if_reject,
&if_handler_callable);
@@ -1170,7 +1201,16 @@ void PromiseBuiltinsAssembler::PromiseReactionJob(Node* context, Node* argument,
context, handler, UndefinedConstant(), argument);
GotoIfException(result, &if_reject, &var_handler_result);
var_handler_result.Bind(result);
- Goto(&if_fulfill);
+ 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());
}
BIND(&if_fulfill);
@@ -1371,7 +1411,8 @@ TF_BUILTIN(PromiseGetCapabilitiesExecutor, PromiseBuiltinsAssembler) {
Node* const reject = Parameter(Descriptor::kReject);
Node* const context = Parameter(Descriptor::kContext);
- Node* const capability = LoadContextElement(context, kCapabilitySlot);
+ Node* const capability =
+ LoadContextElement(context, PromiseBuiltins::kCapabilitySlot);
Label if_alreadyinvoked(this, Label::kDeferred);
GotoIfNot(IsUndefined(
@@ -1439,12 +1480,12 @@ TF_BUILTIN(PromiseReject, PromiseBuiltinsAssembler) {
std::pair<Node*, Node*> PromiseBuiltinsAssembler::CreatePromiseFinallyFunctions(
Node* on_finally, Node* constructor, Node* native_context) {
- Node* const promise_context =
- CreatePromiseContext(native_context, kPromiseFinallyContextLength);
- StoreContextElementNoWriteBarrier(promise_context, kOnFinallySlot,
- on_finally);
- StoreContextElementNoWriteBarrier(promise_context, kConstructorSlot,
- constructor);
+ Node* const promise_context = CreatePromiseContext(
+ native_context, PromiseBuiltins::kPromiseFinallyContextLength);
+ StoreContextElementNoWriteBarrier(
+ promise_context, PromiseBuiltins::kOnFinallySlot, on_finally);
+ StoreContextElementNoWriteBarrier(
+ promise_context, PromiseBuiltins::kConstructorSlot, constructor);
Node* const map = LoadContextElement(
native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
Node* const then_finally_info = LoadContextElement(
@@ -1461,15 +1502,16 @@ std::pair<Node*, Node*> PromiseBuiltinsAssembler::CreatePromiseFinallyFunctions(
TF_BUILTIN(PromiseValueThunkFinally, PromiseBuiltinsAssembler) {
Node* const context = Parameter(Descriptor::kContext);
- Node* const value = LoadContextElement(context, kValueSlot);
+ Node* const value = LoadContextElement(context, PromiseBuiltins::kValueSlot);
Return(value);
}
Node* PromiseBuiltinsAssembler::CreateValueThunkFunction(Node* value,
Node* native_context) {
Node* const value_thunk_context = CreatePromiseContext(
- native_context, kPromiseValueThunkOrReasonContextLength);
- StoreContextElementNoWriteBarrier(value_thunk_context, kValueSlot, value);
+ native_context, PromiseBuiltins::kPromiseValueThunkOrReasonContextLength);
+ StoreContextElementNoWriteBarrier(value_thunk_context,
+ PromiseBuiltins::kValueSlot, value);
Node* const map = LoadContextElement(
native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
Node* const value_thunk_info = LoadContextElement(
@@ -1486,7 +1528,8 @@ TF_BUILTIN(PromiseThenFinally, PromiseBuiltinsAssembler) {
Node* const context = Parameter(Descriptor::kContext);
// 1. Let onFinally be F.[[OnFinally]].
- Node* const on_finally = LoadContextElement(context, kOnFinallySlot);
+ Node* const on_finally =
+ LoadContextElement(context, PromiseBuiltins::kOnFinallySlot);
// 2. Assert: IsCallable(onFinally) is true.
CSA_ASSERT(this, IsCallable(on_finally));
@@ -1497,7 +1540,8 @@ TF_BUILTIN(PromiseThenFinally, PromiseBuiltinsAssembler) {
context, on_finally, UndefinedConstant());
// 4. Let C be F.[[Constructor]].
- Node* const constructor = LoadContextElement(context, kConstructorSlot);
+ Node* const constructor =
+ LoadContextElement(context, PromiseBuiltins::kConstructorSlot);
// 5. Assert: IsConstructor(C) is true.
CSA_ASSERT(this, IsConstructor(constructor));
@@ -1517,7 +1561,7 @@ TF_BUILTIN(PromiseThenFinally, PromiseBuiltinsAssembler) {
TF_BUILTIN(PromiseThrowerFinally, PromiseBuiltinsAssembler) {
Node* const context = Parameter(Descriptor::kContext);
- Node* const reason = LoadContextElement(context, kValueSlot);
+ Node* const reason = LoadContextElement(context, PromiseBuiltins::kValueSlot);
CallRuntime(Runtime::kThrow, context, reason);
Unreachable();
}
@@ -1525,8 +1569,9 @@ TF_BUILTIN(PromiseThrowerFinally, PromiseBuiltinsAssembler) {
Node* PromiseBuiltinsAssembler::CreateThrowerFunction(Node* reason,
Node* native_context) {
Node* const thrower_context = CreatePromiseContext(
- native_context, kPromiseValueThunkOrReasonContextLength);
- StoreContextElementNoWriteBarrier(thrower_context, kValueSlot, reason);
+ native_context, PromiseBuiltins::kPromiseValueThunkOrReasonContextLength);
+ StoreContextElementNoWriteBarrier(thrower_context,
+ PromiseBuiltins::kValueSlot, reason);
Node* const map = LoadContextElement(
native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
Node* const thrower_info = LoadContextElement(
@@ -1543,7 +1588,8 @@ TF_BUILTIN(PromiseCatchFinally, PromiseBuiltinsAssembler) {
Node* const context = Parameter(Descriptor::kContext);
// 1. Let onFinally be F.[[OnFinally]].
- Node* const on_finally = LoadContextElement(context, kOnFinallySlot);
+ Node* const on_finally =
+ LoadContextElement(context, PromiseBuiltins::kOnFinallySlot);
// 2. Assert: IsCallable(onFinally) is true.
CSA_ASSERT(this, IsCallable(on_finally));
@@ -1554,7 +1600,8 @@ TF_BUILTIN(PromiseCatchFinally, PromiseBuiltinsAssembler) {
context, on_finally, UndefinedConstant());
// 4. Let C be F.[[Constructor]].
- Node* const constructor = LoadContextElement(context, kConstructorSlot);
+ Node* const constructor =
+ LoadContextElement(context, PromiseBuiltins::kConstructorSlot);
// 5. Assert: IsConstructor(C) is true.
CSA_ASSERT(this, IsConstructor(constructor));
@@ -1689,8 +1736,8 @@ TF_BUILTIN(RejectPromise, PromiseBuiltinsAssembler) {
// 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.
- GotoIf(IsPromiseHookEnabled(), &if_runtime);
- GotoIf(IsDebugActive(), &if_runtime);
+ GotoIf(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(),
+ &if_runtime);
// 7. If promise.[[PromiseIsHandled]] is false, perform
// HostPromiseRejectionTracker(promise, "reject").
@@ -1737,8 +1784,8 @@ TF_BUILTIN(ResolvePromise, PromiseBuiltinsAssembler) {
// 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.
- GotoIf(IsPromiseHookEnabled(), &if_runtime);
- GotoIf(IsDebugActive(), &if_runtime);
+ GotoIf(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(),
+ &if_runtime);
// 6. If SameValue(resolution, promise) is true, then
// We can use pointer comparison here, since the {promise} is guaranteed
@@ -1837,13 +1884,12 @@ Node* PromiseBuiltinsAssembler::PerformPromiseAll(
Variable* var_exception) {
IteratorBuiltinsAssembler iter_assembler(state());
- Node* const instrumenting = IsDebugActive();
Node* const native_context = LoadNativeContext(context);
// For catch prediction, don't treat the .then calls as handling it;
// instead, recurse outwards.
SetForwardingHandlerIfTrue(
- native_context, instrumenting,
+ native_context, IsDebugActive(),
LoadObjectField(capability, PromiseCapability::kRejectOffset));
Node* const resolve_element_context =
@@ -1873,22 +1919,22 @@ Node* PromiseBuiltinsAssembler::PerformPromiseAll(
native_context, next, fast_iterator_result_map, if_exception,
var_exception);
- // Let nextPromise be ? Invoke(constructor, "resolve", « nextValue »).
- Node* const next_promise =
- InvokeResolve(native_context, constructor, next_value, &close_iterator,
- var_exception);
-
// Check if we reached the limit.
TNode<Smi> const index = var_index.value();
GotoIf(SmiEqual(index, SmiConstant(PropertyArray::HashField::kMax)),
&too_many_elements);
+ // Set index to index + 1.
+ var_index = SmiAdd(index, SmiConstant(1));
+
// Set remainingElementsCount.[[Value]] to
// remainingElementsCount.[[Value]] + 1.
TNode<Smi> const remaining_elements_count = CAST(LoadContextElement(
- resolve_element_context, kPromiseAllResolveElementRemainingSlot));
+ resolve_element_context,
+ PromiseBuiltins::kPromiseAllResolveElementRemainingSlot));
StoreContextElementNoWriteBarrier(
- resolve_element_context, kPromiseAllResolveElementRemainingSlot,
+ resolve_element_context,
+ PromiseBuiltins::kPromiseAllResolveElementRemainingSlot,
SmiAdd(remaining_elements_count, SmiConstant(1)));
// Let resolveElement be CreateBuiltinFunction(steps,
@@ -1905,28 +1951,77 @@ Node* PromiseBuiltinsAssembler::PerformPromiseAll(
Node* const resolve_element_fun = CreatePromiseAllResolveElementFunction(
resolve_element_context, index, native_context);
- // Perform ? Invoke(nextPromise, "then", « resolveElement,
- // resultCapability.[[Reject]] »).
- Node* const then =
- GetProperty(native_context, next_promise, factory()->then_string());
- GotoIfException(then, &close_iterator, var_exception);
+ // 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
+ // (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
+ // contains the intrinsic %PromisePrototype%, and
+ // (d) we're not running with async_hooks or DevTools enabled.
+ //
+ // 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);
+ GotoIf(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(),
+ &if_slow);
+ GotoIf(IsPromiseSpeciesProtectorCellInvalid(), &if_slow);
+ GotoIf(TaggedIsSmi(next_value), &if_slow);
+ Node* const next_value_map = LoadMap(next_value);
+ BranchIfPromiseThenLookupChainIntact(native_context, next_value_map,
+ &if_fast, &if_slow);
- 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);
+ 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);
+ }
- // For catch prediction, mark that rejections here are semantically
- // handled by the combined Promise.
- SetPromiseHandledByIfTrue(native_context, instrumenting, then_call, [=]() {
- // Load promiseCapability.[[Promise]]
- return LoadObjectField(capability, PromiseCapability::kPromiseOffset);
- });
+ BIND(&if_slow);
+ {
+ // Let nextPromise be ? Invoke(constructor, "resolve", « nextValue »).
+ Node* const next_promise =
+ InvokeResolve(native_context, constructor, next_value,
+ &close_iterator, var_exception);
- // Set index to index + 1.
- var_index = SmiAdd(index, SmiConstant(1));
- Goto(&loop);
+ // 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(
+ native_context, IsDebugActive(), then_call, [=]() {
+ // Load promiseCapability.[[Promise]]
+ return LoadObjectField(capability,
+ PromiseCapability::kPromiseOffset);
+ });
+
+ Goto(&loop);
+ }
}
BIND(&too_many_elements);
@@ -1960,11 +2055,13 @@ Node* PromiseBuiltinsAssembler::PerformPromiseAll(
// Set remainingElementsCount.[[Value]] to
// remainingElementsCount.[[Value]] - 1.
TNode<Smi> remaining_elements_count = CAST(LoadContextElement(
- resolve_element_context, kPromiseAllResolveElementRemainingSlot));
+ resolve_element_context,
+ PromiseBuiltins::kPromiseAllResolveElementRemainingSlot));
remaining_elements_count = SmiSub(remaining_elements_count, SmiConstant(1));
- StoreContextElementNoWriteBarrier(resolve_element_context,
- kPromiseAllResolveElementRemainingSlot,
- remaining_elements_count);
+ StoreContextElementNoWriteBarrier(
+ resolve_element_context,
+ PromiseBuiltins::kPromiseAllResolveElementRemainingSlot,
+ remaining_elements_count);
GotoIf(SmiEqual(remaining_elements_count, SmiConstant(0)),
&resolve_promise);
@@ -1973,7 +2070,8 @@ Node* PromiseBuiltinsAssembler::PerformPromiseAll(
// fancy Thenable that calls the resolve callback immediately, so we need
// to handle that correctly here.
Node* const values_array = LoadContextElement(
- resolve_element_context, kPromiseAllResolveElementValuesArraySlot);
+ resolve_element_context,
+ PromiseBuiltins::kPromiseAllResolveElementValuesArraySlot);
Node* const old_elements = LoadElements(values_array);
TNode<Smi> const old_capacity = LoadFixedArrayBaseLength(old_elements);
TNode<Smi> const new_capacity = var_index.value();
@@ -1996,7 +2094,8 @@ Node* PromiseBuiltinsAssembler::PerformPromiseAll(
Node* const resolve =
LoadObjectField(capability, PromiseCapability::kResolveOffset);
Node* const values_array = LoadContextElement(
- resolve_element_context, kPromiseAllResolveElementValuesArraySlot);
+ resolve_element_context,
+ PromiseBuiltins::kPromiseAllResolveElementValuesArraySlot);
Node* const resolve_call = CallJS(
CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
native_context, resolve, UndefinedConstant(), values_array);
@@ -2079,8 +2178,10 @@ TF_BUILTIN(PromiseAllResolveElementClosure, PromiseBuiltinsAssembler) {
// 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);
- CSA_ASSERT(this, SmiEqual(LoadFixedArrayBaseLength(context),
- SmiConstant(kPromiseAllResolveElementLength)));
+ CSA_ASSERT(
+ this,
+ SmiEqual(LoadObjectField<Smi>(context, Context::kLengthOffset),
+ SmiConstant(PromiseBuiltins::kPromiseAllResolveElementLength)));
TNode<Context> native_context = LoadNativeContext(context);
StoreObjectField(function, JSFunction::kContextOffset, native_context);
@@ -2093,8 +2194,8 @@ TF_BUILTIN(PromiseAllResolveElementClosure, PromiseBuiltinsAssembler) {
TNode<IntPtrT> index = IntPtrSub(identity_hash, IntPtrConstant(1));
// Check if we need to grow the [[ValuesArray]] to store {value} at {index}.
- TNode<JSArray> values_array = CAST(
- LoadContextElement(context, kPromiseAllResolveElementValuesArraySlot));
+ TNode<JSArray> values_array = CAST(LoadContextElement(
+ context, PromiseBuiltins::kPromiseAllResolveElementValuesArraySlot));
TNode<FixedArray> elements = CAST(LoadElements(values_array));
TNode<IntPtrT> values_length =
LoadAndUntagObjectField(values_array, JSArray::kLengthOffset);
@@ -2153,17 +2254,18 @@ TF_BUILTIN(PromiseAllResolveElementClosure, PromiseBuiltinsAssembler) {
}
BIND(&done);
- TNode<Smi> remaining_elements_count =
- CAST(LoadContextElement(context, kPromiseAllResolveElementRemainingSlot));
+ TNode<Smi> remaining_elements_count = CAST(LoadContextElement(
+ context, PromiseBuiltins::kPromiseAllResolveElementRemainingSlot));
remaining_elements_count = SmiSub(remaining_elements_count, SmiConstant(1));
- StoreContextElement(context, kPromiseAllResolveElementRemainingSlot,
+ StoreContextElement(context,
+ PromiseBuiltins::kPromiseAllResolveElementRemainingSlot,
remaining_elements_count);
GotoIf(SmiEqual(remaining_elements_count, SmiConstant(0)), &resolve_promise);
Return(UndefinedConstant());
BIND(&resolve_promise);
- TNode<PromiseCapability> capability = CAST(
- LoadContextElement(context, kPromiseAllResolveElementCapabilitySlot));
+ TNode<PromiseCapability> capability = CAST(LoadContextElement(
+ context, PromiseBuiltins::kPromiseAllResolveElementCapabilitySlot));
TNode<Object> resolve =
LoadObjectField(capability, PromiseCapability::kResolveOffset);
CallJS(CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
@@ -2200,14 +2302,12 @@ TF_BUILTIN(PromiseRace, PromiseBuiltinsAssembler) {
Node* const reject =
LoadObjectField(capability, PromiseCapability::kRejectOffset);
- Node* const instrumenting = IsDebugActive();
-
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.
- SetForwardingHandlerIfTrue(context, instrumenting, reject);
+ SetForwardingHandlerIfTrue(context, IsDebugActive(), reject);
// Let iterator be GetIterator(iterable).
// IfAbruptRejectPromise(iterator, promiseCapability).
@@ -2259,7 +2359,7 @@ TF_BUILTIN(PromiseRace, PromiseBuiltinsAssembler) {
// For catch prediction, mark that rejections here are semantically
// handled by the combined Promise.
- SetPromiseHandledByIfTrue(context, instrumenting, then_call, [=]() {
+ SetPromiseHandledByIfTrue(context, IsDebugActive(), then_call, [=]() {
// Load promiseCapability.[[Promise]]
return LoadObjectField(capability, PromiseCapability::kPromiseOffset);
});