fs: make Dir disposers idempotent

PR-URL: https://github.com/nodejs/node/pull/58692
Refs: https://github.com/nodejs/node/pull/58206
Reviewed-By: Ethan Arrowood <ethan@arrowood.dev>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
This commit is contained in:
René 2025-06-20 20:03:34 +01:00 committed by GitHub
parent 88f4cef8b9
commit ce546e468a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 21 additions and 24 deletions

View File

@ -6773,8 +6773,8 @@ changes:
description: No longer experimental. description: No longer experimental.
--> -->
Calls `dir.close()` and returns a promise that fulfills when the Calls `dir.close()` if the directory handle is open, and returns a promise that
dir is closed. fulfills when disposal is complete.
#### `dir[Symbol.dispose]()` #### `dir[Symbol.dispose]()`
@ -6786,7 +6786,8 @@ changes:
description: No longer experimental. description: No longer experimental.
--> -->
Calls `dir.closeSync()` and returns `undefined`. Calls `dir.closeSync()` if the directory handle is open, and returns
`undefined`.
### Class: `fs.Dirent` ### Class: `fs.Dirent`

View File

@ -24,7 +24,6 @@ const {
const { FSReqCallback } = binding; const { FSReqCallback } = binding;
const { const {
assignFunctionName,
promisify, promisify,
} = require('internal/util'); } = require('internal/util');
const { const {
@ -296,31 +295,24 @@ class Dir {
await this.#closePromisified(); await this.#closePromisified();
} }
} }
[SymbolDispose]() {
if (this.#closed) return;
this.closeSync();
}
async [SymbolAsyncDispose]() {
if (this.#closed) return;
await this.#closePromisified();
}
} }
const nonEnumerableDescriptor = {
enumerable: false,
writable: true,
configurable: true,
};
ObjectDefineProperties(Dir.prototype, { ObjectDefineProperties(Dir.prototype, {
[SymbolDispose]: {
__proto__: null,
...nonEnumerableDescriptor,
value: assignFunctionName(SymbolDispose, function() {
this.closeSync();
}),
},
[SymbolAsyncDispose]: {
__proto__: null,
...nonEnumerableDescriptor,
value: assignFunctionName(SymbolAsyncDispose, function() {
this.close();
}),
},
[SymbolAsyncIterator]: { [SymbolAsyncIterator]: {
__proto__: null, __proto__: null,
...nonEnumerableDescriptor, enumerable: false,
writable: true,
configurable: true,
value: Dir.prototype.entries, value: Dir.prototype.entries,
}, },
}); });

View File

@ -11,10 +11,14 @@ async function explicitCall() {
const dh = await fs.opendir(__dirname); const dh = await fs.opendir(__dirname);
await dh[Symbol.asyncDispose](); await dh[Symbol.asyncDispose]();
// Repeat invocations should not reject
await dh[Symbol.asyncDispose]();
await assert.rejects(dh.read(), { code: 'ERR_DIR_CLOSED' }); await assert.rejects(dh.read(), { code: 'ERR_DIR_CLOSED' });
const dhSync = opendirSync(__dirname); const dhSync = opendirSync(__dirname);
dhSync[Symbol.dispose](); dhSync[Symbol.dispose]();
// Repeat invocations should not throw
dhSync[Symbol.dispose]();
assert.throws(() => dhSync.readSync(), { code: 'ERR_DIR_CLOSED' }); assert.throws(() => dhSync.readSync(), { code: 'ERR_DIR_CLOSED' });
} }