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

Migrate process errors from C++ to JS #19973

Closed
wants to merge 3 commits into from

Conversation

targos
Copy link
Member

@targos targos commented Apr 12, 2018

commit 2b3b84822baa258fe67f17979b17fac523455612

lib: introduce internal/validators

Create a file to centralize argument validators that are used in
multiple internal modules.
Move validateInt32 and validateUint32 to this file.

commit aeb33c9b0d4be6693d7eda0810b4c3f4d213d743

process: migrate methods to throw errors with code

Migrate some methods from node.cc to JS in order to properly throw errors
with codes.
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

@targos targos added semver-major PRs that contain breaking changes and should be released in the next major version. errors Issues and PRs related to JavaScript errors originated in Node.js core. labels Apr 12, 2018
@targos targos added this to the 10.0.0 milestone Apr 12, 2018
@targos targos requested review from joyeecheung, BridgeAR and a team April 12, 2018 12:24
@nodejs-github-bot nodejs-github-bot added the lib / src Issues and PRs related to general changes in the lib or src directory. label Apr 12, 2018
@targos targos added the process Issues and PRs related to the process subsystem. label Apr 12, 2018
Copy link
Member

@BridgeAR BridgeAR left a comment

Choose a reason for hiding this comment

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

Just had a brief glance at it and it looks very good. I am going to have a closer look at it later on again to give a LG.

This does conflict with at least one other PR at the moment though that I expect to land earlier. Just as a heads up.

src/node.cc Outdated
if (!args[1]->IsUint32() && !args[1]->IsString()) {
return env->ThrowTypeError("argument 2 must be a number or a string");
}
CHECK_EQ(args.Length(), 1);
Copy link
Member

Choose a reason for hiding this comment

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

Should this not be 2?

Copy link
Member Author

Choose a reason for hiding this comment

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

Right. That means we don't have any test for this function 😱

@@ -837,6 +837,7 @@ E('ERR_INVALID_SYNC_FORK_INPUT',
TypeError);
E('ERR_INVALID_THIS', 'Value of "this" must be of type %s', TypeError);
E('ERR_INVALID_TUPLE', '%s must be an iterable %s tuple', TypeError);
E('ERR_INVALID_UNIX_ID', 'Unix %s id does not exist: %s', Error);
Copy link
Member

Choose a reason for hiding this comment

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

ERR_INVALID_CREDENTIAL/ERR_UNKNOWN_CREDENTIAL? The error message is unambiguous, but it’s not really clear what’s meant by “Unix id” from just seeing the error code, imo

Copy link
Member Author

Choose a reason for hiding this comment

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

What about ERR_UNKNOWN_IDENTIFIER ?

Copy link
Member Author

Choose a reason for hiding this comment

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

And changing the message to "Group/User identifier does not exist: %s" ?

Copy link
Member

Choose a reason for hiding this comment

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

@targos I think that might be a bit generic, especially since “unknown identifier” is a term that has a different meaning in a programming language context

I’m fine with changing the error message to what you’re suggesting, but I’m also fine with it the way it is right now :)

Copy link
Member

Choose a reason for hiding this comment

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

ERR_INVALID_CREDENTIAL sounds better to me since you'd do man credentials to read the manual on this...or maybe ERR_INVALID_PRIVILEGES?

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh, I changed it to ERR_INVALID_CREDENTIAL but forgot to push. Done now.

validateId(user, 'user');
validateId(extraGroup, 'extraGroup');
const result = _initgroups(user, extraGroup);
if (result === 1) {
Copy link
Member

Choose a reason for hiding this comment

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

This seems rather magical, can you put in a comment around these return values or use some kind of constants shared by both sides?

src/node.cc Outdated
oct += c - '0';
}
}
int oct = args[0]->Uint32Value();;
Copy link
Member

Choose a reason for hiding this comment

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

Can you use the .As<Uint32>()->Value() cast here instead of the deprecated API?

@@ -837,6 +837,7 @@ E('ERR_INVALID_SYNC_FORK_INPUT',
TypeError);
E('ERR_INVALID_THIS', 'Value of "this" must be of type %s', TypeError);
E('ERR_INVALID_TUPLE', '%s must be an iterable %s tuple', TypeError);
E('ERR_INVALID_UNIX_ID', 'Unix %s id does not exist: %s', Error);
Copy link
Member

Choose a reason for hiding this comment

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

ERR_INVALID_CREDENTIAL sounds better to me since you'd do man credentials to read the manual on this...or maybe ERR_INVALID_PRIVILEGES?

assert.throws(() => {
process.umask(value);
}, {
code: 'ERR_INVALID_ARG_TYPE',
Copy link
Member

Choose a reason for hiding this comment

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

This error message reads somewhat odd to me. Shouldn't this be a ERR_INVALID_ARG_VALUE?

@BridgeAR BridgeAR self-requested a review April 13, 2018 19:17
@jasnell
Copy link
Member

jasnell commented Apr 14, 2018

@targos ... this needs a quick rebase if you would.

@targos targos force-pushed the error-codes-nodecc branch from be87ffb to 905e138 Compare April 15, 2018 09:19
@targos
Copy link
Member Author

targos commented Apr 15, 2018

Updated!

@joyeecheung I think I addressed all your comments.

@targos targos force-pushed the error-codes-nodecc branch from 905e138 to d7bd31d Compare April 15, 2018 10:06
Copy link
Member

@BridgeAR BridgeAR left a comment

Choose a reason for hiding this comment

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

LGTM, just a few nits / suggestions.


function setgroups(groups) {
if (!Array.isArray(groups)) {
throw new ERR_INVALID_ARG_TYPE('groups', 'array', groups);
Copy link
Member

Choose a reason for hiding this comment

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

It should be Array

function umask(mask) {
if (typeof mask === 'undefined') {
return _umask(mask);
} else if (typeof mask === 'number') {
Copy link
Member

Choose a reason for hiding this comment

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

Nit: do not use the else in case it is not required due to the return.

@@ -0,0 +1,58 @@
'use strict';

Copy link
Member

Choose a reason for hiding this comment

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

I like the consolidation of validation functions!

// 2 ** 31 === 2147483648
err = new ERR_OUT_OF_RANGE(name, '> -2147483649 && < 2147483648', value);
}
Error.captureStackTrace(err, validateInt32);
Copy link
Member

Choose a reason for hiding this comment

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

Suggestion: I think it is best to actually keep the internal structure visible in most cases. So I personally would not remove this frame. The same applies to the other validation functions.

Copy link
Member Author

Choose a reason for hiding this comment

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

I prefer not to change this in this PR.

} else if (!Number.isInteger(value)) {
err = new ERR_OUT_OF_RANGE(name, 'an integer', value);
} else {
const min = positive ? 1 : 0;
Copy link
Member

Choose a reason for hiding this comment

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

Suggestion: it is not much overhead but the default argument is slower than not having a default in all cases. The check itself could stay as it is, since it already tests for truthy values only.

Copy link
Member Author

Choose a reason for hiding this comment

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

I removed the default value

err = new ERR_OUT_OF_RANGE(name, 'an integer', value);
} else {
// 2 ** 31 === 2147483648
err = new ERR_OUT_OF_RANGE(name, '> -2147483649 && < 2147483648', value);
Copy link
Member

Choose a reason for hiding this comment

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

Just as a note: as far as I know, this function should actually check >= 0 && ... in all call cases. So we might want to rename the function / add a new one but this does not have to be in this PR.

src/node.cc Outdated
oct += c - '0';
}
}
int oct = args[0].As<Uint32>()->Value();;
Copy link
Member

Choose a reason for hiding this comment

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

Redundant semicolon.

message: /The "directory" argument must be of type string/
};
common.expectsError(function() { process.chdir({}); }, err);
common.expectsError(function() { process.chdir(); }, err);
Copy link
Member

Choose a reason for hiding this comment

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

Please keep the third test but just change it to: process.chdir('x', 'y'); // Should not throw. That makes sure we have no regression in case the function is changed at any point in time.

Besides that it would also be good to keep assert.throws as it has a much nicer error output and is otherwise almost identical.

Copy link
Member Author

Choose a reason for hiding this comment

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

Please keep the third test but just change it to: process.chdir('x', 'y'); // Should not throw. That makes sure we have no regression in case the function is changed at any point in time.

I don't think we need this test. I removed the check because in other functions we never throw when too many arguments are passed.

Besides that it would also be good to keep assert.throws as it has a much nicer error output and is otherwise almost identical.

I know our opinions differ here, but I really like to be able to pass a RegExp. I don't have to put the exact string, so:

  • I don't need to make the test fail to know exactly what to put for the message.
  • I can use one object validator for multiple test cases (that's what I did here, as error the message is not exactly the same for those cases).
  • Message validator is shorter, so less likely to go over 80 chars and also less like to have to be updated in case we change something in the message format.

@@ -43,8 +43,17 @@ assert.strictEqual(old, process.umask());

Copy link
Member

Choose a reason for hiding this comment

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

Would you be so kind and rename this test file to test-process-umask.js?

Copy link
Member Author

Choose a reason for hiding this comment

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

done

@targos targos force-pushed the error-codes-nodecc branch from d7bd31d to 29f1182 Compare April 17, 2018 07:53
@targos
Copy link
Member Author

targos commented Apr 17, 2018

@targos
Copy link
Member Author

targos commented Apr 18, 2018

@nodejs/tsc this needs more TSC reviews, please.

@jasnell
Copy link
Member

jasnell commented Apr 18, 2018

I'm going to take this off the 10.0.0 milestone. It would need to land today and would need explicit @nodejs/tsc approval (not just no-objections) in order to make it in to 10.0.0 as I'm going to be cutting the release candidate build tomorrow.

@jasnell jasnell removed this from the 10.0.0 milestone Apr 18, 2018
targos added 3 commits April 19, 2018 09:53
Create a file to centralize argument validators that are used in
multiple internal modules.
Move validateInt32 and validateUint32 to this file.
Migrate some methods from node.cc to JS in order to properly throw errors
with codes.
@targos targos force-pushed the error-codes-nodecc branch from 29f1182 to 6e43560 Compare April 19, 2018 08:11
@targos
Copy link
Member Author

targos commented Apr 19, 2018

Hopefully fixed the Windows errors by defining POSIX-specific methods only if they exist originally on the process object.

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

'must be an octal string');
}
const octal = Number.parseInt(mask, 8);
validateUint32(octal, 'mask');
Copy link
Member

@BridgeAR BridgeAR Apr 23, 2018

Choose a reason for hiding this comment

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

Suggestion: This is actually only testing for <= 2 ** 32. All other checks are already done due to the RegExp above.
I personally would either remove the extra check or make the whole function stricter to only accept valid entries.
The reason validateUint32 is used for numbers is only about making sure no negative numbers got passed in (as far as I know).

Copy link
Member Author

Choose a reason for hiding this comment

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

Do you know what is the largest allowed value?

Copy link
Member

@joyeecheung joyeecheung Apr 23, 2018

Choose a reason for hiding this comment

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

@targos In some fs methods we throw if the mode is larger than 0o777. It is not strictly documented wether we mask off or throw if the mode_t type of value passed to any API is larger than that though.

Copy link
Member Author

Choose a reason for hiding this comment

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

What happens in the system if the value is larger than 0o777 ?
For example if you pass 0o12345 ? is it truncated to 0o345 ?

Copy link
Member

Choose a reason for hiding this comment

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

Suggestion: add a TODO to investigate and land the PR. In fs there are a couple of validations that need improvements, especially having more consistent checks. I am also working on some of those.

Copy link
Member

Choose a reason for hiding this comment

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

@targos Yes it gets truncated/masked. http://man7.org/linux/man-pages/man2/umask.2.html

       umask() sets the calling process's file mode creation mask (umask) to
       mask & 0777 (i.e., only the file permission bits of mask are used),
       and returns the previous value of the mask.

@BridgeAR BridgeAR added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Apr 23, 2018
@nodejs nodejs deleted a comment Apr 23, 2018
@BridgeAR
Copy link
Member

Landed in e836128 and 2fd248f 🎉

I took the freedom to ignore my own comment.

@BridgeAR BridgeAR closed this Apr 26, 2018
BridgeAR pushed a commit to BridgeAR/node that referenced this pull request Apr 26, 2018
Create a file to centralize argument validators that are used in
multiple internal modules.
Move validateInt32 and validateUint32 to this file.

PR-URL: nodejs#19973
Reviewed-By: Joyee Cheung <[email protected]>
Reviewed-By: Ruben Bridgewater <[email protected]>
Reviewed-By: James M Snell <[email protected]>
BridgeAR pushed a commit to BridgeAR/node that referenced this pull request Apr 26, 2018
Migrate some methods from node.cc to JS in order to properly throw
errors with codes.

PR-URL: nodejs#19973
Reviewed-By: Joyee Cheung <[email protected]>
Reviewed-By: Ruben Bridgewater <[email protected]>
Reviewed-By: James M Snell <[email protected]>
@targos targos deleted the error-codes-nodecc branch April 26, 2018 20:48
targos added a commit to targos/node that referenced this pull request Jun 6, 2018
To ease future backports, create the process/methods file introduced in
nodejs#19973. This commit just adds
the JS functions that forward calls to C++ and does not change type
checking.
targos added a commit that referenced this pull request Jun 7, 2018
To ease future backports, create the process/methods file introduced in
#19973. This commit just adds
the JS functions that forward calls to C++ and does not change type
checking.

PR-URL: #21172
Reviewed-By: Joyee Cheung <[email protected]>
targos added a commit that referenced this pull request Jun 13, 2018
To ease future backports, create the process/methods file introduced in
#19973. This commit just adds
the JS functions that forward calls to C++ and does not change type
checking.

PR-URL: #21172
Reviewed-By: Joyee Cheung <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
author ready PRs that have at least one approval, no pending requests for changes, and a CI started. errors Issues and PRs related to JavaScript errors originated in Node.js core. lib / src Issues and PRs related to general changes in the lib or src directory. process Issues and PRs related to the process subsystem. 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.

6 participants