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

util: fix inspection of module namespaces #20962

Closed

Conversation

devsnek
Copy link
Member

@devsnek devsnek commented May 25, 2018

unbreaks 6.7 upgrade

/cc @targos i don't have 6.7 checked out or the room to do so, can you confirm this patch fixes it?

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

@devsnek devsnek requested a review from BridgeAR May 25, 2018 13:37
@nodejs-github-bot nodejs-github-bot added the util Issues and PRs related to the built-in util module. label May 25, 2018
@devsnek devsnek force-pushed the fix/module-namespace-inspection branch from b3450e9 to e16a700 Compare May 25, 2018 13:50
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.

Why did this break in 6.7? And this will likely have a minor influence on the performance.

lib/util.js Outdated

if (symbols.length !== 0) {
symbols = symbols.filter((key) =>
propertyIsEnumerable.call(value, key));
Copy link
Member

Choose a reason for hiding this comment

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

This should fit on a single line as before.

lib/util.js Outdated
try {
keys = Object.keys(value);
} catch (err) {
if (err instanceof ReferenceError) {
Copy link
Member

Choose a reason for hiding this comment

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

Are there other possibilities for this to throw a ReferenceError?

Copy link
Member Author

Choose a reason for hiding this comment

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

not that I know of

lib/util.js Outdated
keys = Object.keys(value);
} catch (err) {
if (err instanceof ReferenceError) {
keys = Object.getOwnPropertyNames(value);
Copy link
Member

Choose a reason for hiding this comment

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

Why does this not throw compared to Object.keys? Because of https://www.ecma-international.org/ecma-262/6.0/#sec-enumerableownnames 5.a.i.?

Copy link
Member Author

Choose a reason for hiding this comment

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

[[GetOwnProperty]] -> [[Get]] -> [[GetBindingValue]] which throws

lib/util.js Outdated
keys = Object.keys(value);
if (symbols.length !== 0)
symbols = symbols.filter((key) => propertyIsEnumerable.call(value, key));
// this might throw if `value` is a Module Namespace Object from an
Copy link
Member

Choose a reason for hiding this comment

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

Please use upper cases for the first letter of a sentence.

@BridgeAR
Copy link
Member

@devsnek
Copy link
Member Author

devsnek commented May 25, 2018

@BridgeAR v8 fixed a bug where they weren't throwing a reference error and they should have been

@mmarchini
Copy link
Contributor

Why did this break in 6.7?

For reference: https://bugs.chromium.org/p/v8/issues/detail?id=7780

@mmarchini
Copy link
Contributor

I don't have 6.7 checked out or the room to do so, can you confirm this patch fixes it?

Yep, tests are passing on targos:v8-6.7 with these changes.

@BridgeAR
Copy link
Member

I suggest to take a completely different approach: I saw that there is already a custom inspect function for modules. So when calling that function it should just wrap the util.inspect call in a try / catch and handle the error there because it should not be needed in the general util.inspect function.

Btw: normally only the object should be returned instead of calling util.inspect in a custom inspect function. That reduces the overhead. In this specific case it is useful in case that call is wrapped as suggested.

@devsnek
Copy link
Member Author

devsnek commented May 25, 2018

@BridgeAR keys are collected before we check the type or branch to custom inspect functions

edit: i misunderstood; module namespace objects can't have custom inspect functions

@BridgeAR
Copy link
Member

Let's see how the benchmarks come out. I am not a fan of adding a lot of special handling for the namespace edge case.

lib/util.js Outdated
try {
keys = Object.keys(value);
} catch (err) {
if (err instanceof ReferenceError) {
Copy link
Member

Choose a reason for hiding this comment

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

Instead of relying on the reference error here, we might just test for types.isModuleNamespaceObject nevertheless.

@jdalton
Copy link
Member

jdalton commented May 25, 2018

\cc @ljharb

On v8/issues/detail?id=7780 are you planning to file a spec bug?

Update:

I opened issue tc39/ecma262#1209.

lib/util.js Outdated
types.isModuleNamespaceObject(value)) {
keys = Object.getOwnPropertyNames(value);
} else {
throw err;
Copy link
Member

@jdalton jdalton May 25, 2018

Choose a reason for hiding this comment

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

☝️ the Object.getOwnPropertyNames(value) fallback should filter non-enumerable to be equiv to keys

Update:

Not required since this is now restricted to namespace objects.

Copy link
Member

Choose a reason for hiding this comment

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

I suppose you can't have non-enumerable keys on a module namespace?

Copy link
Member Author

@devsnek devsnek May 25, 2018

Choose a reason for hiding this comment

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

@jdalton until/unless the spec changes we have no way to check which ones are enumerable, and what @targos said

Copy link
Member

Choose a reason for hiding this comment

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

i believe the only keys on a module namespace will ever (atm) be enumerable strings for the exports, and Symbol.toStringTag.

@mscdex
Copy link
Contributor

mscdex commented May 25, 2018

If this is going to land on older branches, we should be sure to run benchmarks for those branches first. If there are performance regressions, we should try to see about minimizing the impact (for example the try-catch will probably have a noticeable impact in node v6.x).

@devsnek
Copy link
Member Author

devsnek commented May 25, 2018

@mscdex luckily i don't think module namespace objects exist on 6.x

@targos targos mentioned this pull request May 26, 2018
2 tasks
keys = Object.keys(value);
// This might throw if `value` is a Module Namespace Object from an
// unevaluated module, but we don't want to perform the actual type
// check because it's expensive.

This comment was marked as resolved.

@devsnek devsnek force-pushed the fix/module-namespace-inspection branch from 7d66a91 to f056724 Compare May 28, 2018 15:13
lib/util.js Outdated
if (err instanceof ReferenceError &&
types.isModuleNamespaceObject(value)) {
keys = Object.getOwnPropertyNames(value);
} else {
Copy link
Member

Choose a reason for hiding this comment

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

\cc @bmeurer A pattern in much of Node's code is to extract try-catches into their own functions. In this case it would be named tryKeys(). Is this practice still beneficial in the V8 of today?

Copy link
Member Author

Choose a reason for hiding this comment

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

iirc it hasn't been needed since like v6

Copy link
Member

Choose a reason for hiding this comment

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

That is not necessary anymore. It used to prevent a function from optimizing and this is not the case anymore with turbofan.

@mmarchini
Copy link
Contributor

Is there any strong -1 here or can we land this?

@devsnek
Copy link
Member Author

devsnek commented May 29, 2018

@mmarchini ci still appears to have issues so I'm not running it yet.

@mmarchini
Copy link
Contributor

@devsnek regarding our infrastructure I think the issues are fixed. There are only flaky tests we need to fix now.

@targos
Copy link
Member

targos commented May 29, 2018

// check because it's expensive.
// TODO(devsnek): track https://github.com/tc39/ecma262/issues/1209
// and modify this logic as needed.
try {
Copy link
Member

@jdalton jdalton May 29, 2018

Choose a reason for hiding this comment

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

  • Object.keys and Object.getOwnPropertyNames references should be cherry-picked above.
  • The instanceof ReferenceError check will fail from errors of a different realm (context).

Copy link
Member Author

Choose a reason for hiding this comment

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

for the first point, we purposely leave those as-is because some polyfills for things use them to mess with util.

for the second point, i guess types.isNativeError(err) && err.name === 'ReferenceError' would work?

Copy link
Member

@jdalton jdalton May 29, 2018

Choose a reason for hiding this comment

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

for the first point, we purposely leave those as-is because some polyfills for things use them to mess with util.

Ah cool! Do you have any examples at hand?

for the second point, i guess types.isNativeError(err) && err.name === 'ReferenceError' would work?

Yep.

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 don't know what package does it, but it was brought up during discussions about hardening core against tampering with builtin prototypes.

@devsnek devsnek force-pushed the fix/module-namespace-inspection branch from f056724 to a9a0643 Compare May 29, 2018 14:25
@BridgeAR
Copy link
Member

@BridgeAR BridgeAR added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label May 29, 2018
@@ -772,7 +788,7 @@ function formatNamespaceObject(ctx, value, recurseTimes, keys) {
try {
output[i] = formatProperty(ctx, value, recurseTimes, keys[i], 0);
} catch (err) {
if (!(err instanceof ReferenceError)) {
if (!(types.isNativeError(err) && err.name === 'ReferenceError')) {
throw err;
Copy link
Member

@jdalton jdalton May 29, 2018

Choose a reason for hiding this comment

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

Could the error checks be simplified all up? If namespace objects can only error by throwing a reference error then we can assume that the error accessing it is because of the reference error and remove the error checks and rethrow code above. We could also simplify the initial check by doing:

try {
  keys = Object.keys(value);
} catch (err) {
  if (types.isModuleNamespaceObject(value)) {
    keys = Object.getOwnPropertyNames(value);
  } else {
    throw err;
  }
}

Here erroring should be rare and when it does error it is likely a namespace object so the check up-front on error should be fine.

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 think its best to explicitly check if its a reference error, do you feel strongly about this?

Copy link
Member

Choose a reason for hiding this comment

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

I dig it if code could be simpler, and still correct, that it be simpler is all.

@MylesBorins
Copy link
Contributor

This is blocking V8 6.7 landing

@jdalton is it ok for this to land as is and be simplified in a follow up?

@MylesBorins
Copy link
Contributor

@jdalton
Copy link
Member

jdalton commented May 30, 2018

Yep, yep.

@mmarchini
Copy link
Contributor

FreeBSD failure is a flaky and I have no idea why it's saying OSX failed.

Running another one: https://ci.nodejs.org/job/node-test-pull-request/15169/

@mmarchini
Copy link
Contributor

OSX should work now (thanks @refack!).

5th time's a charm! https://ci.nodejs.org/job/node-test-pull-request/15170/

@mmarchini
Copy link
Contributor

CI is yellow. Landing!

@mmarchini
Copy link
Contributor

Landed in a25730b

@mmarchini mmarchini closed this May 30, 2018
mmarchini pushed a commit that referenced this pull request May 30, 2018
PR-URL: #20962
Reviewed-By: Guy Bedford <guybedford@gmail.com>
Reviewed-By: Matheus Marchini <matheus@sthima.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
@devsnek devsnek deleted the fix/module-namespace-inspection branch May 30, 2018 20:34
@devsnek devsnek removed the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label May 30, 2018
addaleax pushed a commit that referenced this pull request May 31, 2018
PR-URL: #20962
Reviewed-By: Guy Bedford <guybedford@gmail.com>
Reviewed-By: Matheus Marchini <matheus@sthima.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
@MylesBorins MylesBorins mentioned this pull request Jun 6, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
util Issues and PRs related to the built-in util module.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants