Skip to content

Commit

Permalink
util: implement util.getSystemErrorName()
Browse files Browse the repository at this point in the history
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: nodejs#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>
  • Loading branch information
joyeecheung committed Feb 22, 2018
1 parent 017013a commit 3b18dae
Show file tree
Hide file tree
Showing 9 changed files with 60 additions and 23 deletions.
22 changes: 21 additions & 1 deletion doc/api/util.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,25 @@ without any formatting.
util.format('%% %s'); // '%% %s'
```

## 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)
<!-- YAML
added: v0.3.0
Expand Down Expand Up @@ -1228,5 +1247,6 @@ 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/
[constructor]: https://developer.mozilla.org/en-US/JavaScript/Reference/Global_Objects/Object/constructor
[Common System Errors]: errors.html#errors_common_system_errors
[constructor]: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/constructor
[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,14 +22,15 @@
'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 { errname } = process.binding('uv');
const child_process = require('internal/child_process');
const {
_validateStdio,
Expand Down Expand Up @@ -271,7 +272,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
13 changes: 12 additions & 1 deletion lib/internal/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
const errors = require('internal/errors');
const binding = process.binding('util');
const { signals } = process.binding('constants').os;

const { createPromise, promiseResolve, promiseReject } = binding;
const { errmap } = process.binding('uv');

const kArrowMessagePrivateSymbolIndex = binding.arrow_message_private_symbol;
const kDecoratedPrivateSymbolIndex = binding.decorated_private_symbol;
Expand Down Expand Up @@ -207,6 +207,16 @@ function getConstructorOf(obj) {
return null;
}

function getSystemErrorName(err) {
if (typeof err !== 'number' || err >= 0 || !Number.isSafeInteger(err)) {
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'err',
'negative number');
}

const entry = errmap.get(err);
return entry ? entry[0] : `Unknown system error ${err}`;
}

const kCustomPromisifiedSymbol = Symbol('util.promisify.custom');
const kCustomPromisifyArgsSymbol = Symbol('customPromisifyArgs');

Expand Down Expand Up @@ -297,6 +307,7 @@ module.exports = {
emitExperimentalWarning,
filterDuplicateStrings,
getConstructorOf,
getSystemErrorName,
isError,
join,
normalizeEncoding,
Expand Down
10 changes: 3 additions & 7 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ const errors = require('internal/errors');
const { TextDecoder, TextEncoder } = require('internal/encoding');
const { isBuffer } = require('buffer').Buffer;

const { errname } = process.binding('uv');

const {
getPromiseDetails,
getProxyDetails,
Expand Down Expand Up @@ -56,6 +54,7 @@ const {
customInspectSymbol,
deprecate,
getConstructorOf,
getSystemErrorName,
isError,
promisify,
join,
Expand Down Expand Up @@ -992,11 +991,7 @@ function error(...args) {
}

function _errnoException(err, syscall, original) {
if (typeof err !== 'number' || err >= 0 || !Number.isSafeInteger(err)) {
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'err',
'negative number');
}
const name = errname(err);
const name = getSystemErrorName(err);
var message = `${syscall} ${name}`;
if (original)
message += ` ${original}`;
Expand Down Expand Up @@ -1085,6 +1080,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 @@ -27,7 +28,7 @@ const execOpts = { encoding: 'utf8', shell: true };
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 @@ -47,9 +47,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
14 changes: 9 additions & 5 deletions test/parallel/test-uv-errno.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,31 @@

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}`);
});
});

[0, 1, 'test', {}, [], Infinity, -Infinity, NaN].forEach((key) => {
common.expectsError(
() => util._errnoException(key),
() => _errnoException(key),
{
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
Expand Down
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 @@ -7,6 +7,7 @@ const net = require('net');
const providers = Object.assign({}, process.binding('async_wrap').Providers);
const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');
const { getSystemErrorName } = require('util');

// Make sure that all Providers are tested.
{
Expand Down Expand Up @@ -214,7 +215,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

0 comments on commit 3b18dae

Please sign in to comment.