-
Notifications
You must be signed in to change notification settings - Fork 30k
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
src: refactor and harden ProcessEmitWarning()
#17420
Conversation
|
||
// MakeCallback() unneeded because emitWarning is internal code, it calls | ||
// process.emit('warning', ...), but does so on the nextTick. | ||
if (emit_warning.As<Function>()->Call(env->context(), |
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.
If process.emitWarning
is supposed to be overwritable, as your test says. Then we should use MakeCallback
, 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.
Mh … I am not sure. There is usually always a JS call stack below, 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.
Well, that won't be true for the domain
MakeCallback
deprecation. Anything could call MakeCallback
in that case.
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.
Hm. True. But that would put us in a pretty difficult position if process.domain
happens to be set in that case (by accident), since process
would be the resource object?
So, I am okay with changing this but I’m not sure where we’d get the async context to use for MakeCallback
?
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.
So, I am okay with changing this but I’m not sure where we’d get the async context to use for MakeCallback?
Yes. This is a general problem. We have process.on('beforeExit')
, process.on('exit')
without context as well. In this case I think there are two options:
- Have a general
async_context
for theprocess
, and use that. - Create a new
async_context
withasync_context_.async_id
as thetriggerAsyncId
.
The latter option is better for this case but I don't see it working in general for all ProcessEmitWarningGeneric
use cases.
src/node.cc
Outdated
if (type != nullptr) { | ||
if (!String::NewFromOneByte(env->isolate(), | ||
reinterpret_cast<const uint8_t*>(type), | ||
v8::NewStringType::kInternalized) |
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.
Question: what is an internalized string. Looking it up, I get something about string interning but that doesn't make much sense in this context.
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.
It is basically what string interning refers to. As far as I know, what it means is that if V8 already has a copy of that same string, it deduplicates them and returns the existing one to the caller; And if not, the string is created in the “old space” part of the heap, that is, not expected to be short-lived and garbage-collected soon.
As a rule of thumb, this makes sense for things like identifier strings, I think.
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, that was my understanding of interning too :) It makes sense for type
then, but not code
as we are unlikely to emit the same deprecation warning twice.
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.
Still – if there is e.g. code that tests the warning code, then that code will contain a copy of that string, right? Also, this is part of the generic helper, and I would like to add codes to the existing native warnings in a follow-up (because I think that might be semver-major)
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.
Still – if there is e.g. code that tests the warning code, then that code will contain a copy of that string, right?
Yes.
Also, this is part of the generic helper, and I would like to add codes to the existing native warnings in a follow-up.
But those will be different codes. Even if it is the same deprecation code from multiple places, it should only be emitted once, thus the V8 string will only be constructed once.
If I understand interning correctly. The benefit isn't so much string copying, but rather string comparison. Instead of a copy, we end up doing some hash table lookup. Which is at least as costly, most likely more costly.
I would even go as far and say that interning the type
only makes sense if the string object is reused. Such that it doesn't have to be reconstructed so often. In this case, it isn't reused, but we should properly do that.
Just to clarify: I really don't care about this, I'm just trying to learn :D
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.
Even if it is the same deprecation code from multiple places, it should only be emitted once, thus the V8 string will only be constructed once.
Sorry, I was talking about the existing non-deprecation warnings (e.g. the crypto warning that is used in the test here :)
The benefit isn't so much string copying, but rather string comparison.
Yes, I would say so :) That’s why I usually go with using this for identifiers as a rule of thumb
src/node.cc
Outdated
|
||
if (!emit_warning->IsFunction()) return Just(false); | ||
|
||
int i = 0; |
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.
Suggestion: call this argc
.
src/node_crypto.cc
Outdated
"load failed: %s\n", | ||
extra_root_certs_file.c_str(), | ||
ERR_error_string(err, nullptr)).IsNothing()) { | ||
return; |
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.
This return
screws up root_cert_store
's reference counting
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.
Hm – how would this fail? Returning here leaves root_cert_store
initialized but unused, but the code doesn’t look to me like that’s harmful?
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.
SecureContext::ctx_
won't have the root certificate store associated with it if you return here. That's a change from before.
src/node_crypto.cc
Outdated
cipher_type); | ||
if (ProcessEmitWarning(env(), "Use Cipheriv for counter mode of %s", | ||
cipher_type).IsNothing()) | ||
return; |
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.
Likewise, this leaves the cipher object in a half-initialized state.
src/node_internals.h
Outdated
v8::Maybe<bool> ProcessEmitWarning(Environment* env, const char* fmt, ...); | ||
v8::Maybe<bool> ProcessEmitDeprecationWarning(Environment* env, | ||
const char* warning, | ||
const char* deprecation_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.
Alignment.
@@ -18,7 +18,7 @@ if (process.env.CHILD) { | |||
|
|||
const env = Object.assign({}, process.env, { | |||
CHILD: 'yes', | |||
NODE_EXTRA_CA_CERTS: `${fixtures.fixturesDir}/no-such-file-exists`, | |||
NODE_EXTRA_CA_CERTS: `${fixtures.fixturesDir}/no-such-file-exists-🐢`, |
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.
Haha :-)
src/node.cc
Outdated
if (type != nullptr) { | ||
if (!String::NewFromOneByte(env->isolate(), | ||
reinterpret_cast<const uint8_t*>(type), | ||
v8::NewStringType::kInternalized) |
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 don't know if it makes sense to intern the strings - they stay around forever but they're probably used only once. (At least, how often is the same warning emitted? Not too often, I'd 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.
You and @AndreasMadsen convinced me :)
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.
Mostly LGTM
I'm fine with using ->Call()
for now. However, the comment should definitely be changed.
As said, we need to fix this on a more general level.
e45d167
to
d83f667
Compare
@addaleax looks like there's at least one related failure in CI:
|
- Handle exceptions when getting `process.emitWarning` or when calling it properly - Add `Maybe<bool>` to the return type, like the V8 API uses it to indicate failure conditions - Update call sites to account for that and clean up/return to JS when encountering an error - Add an internal `ProcessEmitDeprecationWarning()` sibling for use in nodejs#17417, with common code extracted to a helper function - Allow the warning to contain non-Latin-1 characters. Since the message will usually be a template string containing data passed from the user, this is the right thing to do. - Add tests for the failure modes (except string creation failures) and UTF-8 warning messages. Refs: nodejs#17417
d83f667
to
12c4f38
Compare
12c4f38
to
a9613ac
Compare
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.
JS part is LGTM
Landed in f3cd537 |
- Handle exceptions when getting `process.emitWarning` or when calling it properly - Add `Maybe<bool>` to the return type, like the V8 API uses it to indicate failure conditions - Update call sites to account for that and clean up/return to JS when encountering an error - Add an internal `ProcessEmitDeprecationWarning()` sibling for use in nodejs#17417, with common code extracted to a helper function - Allow the warning to contain non-Latin-1 characters. Since the message will usually be a template string containing data passed from the user, this is the right thing to do. - Add tests for the failure modes (except string creation failures) and UTF-8 warning messages. PR-URL: nodejs#17420 Refs: nodejs#17417 Reviewed-By: Andreas Madsen <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]>
- Handle exceptions when getting `process.emitWarning` or when calling it properly - Add `Maybe<bool>` to the return type, like the V8 API uses it to indicate failure conditions - Update call sites to account for that and clean up/return to JS when encountering an error - Add an internal `ProcessEmitDeprecationWarning()` sibling for use in #17417, with common code extracted to a helper function - Allow the warning to contain non-Latin-1 characters. Since the message will usually be a template string containing data passed from the user, this is the right thing to do. - Add tests for the failure modes (except string creation failures) and UTF-8 warning messages. PR-URL: #17420 Refs: #17417 Reviewed-By: Andreas Madsen <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]>
Should this land on LTS? I believe there was a question about potentially backporting this to 6.x, but this may be needed for 8.x either way |
ping re: backport |
1 similar comment
ping re: backport |
process.emitWarning
or whencalling it properly
Maybe<bool>
to the return type, like the V8 API uses itto indicate failure conditions
when encountering an error
ProcessEmitDeprecationWarning()
siblingfor use in domain: runtime deprecate MakeCallback #17417,
with common code extracted to a helper function
message will usually be a template string containing data passed
from the user, this is the right thing to do.
and UTF-8 warning messages.
Refs: #17417
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesAffected core subsystem(s)
src