Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

deps: cherry-pick Swallowed Rejection Hook from V8 #21838

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion common.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

# Reset this number to 0 on major V8 upgrades.
# Increment by one for each non-official patch applied to deps/v8.
'v8_embedder_string': '-node.15',
'v8_embedder_string': '-node.17',

# Enable disassembler for `--print-code` v8 options
'v8_enable_disassembler': 1,
Expand Down
4 changes: 3 additions & 1 deletion deps/v8/include/v8.h
Original file line number Diff line number Diff line change
Expand Up @@ -6394,7 +6394,9 @@ typedef void (*PromiseHook)(PromiseHookType type, Local<Promise> promise,
// --- Promise Reject Callback ---
enum PromiseRejectEvent {
kPromiseRejectWithNoHandler = 0,
kPromiseHandlerAddedAfterReject = 1
kPromiseHandlerAddedAfterReject = 1,
kPromiseRejectAfterResolved = 2,
kPromiseResolveAfterResolved = 3,

This comment was marked as resolved.

};

class PromiseRejectMessage {
Expand Down
34 changes: 28 additions & 6 deletions deps/v8/src/builtins/builtins-promise-gen.cc
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ Node* PromiseBuiltinsAssembler::CreatePromiseResolvingFunctionsContext(
Node* const context =
CreatePromiseContext(native_context, kPromiseContextLength);
StoreContextElementNoWriteBarrier(context, kPromiseSlot, promise);
StoreContextElementNoWriteBarrier(context, kAlreadyResolvedSlot,
FalseConstant());
StoreContextElementNoWriteBarrier(context, kDebugEventSlot, debug_event);
return context;
}
Expand Down Expand Up @@ -733,17 +735,27 @@ TF_BUILTIN(PromiseCapabilityDefaultReject, PromiseBuiltinsAssembler) {
Node* const promise = LoadContextElement(context, kPromiseSlot);

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

// 4. If alreadyResolved.[[Value]] is true, return undefined.
// We use undefined as a marker for the [[AlreadyResolved]] state.
ReturnIf(IsUndefined(promise), UndefinedConstant());
GotoIf(IsTrue(already_resolved), &if_already_resolved);

// 5. Set alreadyResolved.[[Value]] to true.
StoreContextElementNoWriteBarrier(context, kPromiseSlot, UndefinedConstant());
StoreContextElementNoWriteBarrier(context, kAlreadyResolvedSlot,
TrueConstant());

// 6. Return RejectPromise(promise, reason).
Node* const debug_event = LoadContextElement(context, kDebugEventSlot);
Return(CallBuiltin(Builtins::kRejectPromise, context, promise, reason,
debug_event));

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

// ES #sec-promise-resolve-functions
Expand All @@ -755,16 +767,26 @@ TF_BUILTIN(PromiseCapabilityDefaultResolve, PromiseBuiltinsAssembler) {
Node* const promise = LoadContextElement(context, kPromiseSlot);

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

// 4. If alreadyResolved.[[Value]] is true, return undefined.
// We use undefined as a marker for the [[AlreadyResolved]] state.
ReturnIf(IsUndefined(promise), UndefinedConstant());
GotoIf(IsTrue(already_resolved), &if_already_resolved);

// 5. Set alreadyResolved.[[Value]] to true.
StoreContextElementNoWriteBarrier(context, kPromiseSlot, UndefinedConstant());
StoreContextElementNoWriteBarrier(context, kAlreadyResolvedSlot,
TrueConstant());

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

BIND(&if_already_resolved);
{
Return(CallRuntime(Runtime::kPromiseResolveAfterResolved, context, promise,
resolution));
}
}

TF_BUILTIN(PromiseConstructorLazyDeoptContinuation, PromiseBuiltinsAssembler) {
Expand Down
7 changes: 4 additions & 3 deletions deps/v8/src/builtins/builtins-promise-gen.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ typedef compiler::CodeAssemblerState CodeAssemblerState;
class PromiseBuiltinsAssembler : public CodeStubAssembler {
public:
enum PromiseResolvingFunctionContextSlot {
// The promise which resolve/reject callbacks fulfill. If this is
// undefined, then we've already visited this callback and it
// should be a no-op.
// The promise which resolve/reject callbacks fulfill.
kPromiseSlot = Context::MIN_CONTEXT_SLOTS,

// Whether the callback was already invoked.
kAlreadyResolvedSlot,

// Whether to trigger a debug event or not. Used in catch
// prediction.
kDebugEventSlot,
Expand Down
122 changes: 4 additions & 118 deletions deps/v8/src/compiler/js-call-reducer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3519,10 +3519,6 @@ Reduction JSCallReducer::ReduceJSCall(Node* node,
return ReduceAsyncFunctionPromiseCreate(node);
case Builtins::kAsyncFunctionPromiseRelease:
return ReduceAsyncFunctionPromiseRelease(node);
case Builtins::kPromiseCapabilityDefaultReject:
return ReducePromiseCapabilityDefaultReject(node);
case Builtins::kPromiseCapabilityDefaultResolve:
return ReducePromiseCapabilityDefaultResolve(node);
case Builtins::kPromiseInternalConstructor:
return ReducePromiseInternalConstructor(node);
case Builtins::kPromiseInternalReject:
Expand Down Expand Up @@ -5252,120 +5248,6 @@ Reduction JSCallReducer::ReduceAsyncFunctionPromiseRelease(Node* node) {
return Replace(value);
}

// ES section #sec-promise-reject-functions
Reduction JSCallReducer::ReducePromiseCapabilityDefaultReject(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
Node* target = NodeProperties::GetValueInput(node, 0);
Node* resolution = node->op()->ValueInputCount() > 2
? NodeProperties::GetValueInput(node, 2)
: jsgraph()->UndefinedConstant();
Node* frame_state = NodeProperties::GetFrameStateInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);

// We need to execute in the {target}s context.
Node* context = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSFunctionContext()), target,
effect, control);

// Grab the promise closed over by {target}.
Node* promise = effect =
graph()->NewNode(simplified()->LoadField(AccessBuilder::ForContextSlot(
PromiseBuiltinsAssembler::kPromiseSlot)),
context, effect, control);

// Check if the {promise} is still pending or already settled.
Node* check = graph()->NewNode(simplified()->ReferenceEqual(), promise,
jsgraph()->UndefinedConstant());
Node* branch =
graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);

Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* etrue = effect;

Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* efalse = effect;
{
// Mark the {promise} as settled.
efalse = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForContextSlot(
PromiseBuiltinsAssembler::kPromiseSlot)),
context, jsgraph()->UndefinedConstant(), efalse, if_false);

// Check if we should emit a debug event.
Node* debug_event = efalse =
graph()->NewNode(simplified()->LoadField(AccessBuilder::ForContextSlot(
PromiseBuiltinsAssembler::kDebugEventSlot)),
context, efalse, if_false);

// Actually reject the {promise}.
efalse =
graph()->NewNode(javascript()->RejectPromise(), promise, resolution,
debug_event, context, frame_state, efalse, if_false);
}

control = graph()->NewNode(common()->Merge(2), if_true, if_false);
effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);

Node* value = jsgraph()->UndefinedConstant();
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}

