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

src: update v8::Object::GetPropertyNames() usage #23660

Merged
merged 1 commit into from
Oct 17, 2018

Conversation

cjihrig
Copy link
Contributor

@cjihrig cjihrig commented Oct 14, 2018

Use the non-deprecated version of GetPropertyNames().

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • commit message follows commit guidelines

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

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. vm Issues and PRs related to the vm subsystem. labels Oct 14, 2018
@refack
Copy link
Contributor

refack commented Oct 14, 2018

Ref: #23426 (comment)

@@ -508,7 +508,12 @@ void ContextifyContext::PropertyEnumeratorCallback(
if (ctx->context_.IsEmpty())
return;

args.GetReturnValue().Set(ctx->sandbox()->GetPropertyNames());
Local<Array> properties;
Copy link
Contributor

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)

Copy link
Member

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.

Copy link
Contributor

@refack refack Oct 14, 2018

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());

Copy link
Contributor Author

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 Maybes).

Copy link
Contributor

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.

Copy link
Member

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:

node/src/node.cc

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…

Copy link
Contributor

@refack refack Oct 14, 2018

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:

  1. Local Variables - Place a function's variables in the narrowest scope possible, and initialize variables in the declaration.
  2. Output Parameters -
    Prefer using return values rather than output parameters. If output-only parameters are used they should appear after input parameters.

C++CG:

  1. ES.20: Always initialize an object
  2. F.20: For “out” output values, prefer return values to output parameters

Copy link
Member

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 … MaybeLocals 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.

Copy link
Contributor

Choose a reason for hiding this comment

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

It doesn’t compile … MaybeLocals 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");
}

Copy link
Member

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 MaybeLocals 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;
Copy link
Member

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]>
@cjihrig cjihrig merged commit 7872d79 into nodejs:master Oct 17, 2018
@cjihrig cjihrig deleted the getpropnames branch October 17, 2018 16:29
jasnell pushed a commit that referenced this pull request Oct 17, 2018
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]>
addaleax pushed a commit that referenced this pull request Oct 20, 2018
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]>
MylesBorins pushed a commit that referenced this pull request Oct 30, 2018
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]>
@codebytere codebytere mentioned this pull request Nov 27, 2018
rvagg pushed a commit that referenced this pull request Nov 28, 2018
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]>
MylesBorins pushed a commit that referenced this pull request Nov 29, 2018
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]>
@codebytere codebytere mentioned this pull request Nov 29, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ Issues and PRs that require attention from people who are familiar with C++. vm Issues and PRs related to the vm subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants