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

fs: fix incorrect error message in fs.open family when flags are not string or int #2873

Closed
wants to merge 1 commit into from

Conversation

ronkorving
Copy link
Contributor

This fixes #2871

}

cb(err);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note: for async functions this now passes the error to a callback.

@brendanashworth brendanashworth added the fs Issues and PRs related to the fs subsystem / file system. label Sep 15, 2015
@@ -537,8 +561,13 @@ fs.open = function(path, flags, mode, callback_) {
var req = new FSReqWrap();
req.oncomplete = callback;

var intFlags = stringToFlags(flags);
if (intFlags === undefined) {

Choose a reason for hiding this comment

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

unless flag could ever be zero, i'd just go

if (!intFlags) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To be honest, I'm not sure if it could ever be zero... I could also do a Number.isInteger test, but I figured this would execute faster.

@bnoordhuis
Copy link
Member

To stop application bugs from propagating, the rule of thumb has always been "throw on logic error, callback on run-time error." Passing in bad flags or options is a logic error and should fail synchronously.

@reqshark
Copy link

in that case just change an int to a string here: https://github.com/nodejs/node/blob/master/src/node_file.cc#L844

@ronkorving
Copy link
Contributor Author

@bnoordhuis I was not familiar with that philosophy.
@reqshark that won't do, because int is expected and accepted even in JS-land (where both int and string are fine).

@reqshark
Copy link

Ok don't change the text there then.

bad flags or options is a logic error and should fail synchronously

.. sounds to me like return an err before even var callback = makeCallback( gets started

fs.open = function(path, flags, mode, callback_) {
  var callback = makeCallback(arguments[arguments.length - 1]);
  mode = modeNum(mode, 0o666);

  if (!nullCheck(path, callback)) return;

  var req = new FSReqWrap();
  req.oncomplete = callback;

  var intFlags = stringToFlags(flags);
  if (intFlags === undefined) {
    return throwFlagsError(flags, callback);
  }

  binding.open(pathModule._makeLong(path),
               intFlags,
               mode,
               req);
};

@ronkorving
Copy link
Contributor Author

I'll update this PR soon.

@ronkorving
Copy link
Contributor Author

@bnoordhuis now it sticks to what you explained.

if (!(err instanceof TypeError)) {
throw new Error('unexpected error: ' + err);
}
}
Copy link
Member

Choose a reason for hiding this comment

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

You can replace the try/catch block with an assert.throws(...)

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 wanted to make sure it's a TypeError and not the Error of "bad flag value" (which can also happen when it's a string, just not the right one).

Copy link
Contributor

Choose a reason for hiding this comment

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

You can still do that like this :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Understood. If you insist, I'll change it, but I would rather stick to a class check here, to allow other contributions to Node to polish error messages when needed without breaking tests left and right. imho tests shouldn't check for the human readable output when avoidable, and in this case it is avoidable.

Copy link
Contributor

Choose a reason for hiding this comment

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

I second brendan's suggestion. What if TypeError is thrown because of some other problem? I prefer checking the actual message.

I would say, when the mesaages are updated, let the tests also be updated.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Understood. I'll change it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed (I hope :))

@bnoordhuis
Copy link
Member

Basic premise LGTM.

@ronkorving ronkorving changed the title fs: ensure async API doesn't throw on bad flags or options fs: fix incorrect error message in fs.open family when flags are not string or int Sep 15, 2015
@ronkorving ronkorving force-pushed the fix-flags branch 2 times, most recently from d405e03 to 163b012 Compare September 16, 2015 04:02
@@ -32,8 +32,21 @@ fs.open(__filename, 'rs', function(err, fd) {
openFd2 = fd;
});

assert.throws(function() {
fs.open('/path/to/file/that/does/not/exist', { bad: 'flags' }, function() {
Copy link
Contributor

Choose a reason for hiding this comment

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

We can simply this a little bit,

fs.open('/path/to/file/that/does/not/exist', { bad: 'flags' }, assert.fail);

@thefourtheye
Copy link
Contributor

LGTM, with take-it or leave-it suggetions

Before, it would show "TypeError: flags must be an int", which
in native Node is technically correct. But in JS they may be
(and usually are) strings.

Fixes nodejs#2871
@ronkorving
Copy link
Contributor Author

I liked your suggestions :)

@thefourtheye
Copy link
Contributor

@ronkorving Glad you liked them :-) Let's give this some time so that we can get more reviewers see this (hopefully ;-)).

if (err instanceof TypeError) {
return err.message === 'File open flags may only be string or integer';
}
});
Copy link
Member

Choose a reason for hiding this comment

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

You can condense this to assert.throws(function() { ... }, /File open flags may only be string or integer/).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Then I wouldn't be checking that it's a TypeError. That's not a problem?

@bnoordhuis
Copy link
Member

LGTM

assert.throws(function() {
fs.open('/path/to/file/that/does/not/exist', { bad: 'flags' }, assert.fail);
}, function(err) {
if (err instanceof TypeError) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think you want to check if the error is a TypeError. That should be unconditional, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well I wrote the error as a TypeError since its thrown on bad types only, so might as well test for it, no?

Copy link
Contributor

Choose a reason for hiding this comment

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

Let me rephrase. You should assert that the error is a TypeError, not put it in an if statement.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please see my comment below to @bnoordhuis. I can't seem to satisfy both your requests.

Copy link
Contributor

Choose a reason for hiding this comment

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

Do what @bnoordhuis is asking for :-)

@cjihrig
Copy link
Contributor

cjihrig commented Sep 16, 2015

LGTM with one comment.

return flag;
}

// We only allow strings from here on
if (typeof flag !== 'string') {
throw new TypeError('File open flags may only be string or integer');
Copy link
Contributor

Choose a reason for hiding this comment

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

Anyone who sees this message and looks at the docs would immediately report think this a bug, since it conflicts with the docs. If you want to say that, I suggest documenting the behaviour.

I strongly suspect that the fact that flags can be an int is historical, kept for backwards compat from the v0.small days, and should be deprecated, but its hard to deprecate a piece of an API.

// Only mess with strings
if (typeof flag !== 'string') {
// Integers are already good to go
if (Number.isInteger(flag)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This check doesn't work as you think it does: the C++ layer requires flags to fit within 32 bits, this check here only requires it to be integral. Try with 0xfffffffff, and you will see.

That's why in my PR I pass anything with typeof number to the C++ layer, it seems easier to let it do this check, because it does it correctly. Though you can put more code in here, checking the bounds futher.

@ronkorving
Copy link
Contributor Author

Closing in favor of #2902

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
fs Issues and PRs related to the fs subsystem / file system.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

fs: bad error message in fs.openSync
7 participants