// ES section #sec-promise-resolve-functions
Reduction JSCallReducer::ReducePromiseCapabilityDefaultResolve(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
Node* target = NodeProperties::GetValueInput(node, 0);
Node* resolution = node->op()->ValueInputCount() > 2
? NodeProperties::GetValueInput(node, 2)
: jsgraph()->UndefinedConstant();
Node* frame_state = NodeProperties::GetFrameStateInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);

// We need to execute in the {target}s context.
Node* context = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSFunctionContext()), target,
effect, control);

// Grab the promise closed over by {target}.
Node* promise = effect =
graph()->NewNode(simplified()->LoadField(AccessBuilder::ForContextSlot(
PromiseBuiltinsAssembler::kPromiseSlot)),
context, effect, control);

// Check if the {promise} is still pending or already settled.
Node* check = graph()->NewNode(simplified()->ReferenceEqual(), promise,
jsgraph()->UndefinedConstant());
Node* branch =
graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);

Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* etrue = effect;

Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* efalse = effect;
{
// Mark the {promise} as settled.
efalse = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForContextSlot(
PromiseBuiltinsAssembler::kPromiseSlot)),
context, jsgraph()->UndefinedConstant(), efalse, if_false);

// Actually resolve the {promise}.
efalse =
graph()->NewNode(javascript()->ResolvePromise(), promise, resolution,
context, frame_state, efalse, if_false);
}

