From e0fc87f6d1e48ddd6b38777bcd44f61d5f91d9ae Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Mon, 13 Jul 2026 10:47:05 +0200 Subject: [PATCH] stream: move `rejectedHandledRecord` to native Signed-off-by: Antoine du Hamel --- lib/internal/webstreams/util.js | 8 +++----- src/node_util.cc | 25 +++++++++++++++++++++++++ typings/internalBinding/util.d.ts | 1 + 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/lib/internal/webstreams/util.js b/lib/internal/webstreams/util.js index a27c61ed5e5db7..7092f35503316e 100644 --- a/lib/internal/webstreams/util.js +++ b/lib/internal/webstreams/util.js @@ -14,7 +14,6 @@ const { NumberIsNaN, ObjectFreeze, PromisePrototypeThen, - PromiseReject, PromiseResolve, Symbol, TypedArrayPrototypeGetBuffer, @@ -41,6 +40,7 @@ const { constants: { kPending, }, + createRejectedHandledPromise, getPromiseDetails, } = internalBinding('util'); @@ -365,13 +365,11 @@ function resolvedRecord() { } function rejectedHandledRecord(error) { - const record = { - promise: PromiseReject(error), + return { + promise: createRejectedHandledPromise(error), resolve: undefined, reject: undefined, }; - setPromiseHandled(record.promise); - return record; } function setPromiseHandled(promise) { diff --git a/src/node_util.cc b/src/node_util.cc index 6d3373caae6c5c..c8073d0d18d4f5 100644 --- a/src/node_util.cc +++ b/src/node_util.cc @@ -469,6 +469,26 @@ void MarkPromiseAsHandled(const FunctionCallbackInfo& args) { promise->MarkAsSilent(); } +// Returns a promise rejected with the given reason, already marked as handled +// and silent so it does not trigger unhandled rejection tracking. This avoids +// the extra boundary crossing of rejecting the promise in JS and then marking +// it as handled through a separate call. +void CreateRejectedHandledPromise(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + Local resolver; + if (!Promise::Resolver::New(context).ToLocal(&resolver)) { + return; + } + Local promise = resolver->GetPromise(); + promise->MarkAsHandled(); + promise->MarkAsSilent(); + if (resolver->Reject(context, args[0]).IsNothing()) { + return; + } + args.GetReturnValue().Set(promise); +} + void RegisterExternalReferences(ExternalReferenceRegistry* registry) { registry->Register(GetPromiseDetails); registry->Register(GetProxyDetails); @@ -488,6 +508,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) { registry->Register(DefineLazyPropertiesGetter); registry->Register(ConstructSharedArrayBuffer); registry->Register(MarkPromiseAsHandled); + registry->Register(CreateRejectedHandledPromise); } void Initialize(Local target, @@ -594,6 +615,10 @@ void Initialize(Local target, "constructSharedArrayBuffer", ConstructSharedArrayBuffer); SetMethod(context, target, "markPromiseAsHandled", MarkPromiseAsHandled); + SetMethod(context, + target, + "createRejectedHandledPromise", + CreateRejectedHandledPromise); Local should_abort_on_uncaught_toggle = FIXED_ONE_BYTE_STRING(env->isolate(), "shouldAbortOnUncaughtToggle"); diff --git a/typings/internalBinding/util.d.ts b/typings/internalBinding/util.d.ts index a3026b5a0305e5..72e6d6bf5e24e1 100644 --- a/typings/internalBinding/util.d.ts +++ b/typings/internalBinding/util.d.ts @@ -24,6 +24,7 @@ export interface UtilBinding { getHiddenValue(object: object, index: number): any; setHiddenValue(object: object, index: number, value: any): boolean; getPromiseDetails(promise: any): undefined | [state: 0] | [state: 1 | 2, result: any]; + createRejectedHandledPromise(reason: any): Promise; getProxyDetails(proxy: any, fullProxy?: boolean): undefined | any | [target: any, handler: any]; previewEntries(object: object, slowPath?: boolean): undefined | any[] | [entries: any[], isKeyValue: boolean]; getOwnNonIndexProperties(object: object, filter: number): Array;