[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:
Joshua Litt 2019-10-31 13:23:09 -07:00 committed by Commit Bot
parent 7f4a2ec4d9
commit aeda4157d4
8 changed files with 279 additions and 206 deletions

View File

@ -1151,12 +1151,16 @@ const kPromisePending: constexpr PromiseState
generates 'Promise::kPending'; generates 'Promise::kPending';
const kPromiseFulfilled: constexpr PromiseState const kPromiseFulfilled: constexpr PromiseState
generates 'Promise::kFulfilled'; generates 'Promise::kFulfilled';
const kPromiseRejected: constexpr PromiseState
generates 'Promise::kRejected';
// JSPromise constants // JSPromise constants
const kJSPromiseStatusMask: constexpr int31 const kJSPromiseStatusMask: constexpr int31
generates 'JSPromise::kStatusMask'; generates 'JSPromise::kStatusMask';
const kJSPromiseStatusShift: constexpr int31 const kJSPromiseStatusShift: constexpr int31
generates 'JSPromise::kStatusShift'; generates 'JSPromise::kStatusShift';
const kJSPromiseHasHandlerMask: constexpr int31
generates 'JSPromise::kHasHandlerMask';
@generateCppClass @generateCppClass
extern class JSPromise extends JSObject { extern class JSPromise extends JSObject {
@ -1174,9 +1178,13 @@ extern class JSPromise extends JSObject {
this.flags = this.flags | mask; this.flags = this.flags | mask;
} }
HasHandler(): bool {
return (this.flags & kJSPromiseHasHandlerMask) != 0;
}
// Smi 0 terminated list of PromiseReaction objects in case the JSPromise was // Smi 0 terminated list of PromiseReaction objects in case the JSPromise was
// not settled yet, otherwise the result. // not settled yet, otherwise the result.
reactions_or_result: Smi|PromiseReaction|JSAny; reactions_or_result: Zero|PromiseReaction|JSAny;
flags: Smi; flags: Smi;
} }
@ -1356,6 +1364,10 @@ const kEmptyFixedArrayRootIndex:
constexpr RootIndex generates 'RootIndex::kEmptyFixedArray'; constexpr RootIndex generates 'RootIndex::kEmptyFixedArray';
const kTheHoleValueRootIndex: const kTheHoleValueRootIndex:
constexpr RootIndex generates 'RootIndex::kTheHoleValue'; constexpr RootIndex generates 'RootIndex::kTheHoleValue';
const kPromiseFulfillReactionJobTaskMapRootIndex: constexpr RootIndex
generates 'RootIndex::kPromiseFulfillReactionJobTaskMap';
const kPromiseRejectReactionJobTaskMapRootIndex: constexpr RootIndex
generates 'RootIndex::kPromiseRejectReactionJobTaskMap';
const kInvalidArrayBufferLength: constexpr MessageTemplate const kInvalidArrayBufferLength: constexpr MessageTemplate
generates 'MessageTemplate::kInvalidArrayBufferLength'; generates 'MessageTemplate::kInvalidArrayBufferLength';
@ -1490,6 +1502,7 @@ const False: False = FalseConstant();
const kEmptyString: EmptyString = EmptyStringConstant(); const kEmptyString: EmptyString = EmptyStringConstant();
const kLengthString: String = LengthStringConstant(); const kLengthString: String = LengthStringConstant();
const kNaN: NaN = NanConstant(); const kNaN: NaN = NanConstant();
const kZero: Zero = %RawDownCast<Zero>(SmiConstant(0));
const true: constexpr bool generates 'true'; const true: constexpr bool generates 'true';
const false: constexpr bool generates 'false'; const false: constexpr bool generates 'false';
@ -1532,6 +1545,14 @@ extern class PromiseCapability extends Struct {
type PromiseReactionType extends int31 constexpr 'PromiseReaction::Type'; type PromiseReactionType extends int31 constexpr 'PromiseReaction::Type';
const kPromiseReactionFulfill: constexpr PromiseReactionType const kPromiseReactionFulfill: constexpr PromiseReactionType
generates 'PromiseReaction::kFulfill'; 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 @generateCppClass
extern class PromiseReaction extends Struct { extern class PromiseReaction extends Struct {
@ -1543,6 +1564,14 @@ extern class PromiseReaction extends Struct {
promise_or_capability: JSPromise|PromiseCapability|Undefined; 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 @abstract
@generateCppClass @generateCppClass
extern class PromiseReactionJobTask extends Microtask { extern class PromiseReactionJobTask extends Microtask {
@ -2275,6 +2304,11 @@ Cast<PositiveSmi>(o: Object): PositiveSmi
return TaggedToPositiveSmi(o) otherwise CastError; 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 Cast<Number>(o: Object): Number
labels CastError { labels CastError {
return TaggedToNumber(o) otherwise 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 { Cast<PromiseReaction>(o: HeapObject): PromiseReaction labels CastError {
if (IsPromiseReaction(o)) return %RawDownCast<PromiseReaction>(o); if (IsPromiseReaction(o)) return %RawDownCast<PromiseReaction>(o);
goto CastError; 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 { Cast<PromiseCapability>(o: HeapObject): PromiseCapability labels CastError {
if (IsPromiseCapability(o)) return %RawDownCast<PromiseCapability>(o); if (IsPromiseCapability(o)) return %RawDownCast<PromiseCapability>(o);
goto CastError; goto CastError;
@ -3566,6 +3636,7 @@ extern macro IsJSRegExp(HeapObject): bool;
extern macro IsJSRegExpStringIterator(HeapObject): bool; extern macro IsJSRegExpStringIterator(HeapObject): bool;
extern macro IsMap(HeapObject): bool; extern macro IsMap(HeapObject): bool;
extern macro IsJSFunction(HeapObject): bool; extern macro IsJSFunction(HeapObject): bool;
extern macro IsJSBoundFunction(HeapObject): bool;
extern macro IsJSObject(HeapObject): bool; extern macro IsJSObject(HeapObject): bool;
extern macro IsJSPromise(HeapObject): bool; extern macro IsJSPromise(HeapObject): bool;
extern macro IsJSTypedArray(HeapObject): bool; extern macro IsJSTypedArray(HeapObject): bool;
@ -3589,6 +3660,8 @@ extern macro IsExtensibleMap(Map): bool;
extern macro IsJSPrimitiveWrapper(HeapObject): bool; extern macro IsJSPrimitiveWrapper(HeapObject): bool;
extern macro IsPromiseCapability(HeapObject): bool; extern macro IsPromiseCapability(HeapObject): bool;
extern macro IsPromiseReaction(HeapObject): bool; extern macro IsPromiseReaction(HeapObject): bool;
extern macro IsPromiseRejectReactionJobTask(HeapObject): bool;
extern macro IsPromiseFulfillReactionJobTask(HeapObject): bool;
extern macro IsSharedFunctionInfo(HeapObject): bool; extern macro IsSharedFunctionInfo(HeapObject): bool;
extern macro IsCustomElementsReceiverInstanceType(int32): bool; extern macro IsCustomElementsReceiverInstanceType(int32): bool;
extern macro Typeof(JSAny): String; extern macro Typeof(JSAny): String;

View File

@ -747,8 +747,6 @@ namespace internal {
TFS(ForInFilter, kKey, kObject) \ TFS(ForInFilter, kKey, kObject) \
\ \
/* Promise */ \ /* Promise */ \
/* ES #sec-rejectpromise */ \
TFS(RejectPromise, kPromise, kReason, kDebugEvent) \
/* ES #sec-promise-resolve-functions */ \ /* ES #sec-promise-resolve-functions */ \
/* Starting at step 6 of "Promise Resolve Functions" */ \ /* Starting at step 6 of "Promise Resolve Functions" */ \
TFS(ResolvePromise, kPromise, kResolution) \ TFS(ResolvePromise, kPromise, kResolution) \

View File

@ -246,19 +246,6 @@ TNode<Word32T> PromiseBuiltinsAssembler::PromiseStatus(Node* promise) {
return Word32And(SmiToInt32(flags), Int32Constant(JSPromise::kStatusMask)); 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) { void PromiseBuiltinsAssembler::PromiseSetHandledHint(Node* promise) {
const TNode<Smi> flags = const TNode<Smi> flags =
CAST(LoadObjectField(promise, JSPromise::kFlagsOffset)); CAST(LoadObjectField(promise, JSPromise::kFlagsOffset));
@ -441,128 +428,6 @@ PromiseBuiltinsAssembler::AllocatePromiseResolveThenableJobTask(
return CAST(microtask); 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> template <typename... TArgs>
Node* PromiseBuiltinsAssembler::InvokeThen(Node* native_context, Node* receiver, Node* PromiseBuiltinsAssembler::InvokeThen(Node* native_context, Node* receiver,
TArgs... args) { TArgs... args) {
@ -1680,52 +1545,6 @@ TF_BUILTIN(PromisePrototypeFinally, PromiseBuiltinsAssembler) {
var_catch_finally.value())); 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 // ES #sec-promise-resolve-functions
TF_BUILTIN(ResolvePromise, PromiseBuiltinsAssembler) { TF_BUILTIN(ResolvePromise, PromiseBuiltinsAssembler) {
const TNode<JSPromise> promise = CAST(Parameter(Descriptor::kPromise)); const TNode<JSPromise> promise = CAST(Parameter(Descriptor::kPromise));

View File

@ -66,11 +66,6 @@ class V8_EXPORT_PRIVATE PromiseBuiltinsAssembler : public CodeStubAssembler {
TNode<JSPromise> promise, TNode<Object> debug_event, TNode<JSPromise> promise, TNode<Object> debug_event,
TNode<NativeContext> native_context); 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: protected:
void PromiseInit(Node* promise); void PromiseInit(Node* promise);
@ -158,8 +153,6 @@ class V8_EXPORT_PRIVATE PromiseBuiltinsAssembler : public CodeStubAssembler {
TNode<BoolT> IsPromiseStatus(TNode<Word32T> actual, TNode<BoolT> IsPromiseStatus(TNode<Word32T> actual,
v8::Promise::PromiseState expected); v8::Promise::PromiseState expected);
void PromiseSetStatus(Node* promise, v8::Promise::PromiseState status);
TNode<JSPromise> AllocateJSPromise(TNode<Context> context); TNode<JSPromise> AllocateJSPromise(TNode<Context> context);
void ExtractHandlerContext(Node* handler, Variable* var_context); void ExtractHandlerContext(Node* handler, Variable* var_context);

View File

@ -5,6 +5,11 @@
#include 'src/builtins/builtins-promise.h' #include 'src/builtins/builtins-promise.h'
#include 'src/builtins/builtins-promise-gen.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 // https://tc39.es/ecma262/#sec-promise-abstract-operations
namespace promise { namespace promise {
const PROMISE_FUNCTION_INDEX: constexpr NativeContextSlot const PROMISE_FUNCTION_INDEX: constexpr NativeContextSlot
@ -30,10 +35,138 @@ namespace promise {
extern macro AllocateFunctionWithMapAndContext( extern macro AllocateFunctionWithMapAndContext(
Map, SharedFunctionInfo, Context): JSFunction; Map, SharedFunctionInfo, Context): JSFunction;
extern macro PromiseBuiltinsAssembler::TriggerPromiseReactions( extern macro PromiseReactionMapConstant(): Map;
implicit context: extern macro PromiseFulfillReactionJobTaskMapConstant(): Map;
Context)(Smi|PromiseReaction, JSAny, constexpr PromiseReactionType): extern macro PromiseRejectReactionJobTaskMapConstant(): Map;
void;
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 // https://tc39.es/ecma262/#sec-fulfillpromise
transitioning builtin transitioning builtin
@ -44,7 +177,7 @@ namespace promise {
// 2. Let reactions be promise.[[PromiseFulfillReactions]]. // 2. Let reactions be promise.[[PromiseFulfillReactions]].
const reactions = const reactions =
UnsafeCast<(Smi | PromiseReaction)>(promise.reactions_or_result); UnsafeCast<(Zero | PromiseReaction)>(promise.reactions_or_result);
// 3. Set promise.[[PromiseResult]] to value. // 3. Set promise.[[PromiseResult]] to value.
// 4. Set promise.[[PromiseFulfillReactions]] to undefined. // 4. Set promise.[[PromiseFulfillReactions]] to undefined.
@ -59,6 +192,43 @@ namespace promise {
return Undefined; 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: const kPromiseCapabilitySize:
constexpr int31 generates 'PromiseCapability::kSize'; constexpr int31 generates 'PromiseCapability::kSize';
const kPromiseBuiltinsCapabilitiesContextLength: constexpr int31 const kPromiseBuiltinsCapabilitiesContextLength: constexpr int31

View File

@ -6332,6 +6332,16 @@ TNode<BoolT> CodeStubAssembler::IsPromiseReaction(
return HasInstanceType(object, PROMISE_REACTION_TYPE); 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 // 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 // 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 // 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)); 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) { TNode<BoolT> CodeStubAssembler::IsJSFunctionMap(SloppyTNode<Map> map) {
return IsJSFunctionInstanceType(LoadMapInstanceType(map)); return IsJSFunctionInstanceType(LoadMapInstanceType(map));
} }
@ -13102,25 +13117,25 @@ TNode<BoolT> CodeStubAssembler::IsElementsKindInRange(
Int32Constant(higher_reference_kind - lower_reference_kind)); Int32Constant(higher_reference_kind - lower_reference_kind));
} }
Node* CodeStubAssembler::IsDebugActive() { TNode<BoolT> CodeStubAssembler::IsDebugActive() {
TNode<Uint8T> is_debug_active = Load<Uint8T>( TNode<Uint8T> is_debug_active = Load<Uint8T>(
ExternalConstant(ExternalReference::debug_is_active_address(isolate()))); ExternalConstant(ExternalReference::debug_is_active_address(isolate())));
return Word32NotEqual(is_debug_active, Int32Constant(0)); return Word32NotEqual(is_debug_active, Int32Constant(0));
} }
Node* CodeStubAssembler::IsPromiseHookEnabled() { TNode<BoolT> CodeStubAssembler::IsPromiseHookEnabled() {
const TNode<RawPtrT> promise_hook = Load<RawPtrT>( const TNode<RawPtrT> promise_hook = Load<RawPtrT>(
ExternalConstant(ExternalReference::promise_hook_address(isolate()))); ExternalConstant(ExternalReference::promise_hook_address(isolate())));
return WordNotEqual(promise_hook, IntPtrConstant(0)); return WordNotEqual(promise_hook, IntPtrConstant(0));
} }
Node* CodeStubAssembler::HasAsyncEventDelegate() { TNode<BoolT> CodeStubAssembler::HasAsyncEventDelegate() {
const TNode<RawPtrT> async_event_delegate = Load<RawPtrT>(ExternalConstant( const TNode<RawPtrT> async_event_delegate = Load<RawPtrT>(ExternalConstant(
ExternalReference::async_event_delegate_address(isolate()))); ExternalReference::async_event_delegate_address(isolate())));
return WordNotEqual(async_event_delegate, IntPtrConstant(0)); return WordNotEqual(async_event_delegate, IntPtrConstant(0));
} }
Node* CodeStubAssembler::IsPromiseHookEnabledOrHasAsyncEventDelegate() { TNode<BoolT> CodeStubAssembler::IsPromiseHookEnabledOrHasAsyncEventDelegate() {
const TNode<Uint8T> promise_hook_or_async_event_delegate = const TNode<Uint8T> promise_hook_or_async_event_delegate =
Load<Uint8T>(ExternalConstant( Load<Uint8T>(ExternalConstant(
ExternalReference::promise_hook_or_async_event_delegate_address( 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)); return Word32NotEqual(promise_hook_or_async_event_delegate, Int32Constant(0));
} }
Node* CodeStubAssembler:: TNode<BoolT> CodeStubAssembler::
IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate() { IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate() {
const TNode<Uint8T> promise_hook_or_debug_is_active_or_async_event_delegate = const TNode<Uint8T> promise_hook_or_debug_is_active_or_async_event_delegate =
Load<Uint8T>(ExternalConstant( Load<Uint8T>(ExternalConstant(

View File

@ -15,6 +15,7 @@
#include "src/objects/arguments.h" #include "src/objects/arguments.h"
#include "src/objects/bigint.h" #include "src/objects/bigint.h"
#include "src/objects/objects.h" #include "src/objects/objects.h"
#include "src/objects/promise.h"
#include "src/objects/shared-function-info.h" #include "src/objects/shared-function-info.h"
#include "src/objects/smi.h" #include "src/objects/smi.h"
#include "src/roots/roots.h" #include "src/roots/roots.h"
@ -2441,6 +2442,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<BoolT> IsAllocationSiteInstanceType(SloppyTNode<Int32T> instance_type); TNode<BoolT> IsAllocationSiteInstanceType(SloppyTNode<Int32T> instance_type);
TNode<BoolT> IsJSFunctionMap(SloppyTNode<Map> map); TNode<BoolT> IsJSFunctionMap(SloppyTNode<Map> map);
TNode<BoolT> IsJSFunction(SloppyTNode<HeapObject> object); TNode<BoolT> IsJSFunction(SloppyTNode<HeapObject> object);
TNode<BoolT> IsJSBoundFunction(SloppyTNode<HeapObject> object);
TNode<BoolT> IsJSGeneratorObject(TNode<HeapObject> object); TNode<BoolT> IsJSGeneratorObject(TNode<HeapObject> object);
TNode<BoolT> IsJSGlobalProxyInstanceType(SloppyTNode<Int32T> instance_type); TNode<BoolT> IsJSGlobalProxyInstanceType(SloppyTNode<Int32T> instance_type);
TNode<BoolT> IsJSGlobalProxyMap(SloppyTNode<Map> map); TNode<BoolT> IsJSGlobalProxyMap(SloppyTNode<Map> map);
@ -2480,6 +2482,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<BoolT> IsPropertyArray(SloppyTNode<HeapObject> object); TNode<BoolT> IsPropertyArray(SloppyTNode<HeapObject> object);
TNode<BoolT> IsPropertyCell(SloppyTNode<HeapObject> object); TNode<BoolT> IsPropertyCell(SloppyTNode<HeapObject> object);
TNode<BoolT> IsPromiseReaction(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, TNode<BoolT> IsPrototypeInitialArrayPrototype(SloppyTNode<Context> context,
SloppyTNode<Map> map); SloppyTNode<Map> map);
TNode<BoolT> IsPrototypeTypedArrayPrototype(SloppyTNode<Context> context, TNode<BoolT> IsPrototypeTypedArrayPrototype(SloppyTNode<Context> context,
@ -3457,7 +3461,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<Context> context); TNode<Context> context);
// Debug helpers // Debug helpers
Node* IsDebugActive(); TNode<BoolT> IsDebugActive();
// JSArrayBuffer helpers // JSArrayBuffer helpers
TNode<Uint32T> LoadJSArrayBufferBitField(TNode<JSArrayBuffer> array_buffer); TNode<Uint32T> LoadJSArrayBufferBitField(TNode<JSArrayBuffer> array_buffer);
@ -3510,10 +3514,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<Context> context); TNode<Context> context);
// Promise helpers // Promise helpers
Node* IsPromiseHookEnabled(); TNode<BoolT> IsPromiseHookEnabled();
Node* HasAsyncEventDelegate(); TNode<BoolT> HasAsyncEventDelegate();
Node* IsPromiseHookEnabledOrHasAsyncEventDelegate(); TNode<BoolT> IsPromiseHookEnabledOrHasAsyncEventDelegate();
Node* IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(); TNode<BoolT> IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate();
// for..in helpers // for..in helpers
void CheckPrototypeEnumCache(Node* receiver, Node* receiver_map, void CheckPrototypeEnumCache(Node* receiver, Node* receiver_map,

View File

@ -74,6 +74,7 @@ class JSPromise : public TorqueGeneratedJSPromise<JSPromise, JSObject> {
static const int kStatusShift = 0; static const int kStatusShift = 0;
static const int kStatusMask = 0x3; static const int kStatusMask = 0x3;
static const int kHasHandlerMask = 0x4;
STATIC_ASSERT(v8::Promise::kPending == 0); STATIC_ASSERT(v8::Promise::kPending == 0);
STATIC_ASSERT(v8::Promise::kFulfilled == 1); STATIC_ASSERT(v8::Promise::kFulfilled == 1);
STATIC_ASSERT(v8::Promise::kRejected == 2); STATIC_ASSERT(v8::Promise::kRejected == 2);