control = graph()->NewNode(common()->Merge(2), if_true, if_false);
effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);

Node* value = jsgraph()->UndefinedConstant();
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}

Node* JSCallReducer::CreateArtificialFrameState(
Node* node, Node* outer_frame_state, int parameter_count,
BailoutId bailout_id, FrameStateType frame_state_type,
Expand Down Expand Up @@ -5463,6 +5345,10 @@ Reduction JSCallReducer::ReducePromiseConstructor(Node* node) {
graph()->NewNode(simplified()->StoreField(AccessBuilder::ForContextSlot(
PromiseBuiltinsAssembler::kPromiseSlot)),
promise_context, promise, effect, control);
effect = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForContextSlot(
PromiseBuiltinsAssembler::kAlreadyResolvedSlot)),
promise_context, jsgraph()->FalseConstant(), effect, control);
effect = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForContextSlot(
PromiseBuiltinsAssembler::kDebugEventSlot)),
Expand Down
2 changes: 0 additions & 2 deletions deps/v8/src/compiler/js-call-reducer.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,6 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {

Reduction ReduceAsyncFunctionPromiseCreate(Node* node);
Reduction ReduceAsyncFunctionPromiseRelease(Node* node);
Reduction ReducePromiseCapabilityDefaultReject(Node* node);
Reduction ReducePromiseCapabilityDefaultResolve(Node* node);
Reduction ReducePromiseConstructor(Node* node);
Reduction ReducePromiseInternalConstructor(Node* node);
Reduction ReducePromiseInternalReject(Node* node);
Expand Down
3 changes: 1 addition & 2 deletions deps/v8/src/isolate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3875,10 +3875,9 @@ void Isolate::SetPromiseRejectCallback(PromiseRejectCallback callback) {
void Isolate::ReportPromiseReject(Handle<JSPromise> promise,
Handle<Object> value,
v8::PromiseRejectEvent event) {
DCHECK_EQ(v8::Promise::kRejected, promise->status());
if (promise_reject_callback_ == nullptr) return;
Handle<FixedArray> stack_trace;
if (event == v8::kPromiseRejectWithNoHandler && value->IsJSObject()) {
if (event != v8::kPromiseHandlerAddedAfterReject && value->IsJSObject()) {
stack_trace = GetDetailedStackTrace(Handle<JSObject>::cast(value));
}
promise_reject_callback_(v8::PromiseRejectMessage(
Expand Down
20 changes: 20 additions & 0 deletions deps/v8/src/runtime/runtime-promise.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,26 @@ RUNTIME_FUNCTION(Runtime_PromiseRejectEventFromStack) {
return isolate->heap()->undefined_value();
}

RUNTIME_FUNCTION(Runtime_PromiseRejectAfterResolved) {
DCHECK_EQ(2, args.length());
HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, reason, 1);
isolate->ReportPromiseReject(promise, reason,
v8::kPromiseRejectAfterResolved);
return isolate->heap()->undefined_value();
}

RUNTIME_FUNCTION(Runtime_PromiseResolveAfterResolved) {
DCHECK_EQ(2, args.length());
HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, resolution, 1);
isolate->ReportPromiseReject(promise, resolution,
v8::kPromiseResolveAfterResolved);
return isolate->heap()->undefined_value();
}

RUNTIME_FUNCTION(Runtime_PromiseRevokeReject) {
DCHECK_EQ(1, args.length());
HandleScope scope(isolate);
Expand Down
4 changes: 3 additions & 1 deletion deps/v8/src/runtime/runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,9 @@ namespace internal {
F(PromiseRevokeReject, 1, 1) \
F(PromiseStatus, 1, 1) \
F(RejectPromise, 3, 1) \
F(ResolvePromise, 2, 1)
F(ResolvePromise, 2, 1) \
F(PromiseRejectAfterResolved, 2, 1) \
F(PromiseResolveAfterResolved, 2, 1)

#define FOR_EACH_INTRINSIC_PROXY(F) \
F(CheckProxyGetSetTrapResult, 2, 1) \
Expand Down
Loading