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: https://github.com/nodejs/node/pull/18186 Refs: http://docs.libuv.org/en/v1.x/errors.html#c.uv_err_name Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
This commit is contained in:
parent
cbd634947d
commit
4af1bba6d0
@ -257,6 +257,25 @@ intended as a debugging tool. Some input values can have a significant
|
|||||||
performance overhead that can block the event loop. Use this function
|
performance overhead that can block the event loop. Use this function
|
||||||
with care and never in a hot code path.
|
with care and never in a hot code path.
|
||||||
|
|
||||||
|
## util.getSystemErrorName(err)
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* `err` {number}
|
||||||
|
* Returns: {string}
|
||||||
|
|
||||||
|
Returns the string name for a numeric error code that comes from a Node.js API.
|
||||||
|
The mapping between error codes and error names is platform-dependent.
|
||||||
|
See [Common System Errors][] for the names of common errors.
|
||||||
|
|
||||||
|
```js
|
||||||
|
fs.access('file/that/does/not/exist', (err) => {
|
||||||
|
const name = util.getSystemErrorName(err.errno);
|
||||||
|
console.error(name); // ENOENT
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
## util.inherits(constructor, superConstructor)
|
## util.inherits(constructor, superConstructor)
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v0.3.0
|
added: v0.3.0
|
||||||
@ -1362,6 +1381,7 @@ Deprecated predecessor of `console.log`.
|
|||||||
[Customizing `util.inspect` colors]: #util_customizing_util_inspect_colors
|
[Customizing `util.inspect` colors]: #util_customizing_util_inspect_colors
|
||||||
[Internationalization]: intl.html
|
[Internationalization]: intl.html
|
||||||
[WHATWG Encoding Standard]: https://encoding.spec.whatwg.org/
|
[WHATWG Encoding Standard]: https://encoding.spec.whatwg.org/
|
||||||
|
[Common System Errors]: errors.html#errors_common_system_errors
|
||||||
[constructor]: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/constructor
|
[constructor]: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/constructor
|
||||||
[list of deprecated APIS]: deprecations.html#deprecations_list_of_deprecated_apis
|
[list of deprecated APIS]: deprecations.html#deprecations_list_of_deprecated_apis
|
||||||
[semantically incompatible]: https://github.com/nodejs/node/issues/4179
|
[semantically incompatible]: https://github.com/nodejs/node/issues/4179
|
||||||
|
@ -22,7 +22,9 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const util = require('util');
|
const util = require('util');
|
||||||
const { deprecate, convertToValidSignal } = require('internal/util');
|
const {
|
||||||
|
deprecate, convertToValidSignal, getSystemErrorName
|
||||||
|
} = require('internal/util');
|
||||||
const { isUint8Array } = require('internal/util/types');
|
const { isUint8Array } = require('internal/util/types');
|
||||||
const { createPromise,
|
const { createPromise,
|
||||||
promiseResolve, promiseReject } = process.binding('util');
|
promiseResolve, promiseReject } = process.binding('util');
|
||||||
@ -30,7 +32,6 @@ const debug = util.debuglog('child_process');
|
|||||||
const { Buffer } = require('buffer');
|
const { Buffer } = require('buffer');
|
||||||
const { Pipe, constants: PipeConstants } = process.binding('pipe_wrap');
|
const { Pipe, constants: PipeConstants } = process.binding('pipe_wrap');
|
||||||
const errors = require('internal/errors');
|
const errors = require('internal/errors');
|
||||||
const { errname } = process.binding('uv');
|
|
||||||
const child_process = require('internal/child_process');
|
const child_process = require('internal/child_process');
|
||||||
const {
|
const {
|
||||||
_validateStdio,
|
_validateStdio,
|
||||||
@ -275,7 +276,7 @@ exports.execFile = function(file /*, args, options, callback*/) {
|
|||||||
if (!ex) {
|
if (!ex) {
|
||||||
ex = new Error('Command failed: ' + cmd + '\n' + stderr);
|
ex = new Error('Command failed: ' + cmd + '\n' + stderr);
|
||||||
ex.killed = child.killed || killed;
|
ex.killed = child.killed || killed;
|
||||||
ex.code = code < 0 ? errname(code) : code;
|
ex.code = code < 0 ? getSystemErrorName(code) : code;
|
||||||
ex.signal = signal;
|
ex.signal = signal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ const {
|
|||||||
arrow_message_private_symbol: kArrowMessagePrivateSymbolIndex,
|
arrow_message_private_symbol: kArrowMessagePrivateSymbolIndex,
|
||||||
decorated_private_symbol: kDecoratedPrivateSymbolIndex
|
decorated_private_symbol: kDecoratedPrivateSymbolIndex
|
||||||
} = process.binding('util');
|
} = process.binding('util');
|
||||||
|
const { errmap } = process.binding('uv');
|
||||||
|
|
||||||
const noCrypto = !process.versions.openssl;
|
const noCrypto = !process.versions.openssl;
|
||||||
|
|
||||||
@ -213,6 +214,19 @@ function getConstructorOf(obj) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSystemErrorName(err) {
|
||||||
|
if (typeof err !== 'number') {
|
||||||
|
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'err', 'number', err);
|
||||||
|
}
|
||||||
|
if (err >= 0 || !Number.isSafeInteger(err)) {
|
||||||
|
throw new errors.RangeError('ERR_OUT_OF_RANGE', 'err',
|
||||||
|
'a negative integer', err);
|
||||||
|
}
|
||||||
|
|
||||||
|
const entry = errmap.get(err);
|
||||||
|
return entry ? entry[0] : `Unknown system error ${err}`;
|
||||||
|
}
|
||||||
|
|
||||||
// getConstructorOf is wrapped into this to save iterations
|
// getConstructorOf is wrapped into this to save iterations
|
||||||
function getIdentificationOf(obj) {
|
function getIdentificationOf(obj) {
|
||||||
const original = obj;
|
const original = obj;
|
||||||
@ -340,6 +354,7 @@ module.exports = {
|
|||||||
emitExperimentalWarning,
|
emitExperimentalWarning,
|
||||||
filterDuplicateStrings,
|
filterDuplicateStrings,
|
||||||
getConstructorOf,
|
getConstructorOf,
|
||||||
|
getSystemErrorName,
|
||||||
getIdentificationOf,
|
getIdentificationOf,
|
||||||
isError,
|
isError,
|
||||||
join,
|
join,
|
||||||
|
12
lib/util.js
12
lib/util.js
@ -25,7 +25,6 @@ const errors = require('internal/errors');
|
|||||||
const { TextDecoder, TextEncoder } = require('internal/encoding');
|
const { TextDecoder, TextEncoder } = require('internal/encoding');
|
||||||
const { isBuffer } = require('buffer').Buffer;
|
const { isBuffer } = require('buffer').Buffer;
|
||||||
|
|
||||||
const { errname } = process.binding('uv');
|
|
||||||
const { previewMapIterator, previewSetIterator } = require('internal/v8');
|
const { previewMapIterator, previewSetIterator } = require('internal/v8');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -56,6 +55,7 @@ const {
|
|||||||
const {
|
const {
|
||||||
customInspectSymbol,
|
customInspectSymbol,
|
||||||
deprecate,
|
deprecate,
|
||||||
|
getSystemErrorName,
|
||||||
getIdentificationOf,
|
getIdentificationOf,
|
||||||
isError,
|
isError,
|
||||||
promisify,
|
promisify,
|
||||||
@ -1055,14 +1055,7 @@ function error(...args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _errnoException(err, syscall, original) {
|
function _errnoException(err, syscall, original) {
|
||||||
if (typeof err !== 'number') {
|
const name = getSystemErrorName(err);
|
||||||
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'err', 'number', err);
|
|
||||||
}
|
|
||||||
if (err >= 0 || !Number.isSafeInteger(err)) {
|
|
||||||
throw new errors.RangeError('ERR_OUT_OF_RANGE', 'err',
|
|
||||||
'a negative integer', err);
|
|
||||||
}
|
|
||||||
const name = errname(err);
|
|
||||||
var message = `${syscall} ${name}`;
|
var message = `${syscall} ${name}`;
|
||||||
if (original)
|
if (original)
|
||||||
message += ` ${original}`;
|
message += ` ${original}`;
|
||||||
@ -1151,6 +1144,7 @@ module.exports = exports = {
|
|||||||
debuglog,
|
debuglog,
|
||||||
deprecate,
|
deprecate,
|
||||||
format,
|
format,
|
||||||
|
getSystemErrorName,
|
||||||
inherits,
|
inherits,
|
||||||
inspect,
|
inspect,
|
||||||
isArray: Array.isArray,
|
isArray: Array.isArray,
|
||||||
|
@ -39,6 +39,8 @@ using v8::String;
|
|||||||
using v8::Value;
|
using v8::Value;
|
||||||
|
|
||||||
|
|
||||||
|
// TODO(joyeecheung): deprecate this function in favor of
|
||||||
|
// lib/util.getSystemErrorName()
|
||||||
void ErrName(const FunctionCallbackInfo<Value>& args) {
|
void ErrName(const FunctionCallbackInfo<Value>& args) {
|
||||||
Environment* env = Environment::GetCurrent(args);
|
Environment* env = Environment::GetCurrent(args);
|
||||||
int err = args[0]->Int32Value();
|
int err = args[0]->Int32Value();
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const common = require('../common');
|
const common = require('../common');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const execFile = require('child_process').execFile;
|
const execFile = require('child_process').execFile;
|
||||||
const uv = process.binding('uv');
|
const { getSystemErrorName } = require('util');
|
||||||
const fixtures = require('../common/fixtures');
|
const fixtures = require('../common/fixtures');
|
||||||
|
|
||||||
const fixture = fixtures.path('exit.js');
|
const fixture = fixtures.path('exit.js');
|
||||||
@ -26,7 +27,7 @@ const fixture = fixtures.path('exit.js');
|
|||||||
const code = -1;
|
const code = -1;
|
||||||
const callback = common.mustCall((err, stdout, stderr) => {
|
const callback = common.mustCall((err, stdout, stderr) => {
|
||||||
assert.strictEqual(err.toString().trim(), errorString);
|
assert.strictEqual(err.toString().trim(), errorString);
|
||||||
assert.strictEqual(err.code, uv.errname(code));
|
assert.strictEqual(err.code, getSystemErrorName(code));
|
||||||
assert.strictEqual(err.killed, true);
|
assert.strictEqual(err.killed, true);
|
||||||
assert.strictEqual(err.signal, null);
|
assert.strictEqual(err.signal, null);
|
||||||
assert.strictEqual(err.cmd, process.execPath);
|
assert.strictEqual(err.cmd, process.execPath);
|
||||||
|
@ -4,7 +4,7 @@ const common = require('../common');
|
|||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const net = require('net');
|
const net = require('net');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const uv = process.binding('uv');
|
const { getSystemErrorName } = require('util');
|
||||||
const { TCP, constants: TCPConstants } = process.binding('tcp_wrap');
|
const { TCP, constants: TCPConstants } = process.binding('tcp_wrap');
|
||||||
const { Pipe, constants: PipeConstants } = process.binding('pipe_wrap');
|
const { Pipe, constants: PipeConstants } = process.binding('pipe_wrap');
|
||||||
|
|
||||||
@ -46,9 +46,10 @@ function randomHandle(type) {
|
|||||||
handleName = `pipe ${path}`;
|
handleName = `pipe ${path}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errno < 0) { // uv.errname requires err < 0
|
if (errno < 0) {
|
||||||
assert(errno >= 0, `unable to bind ${handleName}: ${uv.errname(errno)}`);
|
assert.fail(`unable to bind ${handleName}: ${getSystemErrorName(errno)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!common.isWindows) { // fd doesn't work on windows
|
if (!common.isWindows) { // fd doesn't work on windows
|
||||||
// err >= 0 but fd = -1, should not happen
|
// err >= 0 but fd = -1, should not happen
|
||||||
assert.notStrictEqual(handle.fd, -1,
|
assert.notStrictEqual(handle.fd, -1,
|
||||||
|
@ -2,9 +2,12 @@
|
|||||||
|
|
||||||
const common = require('../common');
|
const common = require('../common');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const util = require('util');
|
const {
|
||||||
const uv = process.binding('uv');
|
getSystemErrorName,
|
||||||
|
_errnoException
|
||||||
|
} = require('util');
|
||||||
|
|
||||||
|
const uv = process.binding('uv');
|
||||||
const keys = Object.keys(uv);
|
const keys = Object.keys(uv);
|
||||||
|
|
||||||
keys.forEach((key) => {
|
keys.forEach((key) => {
|
||||||
@ -12,33 +15,39 @@ keys.forEach((key) => {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
assert.doesNotThrow(() => {
|
assert.doesNotThrow(() => {
|
||||||
const err = util._errnoException(uv[key], 'test');
|
const err = _errnoException(uv[key], 'test');
|
||||||
const name = uv.errname(uv[key]);
|
const name = uv.errname(uv[key]);
|
||||||
assert.strictEqual(err.code, err.errno);
|
assert.strictEqual(getSystemErrorName(uv[key]), name);
|
||||||
assert.strictEqual(err.code, name);
|
assert.strictEqual(err.code, name);
|
||||||
|
assert.strictEqual(err.code, err.errno);
|
||||||
assert.strictEqual(err.message, `test ${name}`);
|
assert.strictEqual(err.message, `test ${name}`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
['test', {}, []].forEach((key) => {
|
function runTest(fn) {
|
||||||
|
['test', {}, []].forEach((err) => {
|
||||||
common.expectsError(
|
common.expectsError(
|
||||||
() => util._errnoException(key),
|
() => fn(err),
|
||||||
{
|
{
|
||||||
code: 'ERR_INVALID_ARG_TYPE',
|
code: 'ERR_INVALID_ARG_TYPE',
|
||||||
type: TypeError,
|
type: TypeError,
|
||||||
message: 'The "err" argument must be of type number. ' +
|
message: 'The "err" argument must be of type number. ' +
|
||||||
`Received type ${typeof key}`
|
`Received type ${typeof err}`
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
[0, 1, Infinity, -Infinity, NaN].forEach((key) => {
|
[0, 1, Infinity, -Infinity, NaN].forEach((err) => {
|
||||||
common.expectsError(
|
common.expectsError(
|
||||||
() => util._errnoException(key),
|
() => fn(err),
|
||||||
{
|
{
|
||||||
code: 'ERR_OUT_OF_RANGE',
|
code: 'ERR_OUT_OF_RANGE',
|
||||||
type: RangeError,
|
type: RangeError,
|
||||||
message: 'The value of "err" is out of range. ' +
|
message: 'The value of "err" is out of range. ' +
|
||||||
'It must be a negative integer. ' +
|
'It must be a negative integer. ' +
|
||||||
`Received ${key}`
|
`Received ${err}`
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
runTest(_errnoException);
|
||||||
|
runTest(getSystemErrorName);
|
||||||
|
@ -6,6 +6,7 @@ const fs = require('fs');
|
|||||||
const net = require('net');
|
const net = require('net');
|
||||||
const providers = Object.assign({}, process.binding('async_wrap').Providers);
|
const providers = Object.assign({}, process.binding('async_wrap').Providers);
|
||||||
const fixtures = require('../common/fixtures');
|
const fixtures = require('../common/fixtures');
|
||||||
|
const { getSystemErrorName } = require('util');
|
||||||
|
|
||||||
// Make sure that all Providers are tested.
|
// Make sure that all Providers are tested.
|
||||||
{
|
{
|
||||||
@ -205,7 +206,7 @@ if (common.hasCrypto) { // eslint-disable-line crypto-check
|
|||||||
// Use a long string to make sure the write happens asynchronously.
|
// Use a long string to make sure the write happens asynchronously.
|
||||||
const err = handle.writeLatin1String(wreq, 'hi'.repeat(100000));
|
const err = handle.writeLatin1String(wreq, 'hi'.repeat(100000));
|
||||||
if (err)
|
if (err)
|
||||||
throw new Error(`write failed: ${process.binding('uv').errname(err)}`);
|
throw new Error(`write failed: ${getSystemErrorName(err)}`);
|
||||||
testInitialized(wreq, 'WriteWrap');
|
testInitialized(wreq, 'WriteWrap');
|
||||||
});
|
});
|
||||||
req.address = common.localhostIPv4;
|
req.address = common.localhostIPv4;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user