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

lib/src: refactoring out of range index and and migrate error for consistency #11296

Closed
wants to merge 3 commits into from

Conversation

larissayvette
Copy link
Contributor

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines
Affected core subsystem(s)

lib, src
Changing where RangeError was out of range index to Index out of range

@nodejs-github-bot nodejs-github-bot added buffer Issues and PRs related to the buffer subsystem. c++ Issues and PRs that require attention from people who are familiar with C++. labels Feb 10, 2017
@jasnell jasnell added the semver-major PRs that contain breaking changes and should be released in the next major version. label Feb 10, 2017
Copy link
Member

@jasnell jasnell left a comment

Choose a reason for hiding this comment

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

If we were to do this, I'd prefer to make it part of the migration to using internal/errors.js. See #11273.

Changing error messages like this is a semver-major change and has a very real chance of breaking user code. Accordingly, since we'll be moving this code over to using internal/errors.js anyway, we might as well do both at the same time.

@Trott
Copy link
Member

Trott commented Feb 10, 2017

@jasnell wrote:

If we were to do this

I don't think there's any question we should do this. There is no reason for writeInt8() to return RangeError: Index out of range but writeFloatLE() to return RangeError: out of range index in the code below.

let buf = Buffer.alloc(9);
try {
  buf.writeInt8(1, -1); 
} catch (e) {
  console.log(e.toString()); // RangeError: Index out of range
}
try {
  buf.writeFloatLE(1, -1); 
} catch (e) {
  console.log(e.toString()); // RangeError: out of range index
}

@jasnell continued:

...I'd prefer to make it part of the migration to using internal/errors.js

Would you be OK with that being a subsequent PR? I'd prefer we separate out changes to error messages from migrations to internal/error.js rather than bundling those adjacent-but-not-integrally-related things. I can make sure that second PR happens immediately after this one lands if that affects your spider-sense on this one.

@jasnell
Copy link
Member

jasnell commented Feb 10, 2017

As long as the two land closely together, or perhaps even as two commits in the same PR, that's fine.

Copy link
Member

@Trott Trott left a comment

Choose a reason for hiding this comment

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

LGTM if CI is green. Might be good to edit the commit message to make it clear that this is not changing an error message just to change it, but rather taking two error messages that are used in essentially identical situations and making them consistent.

@jasnell
Copy link
Member

jasnell commented Feb 10, 2017

I will clear my "changes requested" review once I get a chance to review the other PR.

@Trott
Copy link
Member

Trott commented Feb 14, 2017

@Trott
Copy link
Member

Trott commented Feb 17, 2017

