-
Notifications
You must be signed in to change notification settings - Fork 29.8k
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: update v8::Object::GetPropertyNames() usage #23660
Conversation
Ref: #23426 (comment) |
@@ -508,7 +508,12 @@ void ContextifyContext::PropertyEnumeratorCallback( | |||
if (ctx->context_.IsEmpty()) | |||
return; | |||
|
|||
args.GetReturnValue().Set(ctx->sandbox()->GetPropertyNames()); | |||
Local<Array> properties; |
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.
V8 recommend using the MaybeLocal
returning version.
(IMHO it's also cleaner then the out-param version)
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 is the MaybeLocal
version.
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 why not:
MaybeLocal<Array> maybe = ctx->sandbox()->GetPropertyNames(ctx->context())
if (maybe.IsEmpty())
return;
args.GetReturnValue().Set(maybe.ToLocalChecked());
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.
IMO, this way is easier to read and reason about (don't really have to think about Maybe
s).
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.
IMHO having an uninitialized variable is code smell, and if there's a way to avoid it, why not.
Also if (!ctx->sandbox()->GetPropertyNames(ctx->context()).ToLocal(&properties))
is a bit busy it does like 5 things...
But I leave it to the author.
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.
@refack Maybe a more objective advantage of the current variation here: It’s easier to handle errors from sequences of operations this way:
Lines 1237 to 1241 in 8ce99fa
if (!args[0]->ToObject(context).ToLocal(&module) || | |
!module->Get(context, env->exports_string()).ToLocal(&exports_v) || | |
!exports_v->ToObject(context).ToLocal(&exports)) { | |
return; // Exception pending. | |
} |
At first, I also was a fan of what you’re suggesting, but for cases like these it can become quite annoying…
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.
That's IMHO an extreme example of this pattern. What's wrong with:
Maybe<Object> module = args[0]->ToObject(context);
Maybe<Object> exports = module->Get(context, env->exports_string());
Maybe<Value> exports_v = exports_v->ToObject(context);
if (module.isEmpty() || exports.isEmpty() || exports_v.isEmpty()) {
return; // Exception pending.
}
The original code also has short-circuit logic, so it is tricky to reason about.
P.S. see the // Exception pending
comment, can we tell what when wrong?
We have actually have several guidelines that say to avoid this:
Google's:
- Local Variables - Place a function's variables in the narrowest scope possible, and initialize variables in the declaration.
- Output Parameters -
Prefer using return values rather than output parameters. If output-only parameters are used they should appear after input parameters.
C++CG:
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.
What's wrong with:
It doesn’t compile … MaybeLocal
s sadly don’t form a full monad; you can’t actually call ->Get()
or ->ToObject()
on one. The idea, as I understand V8, is to force API users to return to JS as soon as possible, and not attempt to do more operations.
P.S. see the
// Exception pending
comment, can we tell what when wrong?
No, but ideally we don’t have to care about that anyway.
It’s also a bit odd to quote Google’s style guide here… I totally see what you mean and why this pattern can be counterintuitive, but after all, this pattern is of Google’s making and V8 uses it very extensively in its own code. 😄
ES.20: Always initialize an object
I’d say that rule explicitly lists this pattern as an exception:
If you are declaring an object that is just about to be initialized from input, initializing it would cause a double initialization.
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 doesn’t compile …
MaybeLocal
s sadly don’t form a full monad;
I tried to read this to figure out if the values are dependant. I looked several times, but missed it. That's a red light for me.
Anyway like you say, we write code so it reads well, not writes easy. The following is linear and reads much better, with only one action per line.
Maybe<Object> module = args[0]->ToObject(context);
if (module.isEmpty()) {
return env->isolate()->ThrowException("module is empty");
}
Maybe<Object> exports = module->ToCheckedLocal()->Get(context, env->exports_string());
if (exports.isEmpty()) {
return env->isolate()->ThrowException("exports is empty");
}
Maybe<Value> exports_v = exports_v->ToCheckedLocal()->ToObject(context);
if (exports_v.isEmpty()) {
return env->isolate()->ThrowException("exports_v is empty");
}
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.
The ThrowException()
bit is not quite how MaybeLocal
s work – an empty one typically means that there is already an exception pending – but that’s a fair point, yes.
To be clear, I don’t personally like the repetition there, but I wouldn’t object to anybody using individual checks for each call.
@@ -508,7 +508,12 @@ void ContextifyContext::PropertyEnumeratorCallback( | |||
if (ctx->context_.IsEmpty()) | |||
return; | |||
|
|||
args.GetReturnValue().Set(ctx->sandbox()->GetPropertyNames()); | |||
Local<Array> properties; |
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 is the MaybeLocal
version.
Use the non-deprecated version of GetPropertyNames(). PR-URL: nodejs#23660 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Gus Caplan <[email protected]> Reviewed-By: Sakthipriyan Vairamani <[email protected]> Reviewed-By: James M Snell <[email protected]>
Use the non-deprecated version of GetPropertyNames(). PR-URL: #23660 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Gus Caplan <[email protected]> Reviewed-By: Sakthipriyan Vairamani <[email protected]> Reviewed-By: James M Snell <[email protected]>
Use the non-deprecated version of GetPropertyNames(). PR-URL: #23660 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Gus Caplan <[email protected]> Reviewed-By: Sakthipriyan Vairamani <[email protected]> Reviewed-By: James M Snell <[email protected]>
Use the non-deprecated version of GetPropertyNames(). PR-URL: #23660 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Gus Caplan <[email protected]> Reviewed-By: Sakthipriyan Vairamani <[email protected]> Reviewed-By: James M Snell <[email protected]>
Use the non-deprecated version of GetPropertyNames(). PR-URL: #23660 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Gus Caplan <[email protected]> Reviewed-By: Sakthipriyan Vairamani <[email protected]> Reviewed-By: James M Snell <[email protected]>
Use the non-deprecated version of GetPropertyNames(). PR-URL: #23660 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Gus Caplan <[email protected]> Reviewed-By: Sakthipriyan Vairamani <[email protected]> Reviewed-By: James M Snell <[email protected]>
Use the non-deprecated version of
GetPropertyNames()
.Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesEDIT: CI: https://ci.nodejs.org/job/node-test-pull-request/17938/