Skip to content

Commit 950a441

Browse files
fs: remove coercion to string in writing methods
Moves DEP0162 to End-of-Life. PR-URL: #42796 Reviewed-By: Antoine du Hamel <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]>
1 parent 047cd61 commit 950a441

File tree

7 files changed

+51
-91
lines changed

7 files changed

+51
-91
lines changed

doc/api/deprecations.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -3131,6 +3131,9 @@ resources and not the actual references.
31313131

31323132
<!-- YAML
31333133
changes:
3134+
- version: REPLACEME
3135+
pr-url: https://github.com/nodejs/node/pull/42796
3136+
description: End-of-Life.
31343137
- version: v18.0.0
31353138
pr-url: https://github.com/nodejs/node/pull/42607
31363139
description: Runtime deprecation.
@@ -3141,7 +3144,7 @@ changes:
31413144
description: Documentation-only deprecation.
31423145
-->
31433146

3144-
Type: Runtime
3147+
Type: End-of-Life
31453148

31463149
Implicit coercion of objects with own `toString` property, passed as second
31473150
parameter in [`fs.write()`][], [`fs.writeFile()`][], [`fs.appendFile()`][],

doc/api/fs.md

+17-5
Original file line numberDiff line numberDiff line change
@@ -4534,6 +4534,10 @@ default with the above values.
45344534
<!-- YAML
45354535
added: v0.11.5
45364536
changes:
4537+
- version: REPLACEME
4538+
pr-url: https://github.com/nodejs/node/pull/42796
4539+
description: Passing to the `string` parameter an object with an own
4540+
`toString` function is no longer supported.
45374541
- version: v17.8.0
45384542
pr-url: https://github.com/nodejs/node/pull/42149
45394543
description: Passing to the `string` parameter an object with an own
@@ -4560,16 +4564,16 @@ changes:
45604564
-->
45614565

45624566
* `fd` {integer}
4563-
* `string` {string|Object}
4567+
* `string` {string}
45644568
* `position` {integer|null} **Default:** `null`
45654569
* `encoding` {string} **Default:** `'utf8'`
45664570
* `callback` {Function}
45674571
* `err` {Error}
45684572
* `written` {integer}
45694573
* `string` {string}
45704574

4571-
Write `string` to the file specified by `fd`. If `string` is not a string, or an
4572-
object with an own `toString` function property, then an exception is thrown.
4575+
Write `string` to the file specified by `fd`. If `string` is not a string,
4576+
an exception is thrown.
45734577

45744578
`position` refers to the offset from the beginning of the file where this data
45754579
should be written. If `typeof position !== 'number'` the data will be written at
@@ -4602,6 +4606,10 @@ details.
46024606
<!-- YAML
46034607
added: v0.1.29
46044608
changes:
4609+
- version: REPLACEME
4610+
pr-url: https://github.com/nodejs/node/pull/42796
4611+
description: Passing to the `string` parameter an object with an own
4612+
`toString` function is no longer supported.
46054613
- version: v18.0.0
46064614
pr-url: https://github.com/nodejs/node/pull/41678
46074615
description: Passing an invalid callback to the `callback` argument
@@ -4650,7 +4658,7 @@ changes:
46504658
-->
46514659

46524660
* `file` {string|Buffer|URL|integer} filename or file descriptor
4653-
* `data` {string|Buffer|TypedArray|DataView|Object}
4661+
* `data` {string|Buffer|TypedArray|DataView}
46544662
* `options` {Object|string}
46554663
* `encoding` {string|null} **Default:** `'utf8'`
46564664
* `mode` {integer} **Default:** `0o666`
@@ -5840,6 +5848,10 @@ this API: [`fs.utimes()`][].
58405848
<!-- YAML
58415849
added: v0.1.29
58425850
changes:
5851+
- version: REPLACEME
5852+
pr-url: https://github.com/nodejs/node/pull/42796
5853+
description: Passing to the `data` parameter an object with an own
5854+
`toString` function is no longer supported.
58435855
- version: v17.8.0
58445856
pr-url: https://github.com/nodejs/node/pull/42149
58455857
description: Passing to the `data` parameter an object with an own
@@ -5865,7 +5877,7 @@ changes:
58655877
-->
58665878

