-
Notifications
You must be signed in to change notification settings - Fork 29.6k
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
fs: improve argument handling for ReadStream #19898
Conversation
P.S. Don't run the tests yet, they'd probably need to be changed as this should probably be Just take a look at the code and let me know what you think about it (for now). |
@nodejs/fs |
lib/fs.js
Outdated
|
||
// Case 4: If either start or end is fractional, round them to the nearest | ||
// whole number. (Not integer as negatives not allowed) | ||
if (!Number.isInteger(this.start)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that we are doing much stricter checks, isSafeInteger
would be better I guess.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@thefourtheye I think that does not apply here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@thefourtheye I think that does not too.
Is there a reason to make |
lib/fs.js
Outdated
|
||
// Case 4: If either start or end is fractional, round them to the nearest | ||
// whole number. (Not integer as negatives not allowed) | ||
if (!Number.isInteger(this.start)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@thefourtheye I think that does not apply here.
@tniessen I think you misread the change. As far as I see it, your suggestion is already the case in this PR. |
@ryzokuken What's the reasoning behind rounding floats? To me, a floating point number implies the user is not passing what they intended to pass. Rather than rounding it (ie: magic), I personally would prefer it to throw. |
@ronkorving I had something similar in mind, but Anna convinced me otherwise and with good reason. A lot of people do a ton of math to get the values of Quoting @addaleax here:
For a better insight into our conversations and how we came to this final decision, read the thread at the reference PR (#19732). |
I haven't reviewed the code because I am a little busy these days. Would you like to write/quote the reason of using |
I am wondering if |
@anliting No idea. We could test for that, though. I'll be adding/updating unit tests for |
I mean, you program seems to allow passing |
Added new tests and updated existing ones, take a look when you can. The tests might look a little too much, but better be safe than be sorry, I guess. |
I tried running tests locally, and none of my new tests seemed to fail (which sounds good), but a seemingly related failed. Could someone help me debug it?
|
lib/fs.js
Outdated
} | ||
// Case 0: If either start or end is undefined, set them to defaults | ||
// (0, Infinity) respectively. | ||
this.start = this.start === undefined ? 0 : this.start; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m mostly sure this is the cause of the test failure – start: 0
and start: undefined
aren’t equivalent, because the former means that the stream will use reads with an offset
parameter (which doesn’t work on all types of files). If start: undefined
was passed, this value should be undefined
, too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, let me just remove the condition then.
@addaleax better now? Running tests locally. Please run CI whenever you can. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, assuming the start
-related checks are grouped in a block that ignores start === undefined
(or something equivalent to that)
lib/fs.js
Outdated
} | ||
// Case 1: If the type of either start or end is not number, throw | ||
// ERR_INVALID_ARG_TYPE (TypeError). | ||
if (typeof this.start !== 'number') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you want to allow undefined
explicitly here and for the other checks as well?
(The if (this.start !== undefined) {
block from the original variant of this code seems like a pretty reasonable idea, tbh…)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay. So, should I enclose all the type-checking code within that statement?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ryzokuken All that’s related to start
, yes. end
is another story, and my original TODO
was more or less about keeping the end
checks out of such a block :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly. That's why I wanted to keep the checks separate, so they worked either way.
I've enclosed all this code into the if
block, which should work if end
does not need to be transformed or checked at all if start
is undefined
. That probably isn't the case, is it?
P.S. How should I handle end
if start
is undefined
? Similar to how it was handled earlier?
Edit: Sorry, misunderstood you. Working on it. Hopefully, it will work now.
Umm, okay. So a couple of other The arguments passed don't qualify by the newer standards, I guess. |
@addaleax How about this instead? |
lib/fs.js
Outdated
} | ||
if (!Number.isInteger(this.end)) { | ||
this.end = Math.round(this.end); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we would want to do the type checking for end
before we get to this block? Otherwise end: undefined
would get converted to Math.round(undefined)
here, which is NaN
… if I read the code correctly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Holy moly. I missed this one and got a few failing tests for NaN
. Stupid me, I guess. Thanks for pointing this out.
lib/fs.js
Outdated
} | ||
|
||
// Case 4: If start is fractional, round them to the nearest whole number. | ||
// (Not integer as negatives not allowed) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m a bit confused by the comment here – we do round to the nearest integer, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do. But because we've already throw
n on negatives, we're only left with positive fractions, which will either round to zero or positive integers (whole numbers). I used this term because it's more precise (factually).
If you want, I could remove it and just say that we're rounding to the nearest Integer.
@addaleax hopefully, it should work perfectly now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! :)
lib/fs.js
Outdated
} | ||
|
||
// Case 5: If start is a whole number, work perfectly. | ||
|
||
// Case 6: If start is greater than end, throw ERR_OUT_OF_RANGE (RangeError) | ||
if (this.start > this.end) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this works fine, but as far as I can tell that is because end: undefined
gets coerced to NaN
here (which makes this condition always false), and that the fact that we later round end
does not affect this comparison
Maybe it would be a bit more obvious that this works if we swap type checking for end
and start
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Umm, I couldn't understand what you meant. Should I move the end
block to check first and then check for start
?
end
isn't being coerced to NaN
before this anymore, but end
does get rounded off later on in the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ryzokuken What I meant was, end
is being coerced to NaN
in this comparison if it was passed in as undefined
, because we haven’t yet gotten to the part where it’s converted to Infinity
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh. so, should I move the block that handles this.end === undefined
first and move to a similar type-check below?
Let me make it and demonstrate.
Okay, looks like the tests passed for me this time around, so that's probably a good sign. |
@addaleax it does feel much better now, doesn't it? Definitely more consistent ( |
@BridgeAR you're right, only two files had an hardcoded check for that test and I had also updated one. Running tests in a jiffy. |
Wait, the lint errors are still here. |
Broke down the three lines that were failing on us, all tests should pass now. Waiting for tests to pass locally first this time. |
Okay, tests pass. |
Seems to be an unrelated error with https://ci.nodejs.org/job/node-test-binary-windows/17146/, rerunning. |
Binary Windows Test: https://ci.nodejs.org/job/node-test-binary-windows/17161/ |
Another Java error: https://ci.nodejs.org/job/node-test-commit-freebsd/17632/nodes=freebsd10-64/console Aren't we having too many of these lately? |
Optimistic CI run: https://ci.nodejs.org/job/node-test-pull-request/14780/ |
@ryzokuken the CI does not have to be "green green". All tests related to this PR pass and what you encounter otherwise are flakes. Some are infrastructure issues and some times some tests are not written well enough. That is normal but it does not mean this PR needs any more work. It can land as it is. |
@BridgeAR so, should I land it? Any CITGM or benchmark runs required? |
@ryzokuken running CITGM for semver-majors is always best to detect failures early that have to be fixed. I doubt that it is going to be an issue in this case but here they are nevertheless: CITGM https://ci.nodejs.org/view/Node.js-citgm/job/citgm-smoker/1404/ When they come out "green" (there will be quite a few failures and therefore it is easier to compare the output of those two. If they diverge, check if it has something to do with this PR) you should feel free to land the PR. |
@BridgeAR sounds great! Thanks. |
@BridgeAR not the exact same, but mine had a few less failures? (59 vs 66). Also, a single less skipped test. I hope it's okay to land? |
Landed in 19374fd 🎉 |
Improve handling of erratic arguments in fs.ReadStream Refs: #19732 PR-URL: #19898 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Ron Korving <ron@ronkorving.nl> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Improve handling of erratic arguments in fs.ReadStream
Refs: #19732
For the uninitiated, a summary of what I'm planning to change:
undefined
Infinity
)Infinity
)Number
ERR_INVALID_ARG_TYPE
ERR_INVALID_ARG_TYPE
NaN
ERR_INVALID_ARG_TYPE
ERR_OUT_OF_RANGE
Number
sERR_OUT_OF_RANGE
Number
sMath.round
ERR_OUT_OF_RANGE
ERR_OUT_OF_RANGE
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passescc @addaleax @anliting @nodejs/fs