From 8dd4ba01ad883bef6c54fd7ac67b0aac81e913fa Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 14 Apr 2019 21:34:26 +0200 Subject: [PATCH 1/6] buffer,error: improve bigint, big numbers and more This improves the error message from `ERR_OUT_OF_RANGE` by closer inspecting the value and logging numbers above 2 ** 32 by adding commas to the output for integer and bigint. BigInt is now also marked if used. Buffer errors also format the range as 2 ** n instead of showing a huge number. --- lib/internal/buffer.js | 15 ++++++++++++++- lib/internal/errors.js | 17 +++++++++++++++-- test/parallel/test-buffer-bigint64.js | 6 +++++- test/parallel/test-buffer-writeint.js | 8 ++++++-- test/parallel/test-buffer-writeuint.js | 4 +++- test/parallel/test-crypto-pbkdf2.js | 4 ++-- .../test-event-emitter-max-listeners.js | 5 +++-- test/parallel/test-file-write-stream3.js | 4 ++-- 8 files changed, 50 insertions(+), 13 deletions(-) diff --git a/lib/internal/buffer.js b/lib/internal/buffer.js index 9a70dd4c89e26d..aaaf2ca25f7c14 100644 --- a/lib/internal/buffer.js +++ b/lib/internal/buffer.js @@ -43,7 +43,20 @@ function checkBounds(buf, offset, byteLength) { function checkInt(value, min, max, buf, offset, byteLength) { if (value > max || value < min) { - throw new ERR_OUT_OF_RANGE('value', `>= ${min} and <= ${max}`, value); + // eslint-disable-next-line valid-typeof + const n = typeof min === 'bigint' ? 'n' : ''; + let range; + if (byteLength > 3) { + if (min === 0 || min === 0n) { + range = `>= 0${n} and < 2${n} ** ${(byteLength + 1) * 8}${n}`; + } else { + range = `>= -(2${n} ** ${(byteLength + 1) * 8 - 1}${n}) and < 2 ** ` + + `${(byteLength + 1) * 8 - 1}${n}`; + } + } else { + range = `>= ${min}${n} and <= ${max}${n}`; + } + throw new ERR_OUT_OF_RANGE('value', range, value); } checkBounds(buf, offset, byteLength); } diff --git a/lib/internal/errors.js b/lib/internal/errors.js index bc3f43653a4a9b..33705df0e228a4 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -10,7 +10,7 @@ // value statically and permanently identifies the error. While the error // message may change, the code should not. -const { Object } = primordials; +const { Object, Math } = primordials; const kCode = Symbol('code'); const kInfo = Symbol('info'); @@ -999,7 +999,20 @@ E('ERR_OUT_OF_RANGE', assert(range, 'Missing "range" argument'); let msg = replaceDefaultBoolean ? str : `The value of "${str}" is out of range.`; - msg += ` It must be ${range}. Received ${input}`; + let received; + if (Number.isInteger(input) && Math.abs(input) > 2 ** 32) { + received = String(input).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,'); + // eslint-disable-next-line valid-typeof + } else if (typeof input === 'bigint') { + received = String(input); + if (input > 2n ** 32n || input < -(2n ** 32n)) { + received = received.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,'); + } + received += 'n'; + } else { + received = lazyInternalUtilInspect().inspect(input); + } + msg += ` It must be ${range}. Received ${received}`; return msg; }, RangeError); E('ERR_REQUIRE_ESM', 'Must use import to load ES Module: %s', Error); diff --git a/test/parallel/test-buffer-bigint64.js b/test/parallel/test-buffer-bigint64.js index 859a40811e1bb9..199209f91342f6 100644 --- a/test/parallel/test-buffer-bigint64.js +++ b/test/parallel/test-buffer-bigint64.js @@ -37,7 +37,11 @@ const buf = Buffer.allocUnsafe(8); assert.throws(function() { const val = 0x10000000000000000n; buf['writeBigUInt64' + endianness](val, 0); - }, RangeError); + }, { + code: 'ERR_OUT_OF_RANGE', + message: 'The value of "value" is out of range. It must be ' + + '>= 0n and < 2n ** 64n. Received 18,446,744,073,709,551,616n' + }); // Should throw a TypeError upon invalid input assert.throws(function() { diff --git a/test/parallel/test-buffer-writeint.js b/test/parallel/test-buffer-writeint.js index 7dba14211cf758..e69c785ec8b3e3 100644 --- a/test/parallel/test-buffer-writeint.js +++ b/test/parallel/test-buffer-writeint.js @@ -217,15 +217,19 @@ const errorOutOfBounds = common.expectsError({ ['writeIntBE', 'writeIntLE'].forEach((fn) => { const min = -(2 ** (i * 8 - 1)); const max = 2 ** (i * 8 - 1) - 1; - + let range = `>= ${min} and <= ${max}`; + if (i > 4) { + range = `>= -(2 ** ${i * 8 - 1}) and < 2 ** ${i * 8 - 1}`; + } [min - 1, max + 1].forEach((val) => { + const received = i > 4 ? val.toLocaleString() : val; assert.throws(() => { data[fn](val, 0, i); }, { code: 'ERR_OUT_OF_RANGE', name: 'RangeError', message: 'The value of "value" is out of range. ' + - `It must be >= ${min} and <= ${max}. Received ${val}` + `It must be ${range}. Received ${received}` }); }); diff --git a/test/parallel/test-buffer-writeuint.js b/test/parallel/test-buffer-writeuint.js index cd500004429ba9..7729a7ed89b3a2 100644 --- a/test/parallel/test-buffer-writeuint.js +++ b/test/parallel/test-buffer-writeuint.js @@ -171,6 +171,8 @@ const assert = require('assert'); // Test 1 to 6 bytes. for (let i = 1; i < 6; i++) { + const range = i < 5 ? `= ${val - 1}` : ` 2 ** ${i * 8}`; + const received = i > 4 ? val.toLocaleString() : val; ['writeUIntBE', 'writeUIntLE'].forEach((fn) => { assert.throws(() => { data[fn](val, 0, i); @@ -178,7 +180,7 @@ const assert = require('assert'); code: 'ERR_OUT_OF_RANGE', name: 'RangeError', message: 'The value of "value" is out of range. ' + - `It must be >= 0 and <= ${val - 1}. Received ${val}` + `It must be >= 0 and <${range}. Received ${received}` }); ['', '0', null, {}, [], () => {}, true, false].forEach((o) => { diff --git a/test/parallel/test-crypto-pbkdf2.js b/test/parallel/test-crypto-pbkdf2.js index 4e3c4f64f076d8..b50d398a58d0f6 100644 --- a/test/parallel/test-crypto-pbkdf2.js +++ b/test/parallel/test-crypto-pbkdf2.js @@ -111,8 +111,8 @@ assert.throws( }, { code: 'ERR_OUT_OF_RANGE', name: 'RangeError', - message: 'The value of "keylen" is out of range. It ' + - `must be >= 0 && < 4294967296. Received ${input}` + message: 'The value of "keylen" is out of range. It must be ' + + `>= 0 && < 4294967296. Received ${input.toLocaleString()}` }); }); diff --git a/test/parallel/test-event-emitter-max-listeners.js b/test/parallel/test-event-emitter-max-listeners.js index a906000477f7da..39b5737fde92a2 100644 --- a/test/parallel/test-event-emitter-max-listeners.js +++ b/test/parallel/test-event-emitter-max-listeners.js @@ -22,6 +22,7 @@ 'use strict'; const common = require('../common'); const events = require('events'); +const { inspect } = require('util'); const e = new events.EventEmitter(); e.on('maxListeners', common.mustCall()); @@ -38,7 +39,7 @@ for (const obj of throwsObjs) { code: 'ERR_OUT_OF_RANGE', type: RangeError, message: 'The value of "n" is out of range. ' + - `It must be a non-negative number. Received ${obj}` + `It must be a non-negative number. Received ${inspect(obj)}` } ); @@ -48,7 +49,7 @@ for (const obj of throwsObjs) { code: 'ERR_OUT_OF_RANGE', type: RangeError, message: 'The value of "defaultMaxListeners" is out of range. ' + - `It must be a non-negative number. Received ${obj}` + `It must be a non-negative number. Received ${inspect(obj)}` } ); } diff --git a/test/parallel/test-file-write-stream3.js b/test/parallel/test-file-write-stream3.js index 618fbdf884c0c6..95f736e24a5df3 100644 --- a/test/parallel/test-file-write-stream3.js +++ b/test/parallel/test-file-write-stream3.js @@ -199,8 +199,8 @@ const run_test_5 = common.mustCall(function() { }; const err = { code: 'ERR_OUT_OF_RANGE', - message: 'The value of "start" is out of range. ' + - 'It must be >= 0 and <= 2 ** 53 - 1. Received 9007199254740992', + message: 'The value of "start" is out of range. It must be ' + + '>= 0 and <= 2 ** 53 - 1. Received 9,007,199,254,740,992', type: RangeError }; common.expectsError(fn, err); From 1987eeb2c76a30595a5d170fd915656a28f16ebb Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 15 Apr 2019 20:36:53 +0200 Subject: [PATCH 2/6] fixup! buffer,error: improve bigint, big numbers and more --- test/parallel/test-buffer-writeint.js | 4 +++- test/parallel/test-buffer-writeuint.js | 4 +++- test/parallel/test-crypto-pbkdf2.js | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/test/parallel/test-buffer-writeint.js b/test/parallel/test-buffer-writeint.js index e69c785ec8b3e3..7332d7acf90ff0 100644 --- a/test/parallel/test-buffer-writeint.js +++ b/test/parallel/test-buffer-writeint.js @@ -222,7 +222,9 @@ const errorOutOfBounds = common.expectsError({ range = `>= -(2 ** ${i * 8 - 1}) and < 2 ** ${i * 8 - 1}`; } [min - 1, max + 1].forEach((val) => { - const received = i > 4 ? val.toLocaleString() : val; + const received = i > 4 ? + String(val).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') : + val; assert.throws(() => { data[fn](val, 0, i); }, { diff --git a/test/parallel/test-buffer-writeuint.js b/test/parallel/test-buffer-writeuint.js index 7729a7ed89b3a2..19a02a99231810 100644 --- a/test/parallel/test-buffer-writeuint.js +++ b/test/parallel/test-buffer-writeuint.js @@ -172,7 +172,9 @@ const assert = require('assert'); // Test 1 to 6 bytes. for (let i = 1; i < 6; i++) { const range = i < 5 ? `= ${val - 1}` : ` 2 ** ${i * 8}`; - const received = i > 4 ? val.toLocaleString() : val; + const received = i > 4 ? + String(val).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') : + val; ['writeUIntBE', 'writeUIntLE'].forEach((fn) => { assert.throws(() => { data[fn](val, 0, i); diff --git a/test/parallel/test-crypto-pbkdf2.js b/test/parallel/test-crypto-pbkdf2.js index b50d398a58d0f6..6a386c719c3679 100644 --- a/test/parallel/test-crypto-pbkdf2.js +++ b/test/parallel/test-crypto-pbkdf2.js @@ -111,8 +111,8 @@ assert.throws( }, { code: 'ERR_OUT_OF_RANGE', name: 'RangeError', - message: 'The value of "keylen" is out of range. It must be ' + - `>= 0 && < 4294967296. Received ${input.toLocaleString()}` + message: 'The value of "keylen" is out of range. It must be >= 0 && < ' + + `4294967296. Received ${input === -1 ? '-1' : '4,294,967,297'}` }); }); From 9e051547aada5a693f57daffef89f8c0b124966a Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 23 Apr 2019 16:23:59 +0200 Subject: [PATCH 3/6] fixup! buffer,error: improve bigint, big numbers and more --- lib/internal/errors.js | 4 ++-- test/parallel/test-buffer-bigint64.js | 2 +- test/parallel/test-buffer-writeint.js | 2 +- test/parallel/test-buffer-writeuint.js | 2 +- test/parallel/test-crypto-pbkdf2.js | 2 +- test/parallel/test-file-write-stream3.js | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 33705df0e228a4..68244644e8d923 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -1001,12 +1001,12 @@ E('ERR_OUT_OF_RANGE', `The value of "${str}" is out of range.`; let received; if (Number.isInteger(input) && Math.abs(input) > 2 ** 32) { - received = String(input).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,'); + received = String(input).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1_'); // eslint-disable-next-line valid-typeof } else if (typeof input === 'bigint') { received = String(input); if (input > 2n ** 32n || input < -(2n ** 32n)) { - received = received.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,'); + received = received.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1_'); } received += 'n'; } else { diff --git a/test/parallel/test-buffer-bigint64.js b/test/parallel/test-buffer-bigint64.js index 199209f91342f6..60d376bdaf84af 100644 --- a/test/parallel/test-buffer-bigint64.js +++ b/test/parallel/test-buffer-bigint64.js @@ -40,7 +40,7 @@ const buf = Buffer.allocUnsafe(8); }, { code: 'ERR_OUT_OF_RANGE', message: 'The value of "value" is out of range. It must be ' + - '>= 0n and < 2n ** 64n. Received 18,446,744,073,709,551,616n' + '>= 0n and < 2n ** 64n. Received 18_446_744_073_709_551_616n' }); // Should throw a TypeError upon invalid input diff --git a/test/parallel/test-buffer-writeint.js b/test/parallel/test-buffer-writeint.js index 7332d7acf90ff0..05b0cd1ebbfada 100644 --- a/test/parallel/test-buffer-writeint.js +++ b/test/parallel/test-buffer-writeint.js @@ -223,7 +223,7 @@ const errorOutOfBounds = common.expectsError({ } [min - 1, max + 1].forEach((val) => { const received = i > 4 ? - String(val).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') : + String(val).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1_') : val; assert.throws(() => { data[fn](val, 0, i); diff --git a/test/parallel/test-buffer-writeuint.js b/test/parallel/test-buffer-writeuint.js index 19a02a99231810..3823b74d565519 100644 --- a/test/parallel/test-buffer-writeuint.js +++ b/test/parallel/test-buffer-writeuint.js @@ -173,7 +173,7 @@ const assert = require('assert'); for (let i = 1; i < 6; i++) { const range = i < 5 ? `= ${val - 1}` : ` 2 ** ${i * 8}`; const received = i > 4 ? - String(val).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') : + String(val).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1_') : val; ['writeUIntBE', 'writeUIntLE'].forEach((fn) => { assert.throws(() => { diff --git a/test/parallel/test-crypto-pbkdf2.js b/test/parallel/test-crypto-pbkdf2.js index 6a386c719c3679..1d4e6f6617f074 100644 --- a/test/parallel/test-crypto-pbkdf2.js +++ b/test/parallel/test-crypto-pbkdf2.js @@ -112,7 +112,7 @@ assert.throws( code: 'ERR_OUT_OF_RANGE', name: 'RangeError', message: 'The value of "keylen" is out of range. It must be >= 0 && < ' + - `4294967296. Received ${input === -1 ? '-1' : '4,294,967,297'}` + `4294967296. Received ${input === -1 ? '-1' : '4_294_967_297'}` }); }); diff --git a/test/parallel/test-file-write-stream3.js b/test/parallel/test-file-write-stream3.js index 95f736e24a5df3..45c431b5d51b94 100644 --- a/test/parallel/test-file-write-stream3.js +++ b/test/parallel/test-file-write-stream3.js @@ -200,7 +200,7 @@ const run_test_5 = common.mustCall(function() { const err = { code: 'ERR_OUT_OF_RANGE', message: 'The value of "start" is out of range. It must be ' + - '>= 0 and <= 2 ** 53 - 1. Received 9,007,199,254,740,992', + '>= 0 and <= 2 ** 53 - 1. Received 9_007_199_254_740_992', type: RangeError }; common.expectsError(fn, err); From f8232875f3013c783b591cbf26105fa08286f1f2 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 25 Apr 2019 12:52:51 +0200 Subject: [PATCH 4/6] fixup! buffer,error: improve bigint, big numbers and more --- lib/internal/errors.js | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 68244644e8d923..1976710d0c39da 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -574,6 +574,22 @@ function oneOf(expected, thing) { } } +function addNumericalSeparator(val) { + let res = val[val.length - 1]; + let count = 0; + for (let i = val.length - 2; i >= 0; i--) { + count++; + if (val[i] === '.') { + return val; + } + if (count % 3 === 0 && i !== val.length && val[i] !== '-') { + res = `_${res}`; + } + res = `${val[i]}${res}`; + } + return res; +} + module.exports = { addCodeToName, // Exported for NghttpError codes, @@ -1001,12 +1017,12 @@ E('ERR_OUT_OF_RANGE', `The value of "${str}" is out of range.`; let received; if (Number.isInteger(input) && Math.abs(input) > 2 ** 32) { - received = String(input).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1_'); + received = addNumericalSeparator(String(input)); // eslint-disable-next-line valid-typeof } else if (typeof input === 'bigint') { received = String(input); if (input > 2n ** 32n || input < -(2n ** 32n)) { - received = received.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1_'); + received = addNumericalSeparator(received); } received += 'n'; } else { From 19aebdc67f3af4e42d13ca4d3a2523554f0ee82a Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 26 Apr 2019 18:55:53 +0200 Subject: [PATCH 5/6] fixup! buffer,error: improve bigint, big numbers and more --- lib/internal/errors.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 1976710d0c39da..8875673a4665f5 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -582,7 +582,7 @@ function addNumericalSeparator(val) { if (val[i] === '.') { return val; } - if (count % 3 === 0 && i !== val.length && val[i] !== '-') { + if (count % 3 === 0 && val[i] !== '-') { res = `_${res}`; } res = `${val[i]}${res}`; From f7c61bb69bf0338bd61746b9168353e45d9316ab Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 27 Apr 2019 01:38:28 +0200 Subject: [PATCH 6/6] fixup! buffer,error: improve bigint, big numbers and more --- lib/internal/errors.js | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 8875673a4665f5..c49d0bc84f2549 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -574,20 +574,15 @@ function oneOf(expected, thing) { } } +// Only use this for integers! Decimal numbers do not work with this function. function addNumericalSeparator(val) { - let res = val[val.length - 1]; - let count = 0; - for (let i = val.length - 2; i >= 0; i--) { - count++; - if (val[i] === '.') { - return val; - } - if (count % 3 === 0 && val[i] !== '-') { - res = `_${res}`; - } - res = `${val[i]}${res}`; + let res = ''; + let i = val.length; + const start = val[0] === '-' ? 1 : 0; + for (; i >= start + 4; i -= 3) { + res = `_${val.slice(i - 3, i)}${res}`; } - return res; + return `${val.slice(0, i)}${res}`; } module.exports = {