From f1c3482e7201b6999ffc472f8e9dbeb06fb945da Mon Sep 17 00:00:00 2001 From: Claude Bot Date: Thu, 19 Feb 2026 10:12:10 +0000 Subject: [PATCH] Fix getAsyncDisposableMethod to fall back to @@dispose per TC39 spec The getAsyncDisposableMethod builtin was checking !isCallable(method) before checking isUndefinedOrNull(method), causing a TypeError when @@asyncDispose was undefined instead of falling back to @@dispose. Per the TC39 spec (GetDisposeMethod with hint async-dispose): 1. Check for @@asyncDispose first 2. If undefined/null, fall back to @@dispose and wrap in Promise adapter 3. Only throw if neither is available Also fixes AsyncDisposableStackPrototype.use() which was incorrectly using @getDisposableStackInternalField instead of @getAsyncDisposableStackInternalField. Fixes oven-sh/bun#24277 Co-Authored-By: Claude --- .../builtins/AsyncDisposableStackPrototype.js | 2 +- .../builtins/DisposableStackPrototype.js | 27 ++++++++++++++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/Source/JavaScriptCore/builtins/AsyncDisposableStackPrototype.js b/Source/JavaScriptCore/builtins/AsyncDisposableStackPrototype.js index 80619cb01903..15658203de23 100644 --- a/Source/JavaScriptCore/builtins/AsyncDisposableStackPrototype.js +++ b/Source/JavaScriptCore/builtins/AsyncDisposableStackPrototype.js @@ -153,7 +153,7 @@ function use(value) if (@getAsyncDisposableStackInternalField(this, @asyncDisposableStackFieldState) === @AsyncDisposableStackStateDisposed) throw new @ReferenceError("AsyncDisposableStack.prototype.use requires that |this| be a pending AsyncDisposableStack object"); - @addDisposableResource(@getDisposableStackInternalField(this, @disposableStackFieldCapability), value, /* isAsync */ true); + @addDisposableResource(@getAsyncDisposableStackInternalField(this, @asyncDisposableStackFieldCapability), value, /* isAsync */ true); return value; } diff --git a/Source/JavaScriptCore/builtins/DisposableStackPrototype.js b/Source/JavaScriptCore/builtins/DisposableStackPrototype.js index da3e11a39ff0..21ab82c4de62 100644 --- a/Source/JavaScriptCore/builtins/DisposableStackPrototype.js +++ b/Source/JavaScriptCore/builtins/DisposableStackPrototype.js @@ -31,12 +31,31 @@ function getAsyncDisposableMethod(value) var method = value.@@asyncDispose; + if (@isUndefinedOrNull(method)) { + // Per spec, fall back to @@dispose when @@asyncDispose is not present. + method = value.@@dispose; + + if (@isUndefinedOrNull(method)) + return @undefined; + + if (!@isCallable(method)) + @throwTypeError("@@dispose must be callable"); + + // Wrap the sync dispose method to return a Promise. + var syncMethod = method; + return () => { + try { + syncMethod.@call(value); + } catch (e) { + return @promiseReject(@Promise, e); + } + return @promiseResolve(@Promise, @undefined); + }; + } + if (!@isCallable(method)) @throwTypeError("@@asyncDispose must be callable"); - if (@isUndefinedOrNull(method)) - return @undefined; - return () => { try { method.@call(value); @@ -63,7 +82,7 @@ function createDisposableResource(value, isAsync /* , method */) if (isAsync) { method = @getAsyncDisposableMethod(value); if (method === @undefined) - @throwTypeError("@@asyncDispose must not be an undefined"); + @throwTypeError("Either @@asyncDispose or @@dispose is required"); } else { method = value.@@dispose; if (!@isCallable(method))