Skip to content

Commit 4af1bba

Browse files
committed
util: implement util.getSystemErrorName()
Reimplement uv.errname() as internal/util.getSystemErrorName() to avoid the memory leaks caused by unknown error codes and avoid calling into C++ for the error names. Also expose it as a public API for external use. PR-URL: #18186 Refs: http://docs.libuv.org/en/v1.x/errors.html#c.uv_err_name Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]>
1 parent cbd6349 commit 4af1bba

File tree

9 files changed

+87
-43
lines changed

9 files changed

+87
-43
lines changed

doc/api/util.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,25 @@ intended as a debugging tool. Some input values can have a significant
257257
performance overhead that can block the event loop. Use this function
258258
with care and never in a hot code path.
259259

260+
## util.getSystemErrorName(err)
261+
<!-- YAML
262+
added: REPLACEME
263+
-->
264+
265+
* `err` {number}
266+
* Returns: {string}
267+
268+
Returns the string name for a numeric error code that comes from a Node.js API.
269+
The mapping between error codes and error names is platform-dependent.
270+
See [Common System Errors][] for the names of common errors.
271+
272+
```js
273+
fs.access('file/that/does/not/exist', (err) => {
274+
const name = util.getSystemErrorName(err.errno);
275+
console.error(name); // ENOENT
276+
});
277+
```
278+
260279
## util.inherits(constructor, superConstructor)
261280
<!-- YAML
262281
added: v0.3.0
@@ -1362,6 +1381,7 @@ Deprecated predecessor of `console.log`.
13621381
[Customizing `util.inspect` colors]: #util_customizing_util_inspect_colors
13631382
[Internationalization]: intl.html
13641383
[WHATWG Encoding Standard]: https://encoding.spec.whatwg.org/
1384+
[Common System Errors]: errors.html#errors_common_system_errors
13651385
[constructor]: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/constructor
13661386
[list of deprecated APIS]: deprecations.html#deprecations_list_of_deprecated_apis
13671387
[semantically incompatible]: https://github.com/nodejs/node/issues/4179

lib/child_process.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,16 @@
2222
'use strict';
2323

