Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

util: implement util.getSystemErrorName() #18186

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions doc/api/util.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: It might be good to also show the err.errno here to outline the difference.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bridge The numeric code is platform-dependent so I am not sure if it's OK to just show the value of a particular platform or should I need to show all the possible values...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you meant me ;-)

And true, I forgot about that. Hm, either a "e.g. on platform xyz" or just land it as is. What ever you think is best.

});
```

## util.inherits(constructor, superConstructor)
<!-- YAML
added: v0.3.0
Expand Down Expand Up @@ -1362,6 +1381,7 @@ Deprecated predecessor of `console.log`.
[Customizing `util.inspect` colors]: #util_customizing_util_inspect_colors
[Internationalization]: intl.html
[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
[list of deprecated APIS]: deprecations.html#deprecations_list_of_deprecated_apis
[semantically incompatible]: https://github.com/nodejs/node/issues/4179
7 changes: 4 additions & 3 deletions lib/child_process.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,16 @@
'use strict';

const util = require('util');
const { deprecate, convertToValidSignal } = require('internal/util');
const {
deprecate, convertToValidSignal, getSystemErrorName
} = require('internal/util');
const { isUint8Array } = require('internal/util/types');
const { createPromise,
promiseResolve, promiseReject } = process.binding('util');
const debug = util.debuglog('child_process');
const { Buffer } = require('buffer');
const { Pipe, constants: PipeConstants } = process.binding('pipe_wrap');
const errors = require('internal/errors');
const { errname } = process.binding('uv');
const child_process = require('internal/child_process');
const {
_validateStdio,
Expand Down Expand Up @@ -275,7 +276,7 @@ exports.execFile = function(file /*, args, options, callback*/) {
if (!ex) {
ex = new Error('Command failed: ' + cmd + '\n' + stderr);
ex.killed = child.killed || killed;
ex.code = code < 0 ? errname(code) : code;
ex.code = code < 0 ? getSystemErrorName(code) : code;
ex.signal = signal;
}

Expand Down
15 changes: 15 additions & 0 deletions lib/internal/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const {
arrow_message_private_symbol: kArrowMessagePrivateSymbolIndex,
decorated_private_symbol: kDecoratedPrivateSymbolIndex
} = process.binding('util');
const { errmap } = process.binding('uv');

const noCrypto = !process.versions.openssl;

Expand Down Expand Up @@ -213,6 +214,19 @@ function getConstructorOf(obj) {
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)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that if (err >> 0 |== err) should work also

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
function getIdentificationOf(obj) {
const original = obj;
Expand Down Expand Up @@ -340,6 +354,7 @@ module.exports = {
emitExperimentalWarning,
filterDuplicateStrings,
getConstructorOf,
getSystemErrorName,
getIdentificationOf,
isError,
join,
Expand Down
12 changes: 3 additions & 9 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ const errors = require('internal/errors');
const { TextDecoder, TextEncoder } = require('internal/encoding');
const { isBuffer } = require('buffer').Buffer;

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

const {
Expand Down Expand Up @@ -56,6 +55,7 @@ const {
const {
customInspectSymbol,
deprecate,
getSystemErrorName,
getIdentificationOf,
isError,
promisify,
Expand Down Expand Up @@ -1055,14 +1055,7 @@ function error(...args) {
}

function _errnoException(err, syscall, original) {
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 name = errname(err);
const name = getSystemErrorName(err);
var message = `${syscall} ${name}`;
if (original)
message += ` ${original}`;
Expand Down Expand Up @@ -1151,6 +1144,7 @@ module.exports = exports = {
debuglog,
deprecate,
format,
getSystemErrorName,
inherits,
inspect,
isArray: Array.isArray,
Expand Down
2 changes: 2 additions & 0 deletions src/uv.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ using v8::String;
using v8::Value;


// TODO(joyeecheung): deprecate this function in favor of
// lib/util.getSystemErrorName()
void ErrName(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
int err = args[0]->Int32Value();
Expand Down
5 changes: 3 additions & 2 deletions test/parallel/test-child-process-execfile.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
'use strict';

const common = require('../common');
const assert = require('assert');
const execFile = require('child_process').execFile;
const uv = process.binding('uv');
const { getSystemErrorName } = require('util');
const fixtures = require('../common/fixtures');

const fixture = fixtures.path('exit.js');
Expand All @@ -26,7 +27,7 @@ const fixture = fixtures.path('exit.js');
const code = -1;
const callback = common.mustCall((err, stdout, stderr) => {
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.signal, null);
assert.strictEqual(err.cmd, process.execPath);
Expand Down
7 changes: 4 additions & 3 deletions test/parallel/test-net-server-listen-handle.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const common = require('../common');
const assert = require('assert');
const net = require('net');
const fs = require('fs');
const uv = process.binding('uv');
const { getSystemErrorName } = require('util');
const { TCP, constants: TCPConstants } = process.binding('tcp_wrap');
const { Pipe, constants: PipeConstants } = process.binding('pipe_wrap');

Expand Down Expand Up @@ -46,9 +46,10 @@ function randomHandle(type) {
handleName = `pipe ${path}`;
}

if (errno < 0) { // uv.errname requires err < 0
assert(errno >= 0, `unable to bind ${handleName}: ${uv.errname(errno)}`);
if (errno < 0) {
assert.fail(`unable to bind ${handleName}: ${getSystemErrorName(errno)}`);
}

if (!common.isWindows) { // fd doesn't work on windows
// err >= 0 but fd = -1, should not happen
assert.notStrictEqual(handle.fd, -1,
Expand Down
59 changes: 34 additions & 25 deletions test/parallel/test-uv-errno.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,52 @@

const common = require('../common');
const assert = require('assert');
const util = require('util');
const uv = process.binding('uv');
const {
getSystemErrorName,
_errnoException
} = require('util');

const uv = process.binding('uv');
const keys = Object.keys(uv);

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

assert.doesNotThrow(() => {
const err = util._errnoException(uv[key], 'test');
const err = _errnoException(uv[key], 'test');
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, err.errno);
assert.strictEqual(err.message, `test ${name}`);
});
});

['test', {}, []].forEach((key) => {
common.expectsError(
() => util._errnoException(key),
{
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "err" argument must be of type number. ' +
`Received type ${typeof key}`
});
});
function runTest(fn) {
['test', {}, []].forEach((err) => {
common.expectsError(
() => fn(err),
{
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "err" argument must be of type number. ' +
`Received type ${typeof err}`
});
});

[0, 1, Infinity, -Infinity, NaN].forEach((key) => {
common.expectsError(
() => util._errnoException(key),
{
code: 'ERR_OUT_OF_RANGE',
type: RangeError,
message: 'The value of "err" is out of range. ' +
'It must be a negative integer. ' +
`Received ${key}`
});
});
[0, 1, Infinity, -Infinity, NaN].forEach((err) => {
common.expectsError(
() => fn(err),
{
code: 'ERR_OUT_OF_RANGE',
type: RangeError,
message: 'The value of "err" is out of range. ' +
'It must be a negative integer. ' +
`Received ${err}`
});
});
}

runTest(_errnoException);
runTest(getSystemErrorName);
3 changes: 2 additions & 1 deletion test/sequential/test-async-wrap-getasyncid.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const fs = require('fs');
const net = require('net');
const providers = Object.assign({}, process.binding('async_wrap').Providers);
const fixtures = require('../common/fixtures');
const { getSystemErrorName } = require('util');

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