@jasnell It looks like sometimes this RangeError is thrown in JS-land (in which case we can migrate to the internal error system) and sometimes it is thrown by C++ (in which case we cannot, right? Maybe I'm wrong?).

For the sake of consistency, I wonder if it makes sense to not migrate the JS-land errors to the new system until we can do errors thrown in C++ as well. This way, you don't get very dissimilar RangeError objects depending on whether you are trying to read a float or an int. What do you think?

@jasnell
Copy link
Member

jasnell commented Feb 17, 2017

We're always going to have at least some inconsistency given that errors thrown from V8 will not have this information and neither will userland code. I will work on providing an equivalent mechanism for the errors thrown from C/C++ but I think it makes sense (and is best) to incrementally move forward. If we try to hold off and do them all at once, we're going to end up with too much of a moving target (I've already tried it). I think userland will be willing to forgive some short term inconsistency if it means we're making progress towards something better.

invalidArgs[1] = -1;
assert.throws(
() => Buffer.alloc(9)[funx](...invalidArgs),
/^RangeError: (?:Index )?out of range(?: index)?$/
//(err) => err instanceof RangeError && err.message === "Index out of range"
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Should this commented-out code be removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, fixing it

@Trott
Copy link
Member

Trott commented Feb 19, 2017

@TimothyGu
Copy link
Member

There was a force push right after last CI, so it didn't go through.

New CI: https://ci.nodejs.org/job/node-test-pull-request/6519/

@TimothyGu
Copy link
Member

Heh, seems like there is a merge conflict: https://ci.nodejs.org/job/node-test-commit/8037/console

@larissayvette can you git rebase master?

@larissayvette
Copy link
Contributor Author

@TimothyGu alright

@TimothyGu
Copy link
Member

TimothyGu commented Feb 21, 2017

Seems to be working now: https://ci.nodejs.org/job/node-test-pull-request/6521/

@Trott
Copy link
Member

Trott commented Feb 21, 2017

Tests are passing (hooray!) but linter is failing because a number of lines in test-buffer-fill.js are longer than 80 characters.

Easiest solution is probably to change things like this:

assert.throws(() =>
             Buffer.allocUnsafe(8).fill('a', -1),
              /^RangeError(?:\[ERR_INDEX_OUT_OF_RANGE\])?: Index out of range$/);

...to something more like this:

assert.throws(
  () => Buffer.allocUnsafe(8).fill('a', -1),
  /^RangeError(?:\[ERR_INDEX_OUT_OF_RANGE\])?: Index out of range$/
);

@jasnell
Copy link
Member

jasnell commented Feb 21, 2017

Is there a problem using common.expectsError()?

@larissayvette
Copy link
Contributor Author

@jasnell when common.expectsError() is used it produces this error
'undefined === ERR_INDEX_OUT_OF_RANGE`

@larissayvette
Copy link
Contributor Author

@Trott I think the lint is correct now

@larissayvette larissayvette changed the title lib/src: refactoring out of range index lib/src: refactoring out of range index and and migrate error for consistency Feb 21, 2017
@Trott
Copy link
Member

Trott commented Feb 21, 2017

On closer inspection, @jasnell is right to suggest that the regular expression is unnecessary in most of the places it's used here. It only needs to be used in places where the throw might be C++ or JS. That's probably only going to happen in test-buffer-write-noassert and/or test-buffer-read.

@@ -956,7 +956,7 @@ assert.throws(() => {
const a = Buffer.alloc(1);
const b = Buffer.alloc(1);
a.copy(b, 0, 0x100000000, 0x100000001);
}, /out of range index/);
}, /^RangeError(?:\[ERR_INDEX_OUT_OF_RANGE\])?: Index out of range$/);
Copy link
Member

Choose a reason for hiding this comment

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

This regexp can be replaced with common.expectsError(undefined, RangeError, 'Index out of range')

@@ -57,7 +57,7 @@ assert.strictEqual(1, a.compare(b, Infinity, -Infinity));
// zero length target because default for targetEnd <= targetSource
assert.strictEqual(1, a.compare(b, '0xff'));

const oor = /out of range index/;
const oor = /^RangeError(?:\[ERR_INDEX_OUT_OF_RANGE\])?: Index out of range$/;
Copy link
Member

Choose a reason for hiding this comment

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

This line can be replaced with:

const oor = common.expectsError('ERR_INDEX_OUT_OF_RANGE');

Will need to change the require('../common'); to const common = require('../common'); too.

buf1.fill('a', 0, 0, 'foo'), /^TypeError: Unknown encoding: foo$/);
assert.throws(
() => buf1.fill(0, -1),
/^RangeError(?:\[ERR_INDEX_OUT_OF_RANGE\])?: Index out of range$/);
Copy link
Member

Choose a reason for hiding this comment

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

This one (and likely the others in this file) can be replaced with common.expectsError('ERR_INDEX_OUT_OF_RANGE'));. (Will need to add const common = require... at top of file.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

From line 187 to 196 , test passes only when it is regular expression. and for the subsequent error message it is common.expectsError(undefined, RangeError, 'Index out of range')); that works and not common.expectsError('ERR_INDEX_OUT_OF_RANGE'));