58675879
* `file` {string|Buffer|URL|integer} filename or file descriptor
5868-
* `data` {string|Buffer|TypedArray|DataView|Object}
5880+
* `data` {string|Buffer|TypedArray|DataView}
58695881
* `options` {Object|string}
58705882
* `encoding` {string|null} **Default:** `'utf8'`
58715883
* `mode` {integer} **Default:** `0o666`

lib/fs.js

+7-24
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ const {
3939
ReflectApply,
4040
SafeMap,
4141
SafeSet,
42-
String,
4342
StringPrototypeCharCodeAt,
4443
StringPrototypeIndexOf,
4544
StringPrototypeSlice,
@@ -84,7 +83,6 @@ const { FSReqCallback } = binding;
8483
const { toPathIfFileURL } = require('internal/url');
8584
const {
8685
customPromisifyArgs: kCustomPromisifyArgsSymbol,
87-
deprecate,
8886
kEmptyObject,
8987
promisify: {
9088
custom: kCustomPromisifiedSymbol,
@@ -123,7 +121,6 @@ const {
123121
validateRmOptionsSync,
124122
validateRmdirOptions,
125123
validateStringAfterArrayBufferView,
126-
validatePrimitiveStringAfterArrayBufferView,
127124
warnOnNonPortableTemplate
128125
} = require('internal/fs/utils');
129126
const {
@@ -171,11 +168,6 @@ const isWindows = process.platform === 'win32';
171168
const isOSX = process.platform === 'darwin';
172169

173170

174-
const showStringCoercionDeprecation = deprecate(
175-
() => {},
176-
'Implicit coercion of objects with own toString property is deprecated.',
177-
'DEP0162'
178-
);
179171
function showTruncateDeprecation() {
180172
if (truncateWarn) {
181173
process.emitWarning(
@@ -813,7 +805,7 @@ function readvSync(fd, buffers, position) {
813805
/**
814806
* Writes `buffer` to the specified `fd` (file descriptor).
815807
* @param {number} fd
816-
* @param {Buffer | TypedArray | DataView | string | object} buffer
808+
* @param {Buffer | TypedArray | DataView | string} buffer
817809
* @param {number | object} [offsetOrOptions]
818810
* @param {number} [length]
819811
* @param {number | null} [position]
@@ -861,9 +853,6 @@ function write(fd, buffer, offsetOrOptions, length, position, callback) {
861853
}
862854

863855
validateStringAfterArrayBufferView(buffer, 'buffer');
864-
if (typeof buffer !== 'string') {
865-
showStringCoercionDeprecation();
866-
}
867856

868857
if (typeof position !== 'function') {
869858
if (typeof offset === 'function') {
@@ -875,7 +864,7 @@ function write(fd, buffer, offsetOrOptions, length, position, callback) {
875864
length = 'utf8';
876865
}
877866

878-
const str = String(buffer);
867+
const str = buffer;
879868
validateEncoding(str, length);
880869
callback = maybeCallback(position);
881870

@@ -926,7 +915,7 @@ function writeSync(fd, buffer, offsetOrOptions, length, position) {
926915
result = binding.writeBuffer(fd, buffer, offset, length, position,
927916
undefined, ctx);
928917
} else {
929-
validatePrimitiveStringAfterArrayBufferView(buffer, 'buffer');
918+
validateStringAfterArrayBufferView(buffer, 'buffer');
930919
validateEncoding(buffer, length);
931920

932921
if (offset === undefined)
@@ -2154,7 +2143,7 @@ function writeAll(fd, isUserFd, buffer, offset, length, signal, callback) {
21542143
/**
21552144
* Asynchronously writes data to the file.
21562145
* @param {string | Buffer | URL | number} path
2157-
* @param {string | Buffer | TypedArray | DataView | object} data
2146+
* @param {string | Buffer | TypedArray | DataView} data
21582147
* @param {{
21592148
* encoding?: string | null;
21602149
* mode?: number;
@@ -2171,10 +2160,7 @@ function writeFile(path, data, options, callback) {
21712160

21722161
if (!isArrayBufferView(data)) {
21732162
validateStringAfterArrayBufferView(data, 'data');
2174-
if (typeof data !== 'string') {
2175-
showStringCoercionDeprecation();
2176-
}
2177-
data = Buffer.from(String(data), options.encoding || 'utf8');
2163+
data = Buffer.from(data, options.encoding || 'utf8');
21782164
}
21792165

21802166
if (isFd(path)) {
@@ -2201,7 +2187,7 @@ function writeFile(path, data, options, callback) {
22012187
/**
22022188
* Synchronously writes data to the file.
22032189
* @param {string | Buffer | URL | number} path
2204-
* @param {string | Buffer | TypedArray | DataView | object} data
2190+
* @param {string | Buffer | TypedArray | DataView} data
22052191
* @param {{
22062192
* encoding?: string | null;
22072193
* mode?: number;
@@ -2214,10 +2200,7 @@ function writeFileSync(path, data, options) {
22142200

22152201
if (!isArrayBufferView(data)) {
22162202
validateStringAfterArrayBufferView(data, 'data');
2217-
if (typeof data !== 'string') {
2218-
showStringCoercionDeprecation();
2219-
}
2220-
data = Buffer.from(String(data), options.encoding || 'utf8');
2203+
data = Buffer.from(data, options.encoding || 'utf8');
22212204
}
22222205

22232206
const flag = options.flag || 'w';

lib/internal/fs/promises.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ const {
6666
validateOffsetLengthWrite,
6767
validateRmOptions,
6868
validateRmdirOptions,
69-
validatePrimitiveStringAfterArrayBufferView,
69+
validateStringAfterArrayBufferView,
7070
warnOnNonPortableTemplate,
7171
} = require('internal/fs/utils');
7272
const { opendir } = require('internal/fs/dir');
@@ -608,7 +608,7 @@ async function write(handle, buffer, offsetOrOptions, length, position) {
608608
return { bytesWritten, buffer };
609609
}
610610

611-
validatePrimitiveStringAfterArrayBufferView(buffer, 'buffer');
611+
validateStringAfterArrayBufferView(buffer, 'buffer');
612612
validateEncoding(buffer, length);
613613
const bytesWritten = (await binding.writeString(handle.fd, buffer, offset,
614614
length, kUsePromises)) || 0;
@@ -838,7 +838,7 @@ async function writeFile(path, data, options) {
838838
const flag = options.flag || 'w';
839839

840840
if (!isArrayBufferView(data) && !isCustomIterable(data)) {
841-
validatePrimitiveStringAfterArrayBufferView(data, 'data');
841+
validateStringAfterArrayBufferView(data, 'data');
842842
data = Buffer.from(data, options.encoding || 'utf8');
843843
}
844844

lib/internal/fs/utils.js

-23
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ const {
1414
MathMin,
1515
MathRound,
1616
ObjectIs,
17-
ObjectPrototypeHasOwnProperty,
1817
ObjectSetPrototypeOf,
1918
ReflectApply,
2019
ReflectOwnKeys,
@@ -874,27 +873,6 @@ const getValidMode = hideStackFrames((mode, type) => {
874873
});
875874

876875
const validateStringAfterArrayBufferView = hideStackFrames((buffer, name) => {
877-
if (typeof buffer === 'string') {
878-
return;
879-
}
880-
881-
if (
882-
typeof buffer === 'object' &&
883-
buffer !== null &&
884-
typeof buffer.toString === 'function' &&
885-
ObjectPrototypeHasOwnProperty(buffer, 'toString')
886-
) {
887-
return;
888-
}
889-
890-
throw new ERR_INVALID_ARG_TYPE(
891-
name,
892-
['string', 'Buffer', 'TypedArray', 'DataView'],
893-
buffer
894-
);
895-
});
896-
897-
const validatePrimitiveStringAfterArrayBufferView = hideStackFrames((buffer, name) => {
898876
if (typeof buffer !== 'string') {
899877
throw new ERR_INVALID_ARG_TYPE(
900878
name,
@@ -958,6 +936,5 @@ module.exports = {
958936
validateRmOptionsSync,
959937
validateRmdirOptions,
960938
validateStringAfterArrayBufferView,
961-
validatePrimitiveStringAfterArrayBufferView,
962939
warnOnNonPortableTemplate
963940
};

test/parallel/test-fs-write-file-sync.js

+15-15
Original file line numberDiff line numberDiff line change
@@ -102,20 +102,20 @@ tmpdir.refresh();
102102
assert.strictEqual(content, 'hello world!');
103103
}
104104

105-
// Test writeFileSync with an object with an own toString function
105+
// Test writeFileSync with an invalid input
106106
{
107-
// Runtime deprecated by DEP0162
108-
common.expectWarning('DeprecationWarning',
109-
'Implicit coercion of objects with own toString property is deprecated.',
110-
'DEP0162');
111-
const file = path.join(tmpdir.path, 'testWriteFileSyncStringify.txt');
112-
const data = {
113-
toString() {
114-
return 'hello world!';
115-
}
116-
};
117-
118-
fs.writeFileSync(file, data, { encoding: 'utf8', flag: 'a' });
119-
const content = fs.readFileSync(file, { encoding: 'utf8' });
120-
assert.strictEqual(content, String(data));
107+
const file = path.join(tmpdir.path, 'testWriteFileSyncInvalid.txt');
108+
for (const data of [
109+
false, 5, {}, [], null, undefined, true, 5n, () => {}, Symbol(), new Map(),
110+
new String('notPrimitive'),
111+
{ [Symbol.toPrimitive]: (hint) => 'amObject' },
112+
{ toString() { return 'amObject'; } },
113+
Promise.resolve('amPromise'),
114+
common.mustNotCall(),
115+
]) {
116+
assert.throws(
117+
() => fs.writeFileSync(file, data, { encoding: 'utf8', flag: 'a' }),
118+
{ code: 'ERR_INVALID_ARG_TYPE' }
119+
);
120+
}
121121
}

test/parallel/test-fs-write.js

+5-20
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ const fn = path.join(tmpdir.path, 'write.txt');
3333
const fn2 = path.join(tmpdir.path, 'write2.txt');
3434
const fn3 = path.join(tmpdir.path, 'write3.txt');
3535
const fn4 = path.join(tmpdir.path, 'write4.txt');
36-
const fn5 = path.join(tmpdir.path, 'write5.txt');
3736
const expected = 'ümlaut.';
3837
const constants = fs.constants;
3938

@@ -127,23 +126,6 @@ fs.open(fn3, 'w', 0o644, common.mustSucceed((fd) => {
127126
}));
128127

129128

130-
// Test write with an object with an own toString function
131-
// Runtime deprecated by DEP0162
132-
common.expectWarning('DeprecationWarning',
133-
'Implicit coercion of objects with own toString property is deprecated.',
134-
'DEP0162');
135-
fs.open(fn4, 'w', 0o644, common.mustSucceed((fd) => {
136-
const done = common.mustSucceed((written) => {
137-
assert.strictEqual(written, Buffer.byteLength(expected));
138-
fs.closeSync(fd);
139-
});
140-
141-
const data = {
142-
toString() { return expected; }
143-
};
144-
fs.write(fd, data, done);
145-
}));
146-
147129
[false, 'test', {}, [], null, undefined].forEach((i) => {
148130
assert.throws(
149131
() => fs.write(i, common.mustNotCall()),
@@ -162,9 +144,12 @@ fs.open(fn4, 'w', 0o644, common.mustSucceed((fd) => {
162144
});
163145

164146
[
165-
false, 5, {}, [], null, undefined,
147+
false, 5, {}, [], null, undefined, true, 5n, () => {}, Symbol(), new Map(),
166148
new String('notPrimitive'),
167149
{ [Symbol.toPrimitive]: (hint) => 'amObject' },
150+
{ toString() { return 'amObject'; } },
151+
Promise.resolve('amPromise'),
152+
common.mustNotCall(),
168153
].forEach((data) => {
169154
assert.throws(
170155
() => fs.write(1, data, common.mustNotCall()),
@@ -184,7 +169,7 @@ fs.open(fn4, 'w', 0o644, common.mustSucceed((fd) => {
184169

185170
{
186171
// Regression test for https://github.com/nodejs/node/issues/38168
187-
const fd = fs.openSync(fn5, 'w');
172+
const fd = fs.openSync(fn4, 'w');
188173

189174
assert.throws(
190175
() => fs.writeSync(fd, 'abc', 0, 'hex'),

0 commit comments

Comments
 (0)