Repository: nodejs/node
Pull request: fs: throw `ERR_INVALID_THIS` on illegal invocations

This PR makes `Dir.prototype.path`, `ReadStream.prototype.autoClose`, and `WriteStream.prototype.autoClose` getters and setters throw `ERR_INVALID_THIS` instead of failing with internal implementation-specific messages when called on invalid `this`.

Changed files:
filename: lib/internal/fs/dir.js
patch: @@ -18,6 +18,7 @@ const {
   codes: {
     ERR_DIR_CLOSED,
     ERR_DIR_CONCURRENT_OPERATION,
+    ERR_INVALID_THIS,
     ERR_MISSING_ARGS,
   },
 } = require('internal/errors');
@@ -67,6 +68,8 @@ class Dir {
   }
 
   get path() {
+    if (!(#path in this))
+      throw new ERR_INVALID_THIS('Dir');
     return this.#path;
   }
 

filename: lib/internal/fs/streams.js
patch: @@ -25,6 +25,7 @@ const {
   validateBoolean,
   validateFunction,
   validateInteger,
+  validateThisInternalField,
 } = require('internal/validators');
 const { errorOrDestroy } = require('internal/streams/destroy');
 const fs = require('fs');
@@ -230,9 +231,11 @@ ObjectSetPrototypeOf(ReadStream, Readable);
 ObjectDefineProperty(ReadStream.prototype, 'autoClose', {
   __proto__: null,
   get() {
+    validateThisInternalField(this, kFs, 'ReadStream');
     return this._readableState.autoDestroy;
   },
   set(val) {
+    validateThisInternalField(this, kFs, 'ReadStream');
     this._readableState.autoDestroy = val;
   },
 });
@@ -394,9 +397,11 @@ ObjectSetPrototypeOf(WriteStream, Writable);
 ObjectDefineProperty(WriteStream.prototype, 'autoClose', {
   __proto__: null,
   get() {
+    validateThisInternalField(this, kFs, 'WriteStream');
     return this._writableState.autoDestroy;
   },
   set(val) {
+    validateThisInternalField(this, kFs, 'WriteStream');
     this._writableState.autoDestroy = val;
   },
 });

filename: test/parallel/test-fs-opendir.js
patch: @@ -18,6 +18,13 @@ files.forEach(function(filename) {
   fs.closeSync(fs.openSync(path.join(testDir, filename), 'w'));
 });
 
+function assertDir(dir) {
+  assert(dir instanceof fs.Dir);
+  assert.throws(() => dir.constructor.prototype.path, {
+    code: 'ERR_INVALID_THIS',
+  });
+}
+
 function assertDirent(dirent) {
   assert(dirent instanceof fs.Dirent);
   assert.strictEqual(dirent.isFile(), true);
@@ -45,6 +52,7 @@ const invalidCallbackObj = {
 // Check the opendir Sync version
 {
   const dir = fs.opendirSync(testDir);
+  assertDir(dir);
   const entries = files.map(() => {
     const dirent = dir.readSync();
     assertDirent(dirent);
@@ -67,6 +75,7 @@ const invalidCallbackObj = {
 
 // Check the opendir async version
 fs.opendir(testDir, common.mustSucceed((dir) => {
+  assertDir(dir);
   let sync = true;
   dir.read(common.mustSucceed((dirent) => {
     assert(!sync);
@@ -120,6 +129,7 @@ fs.opendir(__filename, common.mustCall(function(e) {
 async function doPromiseTest() {
   // Check the opendir Promise version
   const dir = await fs.promises.opendir(testDir);
+  assertDir(dir);
   const entries = [];
 
   let i = files.length;

filename: test/parallel/test-fs-write-stream-autoclose-option.js
patch: @@ -56,3 +56,7 @@ function next3() {
     }));
   }));
 }
+
+assert.throws(() => fs.WriteStream.prototype.autoClose, {
+  code: 'ERR_INVALID_THIS',
+});