{
const invalidArgs = Array.from(args);
invalidArgs[1] = -1;
assert.throws(
() => Buffer.alloc(9)[funx](...invalidArgs),
/^RangeError: (?:Index )?out of range(?: index)?$/
/^RangeError(?:\[ERR_INDEX_OUT_OF_RANGE\])?: Index out of range$/
Copy link
Member

Choose a reason for hiding this comment

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

I think this is the only regexp that should stay because we don't know ahead of time whether the error is coming from JS (if writing an int, for example) or C++ (if writing a float, for example).

Copy link
Member

Choose a reason for hiding this comment

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

+1... we should probably look at fixing up common.expectsError() to account for this, however. Will have to think on that for a bit

Copy link
Member

@Trott Trott Feb 22, 2017

Choose a reason for hiding this comment

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

@jasnell If we really wanted to use common.expectsError() and not a RegExp, we could do something like the following (untested) code:

let expectedError;
if (['writeFloatBE', 'writeFloatLE'].includes(funx)) {
  expectedError = common.expectsError(undefined, RangeError, 'Index out of range');
} else {
  expectedError = common.expectsError('ERR_INDEX_OUT_OF_RANGE', RangeError, 'Index out of range');
}
assert.throws(() => { Buffer.alloc(9)[funx](...invalidArgs) }, expectedError);

Definitely more complicated, though, so I'm fine with the RegExp approach. Either way.

Copy link
Member

Choose a reason for hiding this comment

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

bleh... let's keep it with the regex for now

@jasnell
Copy link
Member

jasnell commented Feb 22, 2017

Keep in mind also that eventually, even the C/C++ errors thrown by Node.js will include these codes and the augmented name. We'll still need to be careful about errors thrown by V8 or Chakra

Copy link
Member

@jasnell jasnell left a comment

Choose a reason for hiding this comment

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

Generally LGTM with some nits and @Trott's comments addressed. Thank you for this!

<a id="ERR_INDEX_OUT_OF_RANGE"></a>
### ERR_INDEX_OUT_OF_RANGE

The `'ERR_INDEX_OUT_OF_RANGE'` error code is used to alert user that index parsed is out of range
Copy link
Member

Choose a reason for hiding this comment

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

I would suggest rewording just a bit here...

The `'ERR_INDEX_OUT_OF_RANGE'` error code is used when a given index is out of the
accepted range.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will make the change

/^RangeError(?:\[ERR_INDEX_OUT_OF_RANGE\])?: Index out of range$/);
assert.throws(
() => buf1.fill(0, 0, buf1.length + 1),
/^RangeError(?:\[ERR_INDEX_OUT_OF_RANGE\])?: Index out of range$/);
Copy link
Member

Choose a reason for hiding this comment

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

For the cases where the regex must still be provided, it would be easiest to change later on if the regex was defined once and assigned then used multiple times... e.g.

const errRegex = /^RangeError .../;
assert.throws(() => { /* ... */ }, errRegex);
assert.throws(() => { /* ... */ }, errRegex);

const assert = require('assert');
const errors = require('../../lib/internal/errors');
Copy link
Member

Choose a reason for hiding this comment

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

Can this be require('internal/errors'); along with a // Flags: --expose-internals comment at the top of the file instead?

/^TypeError: encoding must be a string$/);
assert.throws(() =>
buf1.fill('a', 0, 0, 'foo'), /^TypeError: Unknown encoding: foo$/);
const errRegex =
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we need errRegex in this file. I think all instances of it can be replaced with common.expectsError().

const assert = require('assert');
const errors = require('../../lib/internal/errors');// Flags: --expose-internals
Copy link
Member

Choose a reason for hiding this comment

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

I think the comment needs to be the first line of the file. The idea of using it is to be able to change ../../lib/internal/errors on this line to internal/errors. This way, the test runs based on version of node that is compiled and not the contents of lib in the source tree at the time the test is run.

Copy link
Member

Choose a reason for hiding this comment

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

(Also, it means that in order to run the test from the command line, you need to either use tools/test.py or else pass the --expose-internals flag to node. But lots of tests are like that already, so that should be fine.)

Copy link
Member

Choose a reason for hiding this comment

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

Indeed. The // Flags: --expose-internals should be the first line of the test file,
The import must also be require('internal/errors'), without the ../../lib/ part.

@Trott
Copy link
Member

Trott commented Mar 3, 2017

@jasnell Two things: Can you give this another look since it has changed a bit? Also, are we not landing these new internal error things on master yet? What needs to happen before we can start doing that?

@Trott
Copy link
Member

Trott commented Mar 3, 2017

Oh, it looks like this needs to be updated for the new common.expectsError() signature.

Instead of common.expectsError(code, type, msg), it's now common.expectsError({code, type, message: msg}).

@Trott
Copy link
Member

Trott commented Mar 7, 2017

@Trott
Copy link
Member

Trott commented May 28, 2017

Rebased and force pushed.

CI: https://ci.nodejs.org/job/node-test-pull-request/8344/

@Trott
Copy link
Member

Trott commented May 28, 2017

Landed in 234353a.
Thanks for the contribution, @larissayvette! 🎉

@Trott Trott closed this May 28, 2017
Trott pushed a commit that referenced this pull request May 28, 2017
PR-URL: #11296
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
buffer Issues and PRs related to the buffer subsystem. c++ Issues and PRs that require attention from people who are familiar with C++. semver-major PRs that contain breaking changes and should be released in the next major version.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants