[promises] Port RejectPromise to torque.
Also ports TriggerPromiseReaction and ExtractHandler to torque. Bug: v8:9838 Change-Id: I35c07dcf4a0cca988dfb4706557cd6ee6bc66efe Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1864583 Commit-Queue: Joshua Litt <joshualitt@chromium.org> Reviewed-by: Tobias Tebbi <tebbi@chromium.org> Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org> Cr-Commit-Position: refs/heads/master@{#64711}
This commit is contained in:
parent
7f4a2ec4d9
commit
aeda4157d4
@ -1151,12 +1151,16 @@ const kPromisePending: constexpr PromiseState
|
||||
generates 'Promise::kPending';
|
||||
const kPromiseFulfilled: constexpr PromiseState
|
||||
generates 'Promise::kFulfilled';
|
||||
const kPromiseRejected: constexpr PromiseState
|
||||
generates 'Promise::kRejected';
|
||||
|
||||
// JSPromise constants
|
||||
const kJSPromiseStatusMask: constexpr int31
|
||||
generates 'JSPromise::kStatusMask';
|
||||
const kJSPromiseStatusShift: constexpr int31
|
||||
generates 'JSPromise::kStatusShift';
|
||||
const kJSPromiseHasHandlerMask: constexpr int31
|
||||
generates 'JSPromise::kHasHandlerMask';
|
||||
|
||||
@generateCppClass
|
||||
extern class JSPromise extends JSObject {
|
||||
@ -1174,9 +1178,13 @@ extern class JSPromise extends JSObject {
|
||||
this.flags = this.flags | mask;
|
||||
}
|
||||
|
||||
HasHandler(): bool {
|
||||
return (this.flags & kJSPromiseHasHandlerMask) != 0;
|
||||
}
|
||||
|
||||
// Smi 0 terminated list of PromiseReaction objects in case the JSPromise was
|
||||
// not settled yet, otherwise the result.
|
||||
reactions_or_result: Smi|PromiseReaction|JSAny;
|
||||
reactions_or_result: Zero|PromiseReaction|JSAny;
|
||||
flags: Smi;
|
||||
}
|
||||
|
||||
@ -1356,6 +1364,10 @@ const kEmptyFixedArrayRootIndex:
|
||||
constexpr RootIndex generates 'RootIndex::kEmptyFixedArray';
|
||||
const kTheHoleValueRootIndex:
|
||||
constexpr RootIndex generates 'RootIndex::kTheHoleValue';
|
||||
const kPromiseFulfillReactionJobTaskMapRootIndex: constexpr RootIndex
|
||||
generates 'RootIndex::kPromiseFulfillReactionJobTaskMap';
|
||||
const kPromiseRejectReactionJobTaskMapRootIndex: constexpr RootIndex
|
||||
generates 'RootIndex::kPromiseRejectReactionJobTaskMap';
|
||||
|
||||
const kInvalidArrayBufferLength: constexpr MessageTemplate
|
||||
generates 'MessageTemplate::kInvalidArrayBufferLength';
|
||||
@ -1490,6 +1502,7 @@ const False: False = FalseConstant();
|
||||
const kEmptyString: EmptyString = EmptyStringConstant();
|
||||
const kLengthString: String = LengthStringConstant();
|
||||
const kNaN: NaN = NanConstant();
|
||||
const kZero: Zero = %RawDownCast<Zero>(SmiConstant(0));
|
||||
|
||||
const true: constexpr bool generates 'true';
|
||||
const false: constexpr bool generates 'false';
|
||||
@ -1532,6 +1545,14 @@ extern class PromiseCapability extends Struct {
|
||||
type PromiseReactionType extends int31 constexpr 'PromiseReaction::Type';
|
||||
const kPromiseReactionFulfill: constexpr PromiseReactionType
|
||||
generates 'PromiseReaction::kFulfill';
|
||||
const kPromiseReactionReject: constexpr PromiseReactionType
|
||||
generates 'PromiseReaction::kReject';
|
||||
const kPromiseReactionSize:
|
||||
constexpr int31 generates 'PromiseReaction::kSize';
|
||||
const kPromiseReactionFulfillHandlerOffset: constexpr int31
|
||||
generates 'PromiseReaction::kFulfillHandlerOffset';
|
||||
const kPromiseReactionPromiseOrCapabilityOffset: constexpr int31
|
||||
generates 'PromiseReaction::kPromiseOrCapabilityOffset';
|
||||
|
||||
@generateCppClass
|
||||
extern class PromiseReaction extends Struct {
|
||||
@ -1543,6 +1564,14 @@ extern class PromiseReaction extends Struct {
|
||||
promise_or_capability: JSPromise|PromiseCapability|Undefined;
|
||||
}
|
||||
|
||||
// PromiseReactionJobTask constants
|
||||
const kPromiseReactionJobTaskSizeOfAllPromiseReactionJobTasks: constexpr int31
|
||||
generates 'PromiseReactionJobTask::kSizeOfAllPromiseReactionJobTasks';
|
||||
const kPromiseReactionJobTaskHandlerOffset: constexpr int31
|
||||
generates 'PromiseReactionJobTask::kHandlerOffset';
|
||||
const kPromiseReactionJobTaskPromiseOrCapabilityOffset: constexpr int31
|
||||
generates 'PromiseReactionJobTask::kPromiseOrCapabilityOffset';
|
||||
|
||||
@abstract
|
||||
@generateCppClass
|
||||
extern class PromiseReactionJobTask extends Microtask {
|
||||
@ -2275,6 +2304,11 @@ Cast<PositiveSmi>(o: Object): PositiveSmi
|
||||
return TaggedToPositiveSmi(o) otherwise CastError;
|
||||
}
|
||||
|
||||
Cast<Zero>(o: Object): Zero labels CastError {
|
||||
if (TaggedEqual(o, SmiConstant(0))) return %RawDownCast<Zero>(o);
|
||||
goto CastError;
|
||||
}
|
||||
|
||||
Cast<Number>(o: Object): Number
|
||||
labels CastError {
|
||||
return TaggedToNumber(o) otherwise CastError;
|
||||
@ -2754,6 +2788,22 @@ Cast<JSReceiver|Null>(o: HeapObject): JSReceiver|Null
|
||||
}
|
||||
}
|
||||
|
||||
Cast<PromiseFulfillReactionJobTask>(o: HeapObject):
|
||||
PromiseFulfillReactionJobTask labels CastError {
|
||||
if (IsPromiseFulfillReactionJobTask(o)) {
|
||||
return %RawDownCast<PromiseFulfillReactionJobTask>(o);
|
||||
}
|
||||
goto CastError;
|
||||
}
|
||||
|
||||
Cast<PromiseRejectReactionJobTask>(o: HeapObject):
|
||||
PromiseRejectReactionJobTask labels CastError {
|
||||
if (IsPromiseRejectReactionJobTask(o)) {
|
||||
return %RawDownCast<PromiseRejectReactionJobTask>(o);
|
||||
}
|
||||
goto CastError;
|
||||
}
|
||||
|
||||
Cast<PromiseReaction>(o: HeapObject): PromiseReaction labels CastError {
|
||||
if (IsPromiseReaction(o)) return %RawDownCast<PromiseReaction>(o);
|
||||
goto CastError;
|
||||
@ -2773,6 +2823,26 @@ Cast<Smi|PromiseReaction>(o: Object): Smi|PromiseReaction labels CastError {
|
||||
}
|
||||
}
|
||||
|
||||
Cast<Zero|PromiseReaction>(implicit context: Context)(o: Object): Zero|
|
||||
PromiseReaction labels CastError {
|
||||
typeswitch (o) {
|
||||
case (o: Zero): {
|
||||
return o;
|
||||
}
|
||||
case (o: PromiseReaction): {
|
||||
return o;
|
||||
}
|
||||
case (Object): {
|
||||
goto CastError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Cast<JSBoundFunction>(o: HeapObject): JSBoundFunction labels CastError {
|
||||
if (IsJSBoundFunction(o)) return %RawDownCast<JSBoundFunction>(o);
|
||||
goto CastError;
|
||||
}
|
||||
|
||||
Cast<PromiseCapability>(o: HeapObject): PromiseCapability labels CastError {
|
||||
if (IsPromiseCapability(o)) return %RawDownCast<PromiseCapability>(o);
|
||||
goto CastError;
|
||||
@ -3566,6 +3636,7 @@ extern macro IsJSRegExp(HeapObject): bool;
|
||||
extern macro IsJSRegExpStringIterator(HeapObject): bool;
|
||||
extern macro IsMap(HeapObject): bool;
|
||||
extern macro IsJSFunction(HeapObject): bool;
|
||||
extern macro IsJSBoundFunction(HeapObject): bool;
|
||||
extern macro IsJSObject(HeapObject): bool;
|
||||
extern macro IsJSPromise(HeapObject): bool;
|
||||
extern macro IsJSTypedArray(HeapObject): bool;
|
||||
@ -3589,6 +3660,8 @@ extern macro IsExtensibleMap(Map): bool;
|
||||
extern macro IsJSPrimitiveWrapper(HeapObject): bool;
|
||||
extern macro IsPromiseCapability(HeapObject): bool;
|
||||
extern macro IsPromiseReaction(HeapObject): bool;
|
||||
extern macro IsPromiseRejectReactionJobTask(HeapObject): bool;
|
||||
extern macro IsPromiseFulfillReactionJobTask(HeapObject): bool;
|
||||
extern macro IsSharedFunctionInfo(HeapObject): bool;
|
||||
extern macro IsCustomElementsReceiverInstanceType(int32): bool;
|
||||
extern macro Typeof(JSAny): String;
|
||||
|
@ -747,8 +747,6 @@ namespace internal {
|
||||
TFS(ForInFilter, kKey, kObject) \
|
||||
\
|
||||
/* Promise */ \
|
||||
/* ES #sec-rejectpromise */ \
|
||||
TFS(RejectPromise, kPromise, kReason, kDebugEvent) \
|
||||
/* ES #sec-promise-resolve-functions */ \
|
||||
/* Starting at step 6 of "Promise Resolve Functions" */ \
|
||||
TFS(ResolvePromise, kPromise, kResolution) \
|
||||
|
@ -246,19 +246,6 @@ TNode<Word32T> PromiseBuiltinsAssembler::PromiseStatus(Node* promise) {
|
||||
return Word32And(SmiToInt32(flags), Int32Constant(JSPromise::kStatusMask));
|
||||
}
|
||||
|
||||
void PromiseBuiltinsAssembler::PromiseSetStatus(
|
||||
Node* promise, v8::Promise::PromiseState const status) {
|
||||
CSA_ASSERT(this,
|
||||
IsPromiseStatus(PromiseStatus(promise), v8::Promise::kPending));
|
||||
CHECK_NE(status, v8::Promise::kPending);
|
||||
|
||||
TNode<Smi> mask = SmiConstant(status);
|
||||
const TNode<Smi> flags =
|
||||
CAST(LoadObjectField(promise, JSPromise::kFlagsOffset));
|
||||
StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset,
|
||||
SmiOr(flags, mask));
|
||||
}
|
||||
|
||||
void PromiseBuiltinsAssembler::PromiseSetHandledHint(Node* promise) {
|
||||
const TNode<Smi> flags =
|
||||
CAST(LoadObjectField(promise, JSPromise::kFlagsOffset));
|
||||
@ -441,128 +428,6 @@ PromiseBuiltinsAssembler::AllocatePromiseResolveThenableJobTask(
|
||||
return CAST(microtask);
|
||||
}
|
||||
|
||||
// ES #sec-triggerpromisereactions
|
||||
void 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.
|
||||
{
|
||||
VARIABLE(var_current, MachineRepresentation::kTagged, reactions);
|
||||
VARIABLE(var_reversed, MachineRepresentation::kTagged,
|
||||
SmiConstant(Smi::zero()));
|
||||
|
||||
// As an additional safety net against misuse of the V8 Extras API, we
|
||||
// sanity check the {reactions} to make sure that they are actually
|
||||
// PromiseReaction instances and not actual JavaScript values (which
|
||||
// would indicate that we're rejecting or resolving an already settled
|
||||
// promise), see https://crbug.com/931640 for details on this.
|
||||
TNode<Map> promise_reaction_map = PromiseReactionMapConstant();
|
||||
|
||||
Label loop(this, {&var_current, &var_reversed}), done_loop(this);
|
||||
Goto(&loop);
|
||||
BIND(&loop);
|
||||
{
|
||||
Node* current = var_current.value();
|
||||
GotoIf(TaggedIsSmi(current), &done_loop);
|
||||
CSA_CHECK(this,
|
||||
TaggedEqual(LoadMap(CAST(current)), promise_reaction_map));
|
||||
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();
|
||||
}
|
||||
|
||||
// Morph the {reactions} into PromiseReactionJobTasks and push them
|
||||
// onto the microtask queue.
|
||||
{
|
||||
VARIABLE(var_current, MachineRepresentation::kTagged, reactions);
|
||||
|
||||
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));
|
||||
|
||||
VARIABLE(var_context, MachineRepresentation::kTagged,
|
||||
UndefinedConstant());
|
||||
|
||||
Node* primary_handler;
|
||||
Node* secondary_handler;
|
||||
if (type == PromiseReaction::kFulfill) {
|
||||
primary_handler =
|
||||
LoadObjectField(current, PromiseReaction::kFulfillHandlerOffset);
|
||||
secondary_handler =
|
||||
LoadObjectField(current, PromiseReaction::kRejectHandlerOffset);
|
||||
} else {
|
||||
primary_handler =
|
||||
LoadObjectField(current, PromiseReaction::kRejectHandlerOffset);
|
||||
secondary_handler =
|
||||
LoadObjectField(current, PromiseReaction::kFulfillHandlerOffset);
|
||||
}
|
||||
|
||||
{
|
||||
Label use_fallback(this, Label::kDeferred), done(this);
|
||||
ExtractHandlerContext(primary_handler, &var_context);
|
||||
Branch(IsUndefined(var_context.value()), &use_fallback, &done);
|
||||
|
||||
BIND(&use_fallback);
|
||||
var_context.Bind(context);
|
||||
ExtractHandlerContext(secondary_handler, &var_context);
|
||||
CSA_ASSERT(this, IsNotUndefined(var_context.value()));
|
||||
Goto(&done);
|
||||
|
||||
BIND(&done);
|
||||
}
|
||||
|
||||
// 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(
|
||||
static_cast<int>(PromiseReaction::kSize) ==
|
||||
static_cast<int>(
|
||||
PromiseReactionJobTask::kSizeOfAllPromiseReactionJobTasks));
|
||||
if (type == PromiseReaction::kFulfill) {
|
||||
StoreMapNoWriteBarrier(current,
|
||||
RootIndex::kPromiseFulfillReactionJobTaskMap);
|
||||
StoreObjectField(current, PromiseReactionJobTask::kArgumentOffset,
|
||||
argument);
|
||||
StoreObjectField(current, PromiseReactionJobTask::kContextOffset,
|
||||
var_context.value());
|
||||
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 {
|
||||
StoreMapNoWriteBarrier(current,
|
||||
RootIndex::kPromiseRejectReactionJobTaskMap);
|
||||
StoreObjectField(current, PromiseReactionJobTask::kArgumentOffset,
|
||||
argument);
|
||||
StoreObjectField(current, PromiseReactionJobTask::kContextOffset,
|
||||
var_context.value());
|
||||
StoreObjectField(current, PromiseReactionJobTask::kHandlerOffset,
|
||||
primary_handler);
|
||||
STATIC_ASSERT(
|
||||
static_cast<int>(PromiseReaction::kPromiseOrCapabilityOffset) ==
|
||||
static_cast<int>(
|
||||
PromiseReactionJobTask::kPromiseOrCapabilityOffset));
|
||||
}
|
||||
CallBuiltin(Builtins::kEnqueueMicrotask, var_context.value(), current);
|
||||
Goto(&loop);
|
||||
}
|
||||
BIND(&done_loop);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... TArgs>
|
||||
Node* PromiseBuiltinsAssembler::InvokeThen(Node* native_context, Node* receiver,
|
||||
TArgs... args) {
|
||||
@ -1680,52 +1545,6 @@ TF_BUILTIN(PromisePrototypeFinally, PromiseBuiltinsAssembler) {
|
||||
var_catch_finally.value()));
|
||||
}
|
||||
|
||||
// ES #sec-rejectpromise
|
||||
TF_BUILTIN(RejectPromise, PromiseBuiltinsAssembler) {
|
||||
Node* const promise = Parameter(Descriptor::kPromise);
|
||||
Node* const reason = Parameter(Descriptor::kReason);
|
||||
Node* const debug_event = Parameter(Descriptor::kDebugEvent);
|
||||
Node* const context = Parameter(Descriptor::kContext);
|
||||
|
||||
CSA_ASSERT(this, TaggedIsNotSmi(promise));
|
||||
CSA_ASSERT(this, IsJSPromise(promise));
|
||||
CSA_ASSERT(this, IsBoolean(debug_event));
|
||||
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.
|
||||
GotoIf(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(),
|
||||
&if_runtime);
|
||||
|
||||
// 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]].
|
||||
TNode<Object> 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).
|
||||
TriggerPromiseReactions(context, reactions, reason, PromiseReaction::kReject);
|
||||
Return(UndefinedConstant());
|
||||
|
||||
BIND(&if_runtime);
|
||||
TailCallRuntime(Runtime::kRejectPromise, context, promise, reason,
|
||||
debug_event);
|
||||
}
|
||||
|
||||
// ES #sec-promise-resolve-functions
|
||||
TF_BUILTIN(ResolvePromise, PromiseBuiltinsAssembler) {
|
||||
const TNode<JSPromise> promise = CAST(Parameter(Descriptor::kPromise));
|
||||
|
@ -66,11 +66,6 @@ class V8_EXPORT_PRIVATE PromiseBuiltinsAssembler : public CodeStubAssembler {
|
||||
TNode<JSPromise> promise, TNode<Object> debug_event,
|
||||
TNode<NativeContext> native_context);
|
||||
|
||||
// The below methods are only temporarily public until they are
|
||||
// migrated to torque.
|
||||
void TriggerPromiseReactions(Node* context, Node* promise, Node* result,
|
||||
PromiseReaction::Type type);
|
||||
|
||||
protected:
|
||||
void PromiseInit(Node* promise);
|
||||
|
||||
@ -158,8 +153,6 @@ class V8_EXPORT_PRIVATE PromiseBuiltinsAssembler : public CodeStubAssembler {
|
||||
|
||||
TNode<BoolT> IsPromiseStatus(TNode<Word32T> actual,
|
||||
v8::Promise::PromiseState expected);
|
||||
void PromiseSetStatus(Node* promise, v8::Promise::PromiseState status);
|
||||
|
||||
TNode<JSPromise> AllocateJSPromise(TNode<Context> context);
|
||||
|
||||
void ExtractHandlerContext(Node* handler, Variable* var_context);
|
||||
|
@ -5,6 +5,11 @@
|
||||
#include 'src/builtins/builtins-promise.h'
|
||||
#include 'src/builtins/builtins-promise-gen.h'
|
||||
|
||||
namespace runtime {
|
||||
extern transitioning runtime
|
||||
RejectPromise(implicit context: Context)(JSPromise, JSAny, Boolean): Object;
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-promise-abstract-operations
|
||||
namespace promise {
|
||||
const PROMISE_FUNCTION_INDEX: constexpr NativeContextSlot
|
||||
@ -30,10 +35,138 @@ namespace promise {
|
||||
extern macro AllocateFunctionWithMapAndContext(
|
||||
Map, SharedFunctionInfo, Context): JSFunction;
|
||||
|
||||
extern macro PromiseBuiltinsAssembler::TriggerPromiseReactions(
|
||||
implicit context:
|
||||
Context)(Smi|PromiseReaction, JSAny, constexpr PromiseReactionType):
|
||||
void;
|
||||
extern macro PromiseReactionMapConstant(): Map;
|
||||
extern macro PromiseFulfillReactionJobTaskMapConstant(): Map;
|
||||
extern macro PromiseRejectReactionJobTaskMapConstant(): Map;
|
||||
|
||||
extern transitioning builtin
|
||||
EnqueueMicrotask(Context, PromiseReactionJobTask): Undefined;
|
||||
|
||||
macro
|
||||
ExtractHandlerContext(implicit context: Context)(handler: Callable|Undefined):
|
||||
Context labels NotFound {
|
||||
let iter: JSAny = handler;
|
||||
while (true) {
|
||||
typeswitch (iter) {
|
||||
case (b: JSBoundFunction): {
|
||||
iter = b.bound_target_function;
|
||||
}
|
||||
case (p: JSProxy): {
|
||||
iter = p.target;
|
||||
}
|
||||
case (f: JSFunction): {
|
||||
return f.context;
|
||||
}
|
||||
case (JSAny): {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
goto NotFound;
|
||||
}
|
||||
|
||||
transitioning macro MorpAndEnqueuePromiseReaction(implicit context: Context)(
|
||||
promiseReaction: PromiseReaction, argument: JSAny,
|
||||
reactionType: constexpr PromiseReactionType): void {
|
||||
let primaryHandler: Callable|Undefined;
|
||||
let secondaryHandler: Callable|Undefined;
|
||||
if constexpr (reactionType == kPromiseReactionFulfill) {
|
||||
primaryHandler = promiseReaction.fulfill_handler;
|
||||
secondaryHandler = promiseReaction.reject_handler;
|
||||
} else {
|
||||
StaticAssert(reactionType == kPromiseReactionReject);
|
||||
primaryHandler = promiseReaction.reject_handler;
|
||||
secondaryHandler = promiseReaction.fulfill_handler;
|
||||
}
|
||||
|
||||
let handlerContext: Context;
|
||||
try {
|
||||
handlerContext = ExtractHandlerContext(primaryHandler) otherwise NotFound;
|
||||
}
|
||||
label NotFound {
|
||||
handlerContext =
|
||||
ExtractHandlerContext(secondaryHandler) otherwise Default;
|
||||
}
|
||||
label Default {
|
||||
handlerContext = context;
|
||||
}
|
||||
|
||||
// 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.
|
||||
StaticAssert(
|
||||
kPromiseReactionSize ==
|
||||
kPromiseReactionJobTaskSizeOfAllPromiseReactionJobTasks);
|
||||
if constexpr (reactionType == kPromiseReactionFulfill) {
|
||||
promiseReaction.map = PromiseFulfillReactionJobTaskMapConstant();
|
||||
const promiseReactionJobTask =
|
||||
UnsafeCast<PromiseFulfillReactionJobTask>(promiseReaction);
|
||||
promiseReactionJobTask.argument = argument;
|
||||
promiseReactionJobTask.context = handlerContext;
|
||||
EnqueueMicrotask(handlerContext, promiseReactionJobTask);
|
||||
StaticAssert(
|
||||
kPromiseReactionFulfillHandlerOffset ==
|
||||
kPromiseReactionJobTaskHandlerOffset);
|
||||
StaticAssert(
|
||||
kPromiseReactionPromiseOrCapabilityOffset ==
|
||||
kPromiseReactionJobTaskPromiseOrCapabilityOffset);
|
||||
} else {
|
||||
StaticAssert(reactionType == kPromiseReactionReject);
|
||||
promiseReaction.map = PromiseRejectReactionJobTaskMapConstant();
|
||||
const promiseReactionJobTask =
|
||||
UnsafeCast<PromiseRejectReactionJobTask>(promiseReaction);
|
||||
promiseReactionJobTask.argument = argument;
|
||||
promiseReactionJobTask.context = handlerContext;
|
||||
promiseReactionJobTask.handler = primaryHandler;
|
||||
EnqueueMicrotask(handlerContext, promiseReactionJobTask);
|
||||
StaticAssert(
|
||||
kPromiseReactionPromiseOrCapabilityOffset ==
|
||||
kPromiseReactionJobTaskPromiseOrCapabilityOffset);
|
||||
}
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-triggerpromisereactions
|
||||
transitioning macro TriggerPromiseReactions(implicit context: Context)(
|
||||
reactions: Zero|PromiseReaction, argument: JSAny,
|
||||
reactionType: constexpr PromiseReactionType): void {
|
||||
// We need to reverse the {reactions} here, since we record them on the
|
||||
// JSPromise in the reverse order.
|
||||
let current = reactions;
|
||||
let reversed: Zero|PromiseReaction = kZero;
|
||||
|
||||
// As an additional safety net against misuse of the V8 Extras API, we
|
||||
// sanity check the {reactions} to make sure that they are actually
|
||||
// PromiseReaction instances and not actual JavaScript values (which
|
||||
// would indicate that we're rejecting or resolving an already settled
|
||||
// promise), see https://crbug.com/931640 for details on this.
|
||||
while (true) {
|
||||
typeswitch (current) {
|
||||
case (Zero): {
|
||||
break;
|
||||
}
|
||||
case (currentReaction: PromiseReaction): {
|
||||
current = currentReaction.next;
|
||||
currentReaction.next = reversed;
|
||||
reversed = currentReaction;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Morph the {reactions} into PromiseReactionJobTasks and push them
|
||||
// onto the microtask queue.
|
||||
current = reversed;
|
||||
while (true) {
|
||||
typeswitch (current) {
|
||||
case (Zero): {
|
||||
break;
|
||||
}
|
||||
case (currentReaction: PromiseReaction): {
|
||||
current = currentReaction.next;
|
||||
MorpAndEnqueuePromiseReaction(
|
||||
currentReaction, argument, reactionType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-fulfillpromise
|
||||
transitioning builtin
|
||||
@ -44,7 +177,7 @@ namespace promise {
|
||||
|
||||
// 2. Let reactions be promise.[[PromiseFulfillReactions]].
|
||||
const reactions =
|
||||
UnsafeCast<(Smi | PromiseReaction)>(promise.reactions_or_result);
|
||||
UnsafeCast<(Zero | PromiseReaction)>(promise.reactions_or_result);
|
||||
|
||||
// 3. Set promise.[[PromiseResult]] to value.
|
||||
// 4. Set promise.[[PromiseFulfillReactions]] to undefined.
|
||||
@ -59,6 +192,43 @@ namespace promise {
|
||||
return Undefined;
|
||||
}
|
||||
|
||||
extern macro PromiseBuiltinsAssembler::
|
||||
IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(): bool;
|
||||
|
||||
// https://tc39.es/ecma262/#sec-rejectpromise
|
||||
transitioning builtin
|
||||
RejectPromise(implicit context: Context)(
|
||||
promise: JSPromise, reason: JSAny, debugEvent: Boolean): Object {
|
||||
// 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.
|
||||
if (IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate() ||
|
||||
!promise.HasHandler()) {
|
||||
// 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.
|
||||
return runtime::RejectPromise(promise, reason, debugEvent);
|
||||
}
|
||||
|
||||
// 2. Let reactions be promise.[[PromiseRejectReactions]].
|
||||
const reactions =
|
||||
UnsafeCast<(Zero | PromiseReaction)>(promise.reactions_or_result);
|
||||
|
||||
// 3. Set promise.[[PromiseResult]] to reason.
|
||||
// 4. Set promise.[[PromiseFulfillReactions]] to undefined.
|
||||
// 5. Set promise.[[PromiseRejectReactions]] to undefined.
|
||||
promise.reactions_or_result = reason;
|
||||
|
||||
// 6. Set promise.[[PromiseState]] to "rejected".
|
||||
promise.SetStatus(kPromiseRejected);
|
||||
|
||||
// 8. Return TriggerPromiseReactions(reactions, reason).
|
||||
TriggerPromiseReactions(reactions, reason, kPromiseReactionReject);
|
||||
return Undefined;
|
||||
}
|
||||
|
||||
const kPromiseCapabilitySize:
|
||||
constexpr int31 generates 'PromiseCapability::kSize';
|
||||
const kPromiseBuiltinsCapabilitiesContextLength: constexpr int31
|
||||
|
@ -6332,6 +6332,16 @@ TNode<BoolT> CodeStubAssembler::IsPromiseReaction(
|
||||
return HasInstanceType(object, PROMISE_REACTION_TYPE);
|
||||
}
|
||||
|
||||
TNode<BoolT> CodeStubAssembler::IsPromiseRejectReactionJobTask(
|
||||
SloppyTNode<HeapObject> object) {
|
||||
return HasInstanceType(object, PROMISE_REJECT_REACTION_JOB_TASK_TYPE);
|
||||
}
|
||||
|
||||
TNode<BoolT> CodeStubAssembler::IsPromiseFulfillReactionJobTask(
|
||||
SloppyTNode<HeapObject> object) {
|
||||
return HasInstanceType(object, PROMISE_FULFILL_REACTION_JOB_TASK_TYPE);
|
||||
}
|
||||
|
||||
// This complicated check is due to elements oddities. If a smi array is empty
|
||||
// after Array.p.shift, it is replaced by the empty array constant. If it is
|
||||
// later filled with a double element, we try to grow it but pass in a double
|
||||
@ -6557,6 +6567,11 @@ TNode<BoolT> CodeStubAssembler::IsJSFunction(SloppyTNode<HeapObject> object) {
|
||||
return IsJSFunctionMap(LoadMap(object));
|
||||
}
|
||||
|
||||
TNode<BoolT> CodeStubAssembler::IsJSBoundFunction(
|
||||
SloppyTNode<HeapObject> object) {
|
||||
return HasInstanceType(object, JS_BOUND_FUNCTION_TYPE);
|
||||
}
|
||||
|
||||
TNode<BoolT> CodeStubAssembler::IsJSFunctionMap(SloppyTNode<Map> map) {
|
||||
return IsJSFunctionInstanceType(LoadMapInstanceType(map));
|
||||
}
|
||||
@ -13102,25 +13117,25 @@ TNode<BoolT> CodeStubAssembler::IsElementsKindInRange(
|
||||
Int32Constant(higher_reference_kind - lower_reference_kind));
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::IsDebugActive() {
|
||||
TNode<BoolT> CodeStubAssembler::IsDebugActive() {
|
||||
TNode<Uint8T> is_debug_active = Load<Uint8T>(
|
||||
ExternalConstant(ExternalReference::debug_is_active_address(isolate())));
|
||||
return Word32NotEqual(is_debug_active, Int32Constant(0));
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::IsPromiseHookEnabled() {
|
||||
TNode<BoolT> CodeStubAssembler::IsPromiseHookEnabled() {
|
||||
const TNode<RawPtrT> promise_hook = Load<RawPtrT>(
|
||||
ExternalConstant(ExternalReference::promise_hook_address(isolate())));
|
||||
return WordNotEqual(promise_hook, IntPtrConstant(0));
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::HasAsyncEventDelegate() {
|
||||
TNode<BoolT> CodeStubAssembler::HasAsyncEventDelegate() {
|
||||
const TNode<RawPtrT> async_event_delegate = Load<RawPtrT>(ExternalConstant(
|
||||
ExternalReference::async_event_delegate_address(isolate())));
|
||||
return WordNotEqual(async_event_delegate, IntPtrConstant(0));
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::IsPromiseHookEnabledOrHasAsyncEventDelegate() {
|
||||
TNode<BoolT> CodeStubAssembler::IsPromiseHookEnabledOrHasAsyncEventDelegate() {
|
||||
const TNode<Uint8T> promise_hook_or_async_event_delegate =
|
||||
Load<Uint8T>(ExternalConstant(
|
||||
ExternalReference::promise_hook_or_async_event_delegate_address(
|
||||
@ -13128,7 +13143,7 @@ Node* CodeStubAssembler::IsPromiseHookEnabledOrHasAsyncEventDelegate() {
|
||||
return Word32NotEqual(promise_hook_or_async_event_delegate, Int32Constant(0));
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::
|
||||
TNode<BoolT> CodeStubAssembler::
|
||||
IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate() {
|
||||
const TNode<Uint8T> promise_hook_or_debug_is_active_or_async_event_delegate =
|
||||
Load<Uint8T>(ExternalConstant(
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "src/objects/arguments.h"
|
||||
#include "src/objects/bigint.h"
|
||||
#include "src/objects/objects.h"
|
||||
#include "src/objects/promise.h"
|
||||
#include "src/objects/shared-function-info.h"
|
||||
#include "src/objects/smi.h"
|
||||
#include "src/roots/roots.h"
|
||||
@ -2441,6 +2442,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
||||
TNode<BoolT> IsAllocationSiteInstanceType(SloppyTNode<Int32T> instance_type);
|
||||
TNode<BoolT> IsJSFunctionMap(SloppyTNode<Map> map);
|
||||
TNode<BoolT> IsJSFunction(SloppyTNode<HeapObject> object);
|
||||
TNode<BoolT> IsJSBoundFunction(SloppyTNode<HeapObject> object);
|
||||
TNode<BoolT> IsJSGeneratorObject(TNode<HeapObject> object);
|
||||
TNode<BoolT> IsJSGlobalProxyInstanceType(SloppyTNode<Int32T> instance_type);
|
||||
TNode<BoolT> IsJSGlobalProxyMap(SloppyTNode<Map> map);
|
||||
@ -2480,6 +2482,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
||||
TNode<BoolT> IsPropertyArray(SloppyTNode<HeapObject> object);
|
||||
TNode<BoolT> IsPropertyCell(SloppyTNode<HeapObject> object);
|
||||
TNode<BoolT> IsPromiseReaction(SloppyTNode<HeapObject> object);
|
||||
TNode<BoolT> IsPromiseRejectReactionJobTask(SloppyTNode<HeapObject> object);
|
||||
TNode<BoolT> IsPromiseFulfillReactionJobTask(SloppyTNode<HeapObject> object);
|
||||
TNode<BoolT> IsPrototypeInitialArrayPrototype(SloppyTNode<Context> context,
|
||||
SloppyTNode<Map> map);
|
||||
TNode<BoolT> IsPrototypeTypedArrayPrototype(SloppyTNode<Context> context,
|
||||
@ -3457,7 +3461,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
||||
TNode<Context> context);
|
||||
|
||||
// Debug helpers
|
||||
Node* IsDebugActive();
|
||||
TNode<BoolT> IsDebugActive();
|
||||
|
||||
// JSArrayBuffer helpers
|
||||
TNode<Uint32T> LoadJSArrayBufferBitField(TNode<JSArrayBuffer> array_buffer);
|
||||
@ -3510,10 +3514,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
||||
TNode<Context> context);
|
||||
|
||||
// Promise helpers
|
||||
Node* IsPromiseHookEnabled();
|
||||
Node* HasAsyncEventDelegate();
|
||||
Node* IsPromiseHookEnabledOrHasAsyncEventDelegate();
|
||||
Node* IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate();
|
||||
TNode<BoolT> IsPromiseHookEnabled();
|
||||
TNode<BoolT> HasAsyncEventDelegate();
|
||||
TNode<BoolT> IsPromiseHookEnabledOrHasAsyncEventDelegate();
|
||||
TNode<BoolT> IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate();
|
||||
|
||||
// for..in helpers
|
||||
void CheckPrototypeEnumCache(Node* receiver, Node* receiver_map,
|
||||
|
@ -74,6 +74,7 @@ class JSPromise : public TorqueGeneratedJSPromise<JSPromise, JSObject> {
|
||||
|
||||
static const int kStatusShift = 0;
|
||||
static const int kStatusMask = 0x3;
|
||||
static const int kHasHandlerMask = 0x4;
|
||||
STATIC_ASSERT(v8::Promise::kPending == 0);
|
||||
STATIC_ASSERT(v8::Promise::kFulfilled == 1);
|
||||
STATIC_ASSERT(v8::Promise::kRejected == 2);
|
||||
|
Loading…
Reference in New Issue
Block a user