Skip to content
Open
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 @@ -40,7 +40,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.21',
'v8_embedder_string': '-node.24',

##### V8 defaults for Node.js #####

Expand Down
4 changes: 2 additions & 2 deletions deps/v8/include/v8-promise.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,8 @@ using PromiseHook = void (*)(PromiseHookType type, Local<Promise> promise,
enum PromiseRejectEvent {
kPromiseRejectWithNoHandler = 0,
kPromiseHandlerAddedAfterReject = 1,
kPromiseRejectAfterResolved = 2,
kPromiseResolveAfterResolved = 3,
kDeprecatedPromiseRejectAfterResolved V8_DEPRECATED("Removed event") = 2,
kDeprecatedPromiseResolveAfterResolved V8_DEPRECATED("Removed event") = 3,
};

class PromiseRejectMessage {
Expand Down
8 changes: 3 additions & 5 deletions deps/v8/src/builtins/builtins-promise.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@ namespace internal {
class PromiseBuiltins {
public:
enum PromiseResolvingFunctionContextSlot {
// The promise which resolve/reject callbacks fulfill.
kPromiseSlot = Context::MIN_CONTEXT_SLOTS,

// Whether the callback was already invoked.
kAlreadyResolvedSlot,
// The promise which resolve/reject callbacks fulfill, or Undefined
// if already resolved.
kPromiseIfNotResolvedSlot = Context::MIN_CONTEXT_SLOTS,

// Whether to trigger a debug event or not. Used in catch
// prediction.
Expand Down
75 changes: 38 additions & 37 deletions deps/v8/src/builtins/promise-abstract-operations.tq
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,6 @@ extern transitioning runtime RejectPromise(
extern transitioning runtime PromiseRevokeReject(
implicit context: Context)(JSPromise): JSAny;

extern transitioning runtime PromiseRejectAfterResolved(
implicit context: Context)(JSPromise, JSAny): JSAny;

extern transitioning runtime PromiseResolveAfterResolved(
implicit context: Context)(JSPromise, JSAny): JSAny;

extern transitioning runtime PromiseRejectEventFromStack(
implicit context: Context)(JSPromise, JSAny): JSAny;
}
Expand Down Expand Up @@ -271,8 +265,8 @@ const kPromiseCapabilitySize:
type PromiseResolvingFunctionContext extends FunctionContext;
extern enum PromiseResolvingFunctionContextSlot extends intptr
constexpr 'PromiseBuiltins::PromiseResolvingFunctionContextSlot' {
kPromiseSlot: Slot<PromiseResolvingFunctionContext, JSPromise>,
kAlreadyResolvedSlot: Slot<PromiseResolvingFunctionContext, Boolean>,
kPromiseIfNotResolvedSlot:
Slot<PromiseResolvingFunctionContext, JSPromise|Undefined>,
kDebugEventSlot: Slot<PromiseResolvingFunctionContext, Boolean>,
kPromiseContextLength
}
Expand Down Expand Up @@ -396,25 +390,29 @@ transitioning builtin NewPromiseCapability(
transitioning javascript builtin PromiseCapabilityDefaultReject(
js-implicit context: Context, receiver: JSAny)(reason: JSAny): JSAny {
const context = %RawDownCast<PromiseResolvingFunctionContext>(context);
// 2. Let promise be F.[[Promise]].
const promise =
*ContextSlot(context, PromiseResolvingFunctionContextSlot::kPromiseSlot);

// 3. Let alreadyResolved be F.[[AlreadyResolved]].
const alreadyResolved = *ContextSlot(
context, PromiseResolvingFunctionContextSlot::kAlreadyResolvedSlot);

// 4. If alreadyResolved.[[Value]] is true, return undefined.
if (alreadyResolved == True) {
return runtime::PromiseRejectAfterResolved(promise, reason);
// 2. Let promise be promiseOrEmpty.[[Value]].
const promiseOrEmpty =
*ContextSlot(
context, PromiseResolvingFunctionContextSlot::kPromiseIfNotResolvedSlot);

// 1. If promiseOrEmpty.[[Value]] is ~empty~, return undefined.
let promise: JSPromise;
typeswitch (promiseOrEmpty) {
case (Undefined): {
return Undefined;
}
case (p: JSPromise): {
promise = p;
}
}

// 5. Set alreadyResolved.[[Value]] to true.
// 3. Set promiseOrEmpty.[[Value]] to ~empty~.
*ContextSlot(
context, PromiseResolvingFunctionContextSlot::kAlreadyResolvedSlot) =
True;
context, PromiseResolvingFunctionContextSlot::kPromiseIfNotResolvedSlot) =
Undefined;

// 6. Return RejectPromise(promise, reason).
// 4. Perform RejectPromise(promise, reason).
// 5. Return undefined.
const debugEvent = *ContextSlot(
context, PromiseResolvingFunctionContextSlot::kDebugEventSlot);
return RejectPromise(promise, reason, debugEvent);
Expand All @@ -424,23 +422,26 @@ transitioning javascript builtin PromiseCapabilityDefaultReject(
transitioning javascript builtin PromiseCapabilityDefaultResolve(
js-implicit context: Context, receiver: JSAny)(resolution: JSAny): JSAny {
const context = %RawDownCast<PromiseResolvingFunctionContext>(context);
// 2. Let promise be F.[[Promise]].
const promise: JSPromise =
*ContextSlot(context, PromiseResolvingFunctionContextSlot::kPromiseSlot);

// 3. Let alreadyResolved be F.[[AlreadyResolved]].
const alreadyResolved: Boolean = *ContextSlot(
context, PromiseResolvingFunctionContextSlot::kAlreadyResolvedSlot);

// 4. If alreadyResolved.[[Value]] is true, return undefined.
if (alreadyResolved == True) {
return runtime::PromiseResolveAfterResolved(promise, resolution);
// 2. Let promise be promiseOrEmpty.[[Value]].
const promiseOrEmpty =
*ContextSlot(
context, PromiseResolvingFunctionContextSlot::kPromiseIfNotResolvedSlot);

// 1. If promiseOrEmpty.[[Value]] is ~empty~, return undefined.
let promise: JSPromise;
typeswitch (promiseOrEmpty) {
case (Undefined): {
return Undefined;
}
case (p: JSPromise): {
promise = p;
}
}

// 5. Set alreadyResolved.[[Value]] to true.
// 3. Set promiseOrEmpty.[[Value]] to ~empty~.
*ContextSlot(
context, PromiseResolvingFunctionContextSlot::kAlreadyResolvedSlot) =
True;
context, PromiseResolvingFunctionContextSlot::kPromiseIfNotResolvedSlot) =
Undefined;

// The rest of the logic (and the catch prediction) is
// encapsulated in the dedicated ResolvePromise builtin.
Expand Down
9 changes: 3 additions & 6 deletions deps/v8/src/builtins/promise-all.tq
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,14 @@ macro CreatePromiseResolvingFunctionsContext(
nativeContext,
PromiseResolvingFunctionContextSlot::kPromiseContextLength));
InitContextSlot(
resolveContext, PromiseResolvingFunctionContextSlot::kPromiseSlot,
promise);
InitContextSlot(
resolveContext, PromiseResolvingFunctionContextSlot::kAlreadyResolvedSlot,
False);
resolveContext,
PromiseResolvingFunctionContextSlot::kPromiseIfNotResolvedSlot, promise);
InitContextSlot(
resolveContext, PromiseResolvingFunctionContextSlot::kDebugEventSlot,
debugEvent);
static_assert(
PromiseResolvingFunctionContextSlot::kPromiseContextLength ==
ContextSlot::MIN_CONTEXT_SLOTS + 3);
ContextSlot::MIN_CONTEXT_SLOTS + 2);
return resolveContext;
}

Expand Down
6 changes: 2 additions & 4 deletions deps/v8/src/compiler/js-call-reducer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2543,10 +2543,8 @@ TNode<Object> PromiseBuiltinReducerAssembler::ReducePromiseConstructor(
// Allocate a promise context for the closures below.
TNode<Context> promise_context = CreateFunctionContext(
native_context, context, PromiseBuiltins::kPromiseContextLength);
StoreContextNoCellSlot(promise_context, PromiseBuiltins::kPromiseSlot,
promise);
StoreContextNoCellSlot(promise_context, PromiseBuiltins::kAlreadyResolvedSlot,
FalseConstant());
StoreContextNoCellSlot(promise_context,
PromiseBuiltins::kPromiseIfNotResolvedSlot, promise);
StoreContextNoCellSlot(promise_context, PromiseBuiltins::kDebugEventSlot,
TrueConstant());

Expand Down
5 changes: 0 additions & 5 deletions deps/v8/src/d8/d8.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4575,11 +4575,6 @@ static void PrintMessageCallback(Local<Message> message, Local<Value> error) {

void Shell::PromiseRejectCallback(v8::PromiseRejectMessage data) {
if (options.ignore_unhandled_promises) return;
if (data.GetEvent() == v8::kPromiseRejectAfterResolved ||
data.GetEvent() == v8::kPromiseResolveAfterResolved) {
// Ignore reject/resolve after resolved.
return;
}
v8::Local<v8::Promise> promise = data.GetPromise();
v8::Isolate* isolate = v8::Isolate::GetCurrent();
PerIsolateData* isolate_data = PerIsolateData::Get(isolate);
Expand Down
9 changes: 6 additions & 3 deletions deps/v8/src/execution/isolate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1253,9 +1253,12 @@ void CaptureAsyncStackTrace(Isolate* isolate, DirectHandle<JSPromise> promise,
DirectHandle<JSFunction> function(
Cast<JSFunction>(reaction->fulfill_handler()), isolate);
DirectHandle<Context> context(function->context(), isolate);
promise = direct_handle(
Cast<JSPromise>(context->GetNoCell(PromiseBuiltins::kPromiseSlot)),
isolate);
Tagged<Object> promise_or_undefined =
context->GetNoCell(PromiseBuiltins::kPromiseIfNotResolvedSlot);
if (!TryCast(direct_handle(promise_or_undefined, isolate), &promise)) {
DCHECK(IsUndefined(promise_or_undefined));
return;
}
} else {
// We have some generic promise chain here, so try to
// continue with the chained promise on the reaction
Expand Down
20 changes: 0 additions & 20 deletions deps/v8/src/runtime/runtime-promise.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,6 @@ RUNTIME_FUNCTION(Runtime_PromiseRejectEventFromStack) {
return ReadOnlyRoots(isolate).undefined_value();
}

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

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

RUNTIME_FUNCTION(Runtime_PromiseRevokeReject) {
DCHECK_EQ(1, args.length());
HandleScope scope(isolate);
Expand Down
2 changes: 0 additions & 2 deletions deps/v8/src/runtime/runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -441,8 +441,6 @@ constexpr bool CanTriggerGC(T... properties) {
F(PromiseRevokeReject, 1, 1) \
F(RejectPromise, 3, 1) \
F(ResolvePromise, 2, 1) \
F(PromiseRejectAfterResolved, 2, 1) \
F(PromiseResolveAfterResolved, 2, 1) \
F(ConstructSuppressedError, 3, 1) \
F(ConstructAggregateErrorHelper, 4, 1) \
F(ConstructInternalAggregateErrorHelper, -1 /* <= 5*/, 1)
Expand Down
18 changes: 6 additions & 12 deletions deps/v8/test/cctest/test-api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16381,8 +16381,6 @@ TEST(ErrorLevelWarning) {
v8::PromiseRejectEvent reject_event = v8::kPromiseRejectWithNoHandler;
int promise_reject_counter = 0;
int promise_revoke_counter = 0;
int promise_reject_after_resolved_counter = 0;
int promise_resolve_after_resolved_counter = 0;
int promise_reject_msg_line_number = -1;
int promise_reject_msg_column_number = -1;
int promise_reject_line_number = -1;
Expand Down Expand Up @@ -16438,14 +16436,16 @@ void PromiseRejectCallback(v8::PromiseRejectMessage reject_message) {
CHECK(reject_message.GetValue().IsEmpty());
break;
}
case v8::kPromiseRejectAfterResolved: {
promise_reject_after_resolved_counter++;
START_ALLOW_USE_DEPRECATED();
case v8::kDeprecatedPromiseRejectAfterResolved: {
// Unreachable
break;
}
case v8::kPromiseResolveAfterResolved: {
promise_resolve_after_resolved_counter++;
case v8::kDeprecatedPromiseResolveAfterResolved: {
// Unreachable
break;
}
END_ALLOW_USE_DEPRECATED();
}
}

Expand All @@ -16468,8 +16468,6 @@ v8::Local<v8::Value> RejectValue() {
void ResetPromiseStates() {
promise_reject_counter = 0;
promise_revoke_counter = 0;
promise_reject_after_resolved_counter = 0;
promise_resolve_after_resolved_counter = 0;
promise_reject_msg_line_number = -1;
promise_reject_msg_column_number = -1;
promise_reject_line_number = -1;
Expand Down Expand Up @@ -16708,8 +16706,6 @@ TEST(PromiseRejectCallback) {
CHECK(!GetPromise("v0")->HasHandler());
CHECK_EQ(0, promise_reject_counter);
CHECK_EQ(0, promise_revoke_counter);
CHECK_EQ(1, promise_reject_after_resolved_counter);
CHECK_EQ(0, promise_resolve_after_resolved_counter);

ResetPromiseStates();

Expand All @@ -16726,8 +16722,6 @@ TEST(PromiseRejectCallback) {
CHECK(!GetPromise("y0")->HasHandler());
CHECK_EQ(1, promise_reject_counter);
CHECK_EQ(0, promise_revoke_counter);
CHECK_EQ(0, promise_reject_after_resolved_counter);
CHECK_EQ(1, promise_resolve_after_resolved_counter);

// Test stack frames.
env.isolate()->SetCaptureStackTraceForUncaughtExceptions(true);
Expand Down
6 changes: 4 additions & 2 deletions deps/v8/test/cctest/test-code-stub-assembler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3037,7 +3037,8 @@ TEST(CreatePromiseResolvingFunctionsContext) {
DirectHandle<Context> context_js = Cast<Context>(result);
CHECK_EQ(isolate->root(RootIndex::kEmptyScopeInfo), context_js->scope_info());
CHECK_EQ(*isolate->native_context(), context_js->native_context());
CHECK(IsJSPromise(context_js->GetNoCell(PromiseBuiltins::kPromiseSlot)));
CHECK(IsJSPromise(
context_js->GetNoCell(PromiseBuiltins::kPromiseIfNotResolvedSlot)));
CHECK_EQ(ReadOnlyRoots(isolate).false_value(),
context_js->GetNoCell(PromiseBuiltins::kDebugEventSlot));
}
Expand Down Expand Up @@ -3246,7 +3247,8 @@ TEST(NewPromiseCapability) {
CHECK_EQ(*isolate->native_context(), callback_context->native_context());
CHECK_EQ(PromiseBuiltins::kPromiseContextLength,
callback_context->length());
CHECK_EQ(callback_context->GetNoCell(PromiseBuiltins::kPromiseSlot),
CHECK_EQ(callback_context->GetNoCell(
PromiseBuiltins::kPromiseIfNotResolvedSlot),
result->promise());
}
}
Expand Down
5 changes: 1 addition & 4 deletions deps/v8/test/inspector/isolate-data.cc
Original file line number Diff line number Diff line change
Expand Up @@ -401,10 +401,7 @@ void InspectorIsolateData::PromiseRejectHandler(v8::PromiseRejectMessage data) {
v8_inspector::StringView(reinterpret_cast<const uint8_t*>(reason_str),
strlen(reason_str)));
return;
} else if (data.GetEvent() == v8::kPromiseRejectAfterResolved ||
data.GetEvent() == v8::kPromiseResolveAfterResolved) {
// Ignore reject/resolve after resolved, like the blink handler.
return;

}

v8::Local<v8::Value> exception = data.GetValue();
Expand Down
26 changes: 26 additions & 0 deletions deps/v8/test/mjsunit/regress/regress-crbug-42213031.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2026 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Flags: --expose-gc

const pending = new Promise(() => {});

(async function () {
let wr;

await (async function () {
const payload = { };
wr = new WeakRef(payload);
const resolved = Promise.resolve(payload);
// The pending Promise should not prevent GC of the race Promise once the race settles.
await Promise.race([pending, resolved]);
})();

await gc({ type: 'major', execution: 'async' });

assertEquals(undefined, wr.deref());
})().catch((e) => {
console.error(e);
quit(1);
});
Loading