2424
const util = require('util');
25-
const { deprecate, convertToValidSignal } = require('internal/util');
25+
const {
26+
deprecate, convertToValidSignal, getSystemErrorName
27+
} = require('internal/util');
2628
const { isUint8Array } = require('internal/util/types');
2729
const { createPromise,
2830
promiseResolve, promiseReject } = process.binding('util');
2931
const debug = util.debuglog('child_process');
3032
const { Buffer } = require('buffer');
3133
const { Pipe, constants: PipeConstants } = process.binding('pipe_wrap');
3234
const errors = require('internal/errors');
33-
const { errname } = process.binding('uv');
3435
const child_process = require('internal/child_process');
3536
const {
3637
_validateStdio,
@@ -275,7 +276,7 @@ exports.execFile = function(file /*, args, options, callback*/) {
275276
if (!ex) {
276277
ex = new Error('Command failed: ' + cmd + '\n' + stderr);
277278
ex.killed = child.killed || killed;
278-
ex.code = code < 0 ? errname(code) : code;
279+
ex.code = code < 0 ? getSystemErrorName(code) : code;
279280
ex.signal = signal;
280281
}
281282

lib/internal/util.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const {
1212
arrow_message_private_symbol: kArrowMessagePrivateSymbolIndex,
1313
decorated_private_symbol: kDecoratedPrivateSymbolIndex
1414
} = process.binding('util');
15+
const { errmap } = process.binding('uv');
1516

1617
const noCrypto = !process.versions.openssl;
1718

@@ -213,6 +214,19 @@ function getConstructorOf(obj) {
213214
return null;
214215
}
215216

217+
function getSystemErrorName(err) {
218+
if (typeof err !== 'number') {
219+
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'err', 'number', err);
220+
}
221+
if (err >= 0 || !Number.isSafeInteger(err)) {
222+
throw new errors.RangeError('ERR_OUT_OF_RANGE', 'err',
223+
'a negative integer', err);
224+
}
225+
226+
const entry = errmap.get(err);
227+
return entry ? entry[0] : `Unknown system error ${err}`;
228+
}
229+
216230
// getConstructorOf is wrapped into this to save iterations
217231
function getIdentificationOf(obj) {
218232
const original = obj;
@@ -340,6 +354,7 @@ module.exports = {
340354
emitExperimentalWarning,
341355
filterDuplicateStrings,
342356
getConstructorOf,
357+
getSystemErrorName,
343358
getIdentificationOf,
344359
isError,
345360
join,

lib/util.js

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ const errors = require('internal/errors');
2525
const { TextDecoder, TextEncoder } = require('internal/encoding');
2626
const { isBuffer } = require('buffer').Buffer;
2727

28-
const { errname } = process.binding('uv');
2928
const { previewMapIterator, previewSetIterator } = require('internal/v8');
3029

3130
const {
@@ -56,6 +55,7 @@ const {
5655
const {
5756
customInspectSymbol,
5857
deprecate,
58+
getSystemErrorName,
5959
getIdentificationOf,
6060
isError,
6161
promisify,
@@ -1055,14 +1055,7 @@ function error(...args) {
10551055
}
10561056

10571057
function _errnoException(err, syscall, original) {
1058-
if (typeof err !== 'number') {
1059-
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'err', 'number', err);
1060-
}
1061-
if (err >= 0 || !Number.isSafeInteger(err)) {
1062-
throw new errors.RangeError('ERR_OUT_OF_RANGE', 'err',
1063-
'a negative integer', err);
1064-
}
1065-
const name = errname(err);
1058+
const name = getSystemErrorName(err);
10661059
var message = `${syscall} ${name}`;
10671060
if (original)
10681061
message += ` ${original}`;
@@ -1151,6 +1144,7 @@ module.exports = exports = {
11511144
debuglog,
11521145
deprecate,
11531146
format,
1147+
getSystemErrorName,
11541148
inherits,
11551149
inspect,
11561150
isArray: Array.isArray,

src/uv.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ using v8::String;
3939
using v8::Value;
4040

4141

42+
// TODO(joyeecheung): deprecate this function in favor of
43+
// lib/util.getSystemErrorName()
4244
void ErrName(const FunctionCallbackInfo<Value>& args) {
4345
Environment* env = Environment::GetCurrent(args);
4446
int err = args[0]->Int32Value();

test/parallel/test-child-process-execfile.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
'use strict';
2+
23
const common = require('../common');
34
const assert = require('assert');
45
const execFile = require('child_process').execFile;
5-
const uv = process.binding('uv');
6+
const { getSystemErrorName } = require('util');
67
const fixtures = require('../common/fixtures');
78

89
const fixture = fixtures.path('exit.js');
@@ -26,7 +27,7 @@ const fixture = fixtures.path('exit.js');
2627
const code = -1;
2728
const callback = common.mustCall((err, stdout, stderr) => {
2829
assert.strictEqual(err.toString().trim(), errorString);
29-
assert.strictEqual(err.code, uv.errname(code));
30+
assert.strictEqual(err.code, getSystemErrorName(code));
3031
assert.strictEqual(err.killed, true);
3132
assert.strictEqual(err.signal, null);
3233
assert.strictEqual(err.cmd, process.execPath);

test/parallel/test-net-server-listen-handle.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const common = require('../common');
44
const assert = require('assert');
55
const net = require('net');
66
const fs = require('fs');
7-
const uv = process.binding('uv');
7+
const { getSystemErrorName } = require('util');
88
const { TCP, constants: TCPConstants } = process.binding('tcp_wrap');
99
const { Pipe, constants: PipeConstants } = process.binding('pipe_wrap');
1010

@@ -46,9 +46,10 @@ function randomHandle(type) {
4646
handleName = `pipe ${path}`;
4747
}
4848

49-
if (errno < 0) { // uv.errname requires err < 0
50-
assert(errno >= 0, `unable to bind ${handleName}: ${uv.errname(errno)}`);
49+
if (errno < 0) {
50+
assert.fail(`unable to bind ${handleName}: ${getSystemErrorName(errno)}`);
5151
}
52+
5253
if (!common.isWindows) { // fd doesn't work on windows
5354
// err >= 0 but fd = -1, should not happen
5455
assert.notStrictEqual(handle.fd, -1,

test/parallel/test-uv-errno.js

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,52 @@
22

33
const common = require('../common');
44
const assert = require('assert');
5-
const util = require('util');
6-
const uv = process.binding('uv');
5+
const {
6+
getSystemErrorName,
7+
_errnoException
8+
} = require('util');
79

10+
const uv = process.binding('uv');
811
const keys = Object.keys(uv);
912

1013
keys.forEach((key) => {
1114
if (!key.startsWith('UV_'))
1215
return;
1316

1417
assert.doesNotThrow(() => {
15-
const err = util._errnoException(uv[key], 'test');
18+
const err = _errnoException(uv[key], 'test');
1619
const name = uv.errname(uv[key]);
17-
assert.strictEqual(err.code, err.errno);
20+
assert.strictEqual(getSystemErrorName(uv[key]), name);
1821
assert.strictEqual(err.code, name);
22+
assert.strictEqual(err.code, err.errno);
1923
assert.strictEqual(err.message, `test ${name}`);
2024
});
2125
});
2226

23-
['test', {}, []].forEach((key) => {
24-
common.expectsError(
25-
() => util._errnoException(key),
26-
{
27-
code: 'ERR_INVALID_ARG_TYPE',
28-
type: TypeError,
29-
message: 'The "err" argument must be of type number. ' +
30-
`Received type ${typeof key}`
31-
});
32-
});
27+
function runTest(fn) {
28+
['test', {}, []].forEach((err) => {
29+
common.expectsError(
30+
() => fn(err),
31+
{
32+
code: 'ERR_INVALID_ARG_TYPE',
33+
type: TypeError,
34+
message: 'The "err" argument must be of type number. ' +
35+
`Received type ${typeof err}`
36+
});
37+
});
3338

34-
[0, 1, Infinity, -Infinity, NaN].forEach((key) => {
35-
common.expectsError(
36-
() => util._errnoException(key),
37-
{
38-
code: 'ERR_OUT_OF_RANGE',
39-
type: RangeError,
40-
message: 'The value of "err" is out of range. ' +
41-
'It must be a negative integer. ' +
42-
`Received ${key}`
43-
});
44-
});
39+
[0, 1, Infinity, -Infinity, NaN].forEach((err) => {
40+
common.expectsError(
41+
() => fn(err),
42+
{
43+
code: 'ERR_OUT_OF_RANGE',
44+
type: RangeError,
45+
message: 'The value of "err" is out of range. ' +
46+
'It must be a negative integer. ' +
47+
`Received ${err}`
48+
});
49+
});
50+
}
51+
52+
runTest(_errnoException);
53+
runTest(getSystemErrorName);

test/sequential/test-async-wrap-getasyncid.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const fs = require('fs');
66
const net = require('net');
77
const providers = Object.assign({}, process.binding('async_wrap').Providers);
88
const fixtures = require('../common/fixtures');
9+
const { getSystemErrorName } = require('util');
910

1011
// Make sure that all Providers are tested.
1112
{
@@ -205,7 +206,7 @@ if (common.hasCrypto) { // eslint-disable-line crypto-check
205206
// Use a long string to make sure the write happens asynchronously.
206207
const err = handle.writeLatin1String(wreq, 'hi'.repeat(100000));
207208
if (err)
208-
throw new Error(`write failed: ${process.binding('uv').errname(err)}`);
209+
throw new Error(`write failed: ${getSystemErrorName(err)}`);
209210
testInitialized(wreq, 'WriteWrap');
210211
});
211212
req.address = common.localhostIPv4;

0 commit comments

Comments
 (0)