-
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: make global
non-enumerable
#10405
Conversation
Per https://github.com/tc39/proposal-global, the global value `global` should be configurable, writable, and non-enumerable. `node` has always provided it as configurable, writable, and enumerable. The plan is to make it non-enumerable, and barring strong evidence that node can not ship `global` as non-enumerable, this will be how it lands as stage 4 (in the spec).
@ljharb is there a summary on why it's not enumerable? I think it currently is here which mean this should probably be considered a breaking change...? |
@Fishrock123 |
@ljharb Here's a minimal test you can add to 'use strict';
require('../common');
const assert = require('assert');
assert(!Object.keys(global).includes('global'),
'global should be non-enumerable'); Adding additional tests to that for |
Thanks to you both; I'll update the commit message as well as add a test. @Fishrock123 it's non-enumerable because things shouldn't be enumerable unless there's a good reason for there to be so - the proposal was made stage 3 with the understanding that if node decided making it non-enumerable was untenable, that the spec would change to make it enumerable. I think semver-major is appropriate, although I very much doubt the change will actually break anyone. |
lib/internal/bootstrap_node.js
Outdated
@@ -190,6 +190,12 @@ | |||
} | |||
|
|||
function setupGlobalVariables() { | |||
Object.defineProperty(global, 'global', { |
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.
By the time this code is run, the global
property of the global object is already defined, and this line redefines it to be non-enumerable. This works, but I'm wondering if there's a less roundabout way to do it. (I assume it would be related to this, but I'm not sure about the specifics of how to make a property non-enumerable from the C++ side.)
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.
Thanks, I can explore that file, thanks for the pointer.
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'd definitely prefer to define it as non-enumerable in C++ instead, to be sure)
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.
k, figured this out, new commit incoming
Enumerability makes it easier for modules like https://github.com/sindresorhus/globals to discover the available globals, just saying. |
@silverwind This only applies to one property: the As far as libraries like https://github.com/sindresorhus/globals are concerned, I assume they use |
@silverwind indeed, this doesn't affect the enumerability of all global variables, just of the global variable |
Okay, sounds good. |
Ummm. I'm not ok with that?? I've used this for debugging certain things and finding things before... and I can see myself needing it again in the future... ...
It would be unchangeable.
This is what I don't get, it is only not "enumerable" by |
"Enumerable" determines whether it shows up in for..in, Object.assign, Object.keys/values/entries, etc. Object.getOwnPropertyNames, Reflect.ownKeys, and Object.getOwnPropertyDescriptors are the only ones that give you non-enumerable values. Note that I said the spec would be changeable - the assumption is that if this PR were merged, that would constitute evidence that node is willing to make this change. Separately, it is not a breaking change to make a non-enumerable thing enumerable, it's a semver-minor, so it's definitely changeable anyways. |
Can you explain how this change would cause problems when using [ 'DTRACE_NET_SERVER_CONNECTION',
'DTRACE_NET_STREAM_END',
'DTRACE_HTTP_SERVER_REQUEST',
'DTRACE_HTTP_SERVER_RESPONSE',
'DTRACE_HTTP_CLIENT_REQUEST',
'DTRACE_HTTP_CLIENT_RESPONSE',
'global',
'process',
'Buffer',
'clearImmediate',
'clearInterval',
'clearTimeout',
'setImmediate',
'setInterval',
'setTimeout',
'console',
'module',
'require' ] With the proposed change here, it will instead return this: [ 'DTRACE_NET_SERVER_CONNECTION',
'DTRACE_NET_STREAM_END',
'DTRACE_HTTP_SERVER_REQUEST',
'DTRACE_HTTP_SERVER_RESPONSE',
'DTRACE_HTTP_CLIENT_REQUEST',
'DTRACE_HTTP_CLIENT_RESPONSE',
'process',
'Buffer',
'clearImmediate',
'clearInterval',
'clearTimeout',
'setImmediate',
'setInterval',
'setTimeout',
'console',
'module',
'require' ] |
global
global
non-enumerable
Updated to set the property as non-enumerable from C++, rather than having to change it in JS. ( Happy to squash the commits upon review approval. |
// (Allows you to set stuff on `global` from anywhere in JavaScript.) | ||
global->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "global"), global); | ||
NONENUMERABLE_PROPERTY(global, "global", global); |
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.
global global global
global
non-enumerableglobal
non-enumerable
Very minor note: Now that the change is in a C++ file in |
@Trott makes sense; when I squash post-approval pre-merge i'll update the commit message to say |
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.
LGTM. This introduces a slight naming inconsistency with the existing READONLY_DONT_ENUM_PROPERTY
, although I personally prefer NONENUMERABLE_PROPERTY
.
@cjihrig happy to rename |
commits squashed. |
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.
LGTM aside from a nitpick with the test
writable | ||
} = Object.getOwnPropertyDescriptor(global, 'global'); | ||
|
||
assert.strictEqual(value, global, 'global should be global object'); |
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 assertion isn't actually verifying that global
is the global object. (If there was a bug where global
was set to some other value with a self-referential global
property, the assertion would still pass because it's just comparing (the global object).global
to (the global object).global.global
.)
I think it would be better to compare global
to something that is guaranteed to be the global object. For example:
assert.strictEqual(value, (0, eval)('this'), 'global should be global object');
edit: reworded for clarity
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.
Good point, I'll change to Function('return this')()
(i assume we're not worried about Function
being overwritten in tests, or else i could use function () {}.constructor('return this')()
)
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 think Function('return this')()
is fine. (Technically, function () {}.constructor('return this')()
could also cause a problem if Function.prototype.constructor
was overridden, but I don't think we need to worry about that here.)
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.
In that case constructor
would be an own property on the function, and thus would shadow anything on Function.prototype
, but I agree we don't need to worry about it :-)
FWIW I created a CL to implement this in V8: https://codereview.chromium.org/2599903003 |
@ljharb The |
So does semver-major here mean it won't land in 7.x, but might in 8.x? Or does it mean it won't land until 9.x? |
It means it might land in 8.x. |
To clarify, assuming we reach consensus that this is a good idea, this PR will probably be merged and land on master fairly soon. But it won't make it into a release until Node 8 is released in April. |
@ljharb Two more things of note:
|
@not-an-aardvark what consensus do you envision is required? Node representatives at TC39 in September agreed that unless there was concrete evidence of code breakage, that it would be acceptable - that helped allow the proposal to go to stage 3. node 8.x in April seems fine, thanks for clarifying. |
Yep, I don't see any reason why this change cannot land but given the nature of the change, semver-major is appropriate. As @not-an-aardvark and @Trott point out, that means that it can land in |
The absolute rock-bottom minimum would be:
Covering these each in turn:
Again, the above is the absolute minimum. More consensus is always better, of course. |
@Fishrock123 wrote:
Re-reading what I wrote (that the above was a response to), I see that I phrased it badly. I made it sound like |
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.
LGTM and +1 on considering this semver-major
writable | ||
} = Object.getOwnPropertyDescriptor(global, 'global'); | ||
|
||
var actualGlobal = Function('return this')(); |
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.
tiny nit: This could be const
, 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.
fixed
6f761fd
to
4744e3f
Compare
Merging this should be withheld, pending the outcome of the issues reported in tc39/proposal-global#20 |
I added a |
@ljharb ... where are we at on this? |
@jasnell It seems like I'll open a new PR in the future when we've settled on the new top-level variable name. |
Per https://github.com/tc39/proposal-global
This is my first PR to node core, so I'm putting it up now without a completed checklist. Early feedback is welcome, and I'll be moving through the items as quickly I can.
Checklist
make -j4 test
(UNIX),orpassesvcbuild test
(Windows)Affected core subsystem(s)
the JS environment
Description of change
Per the stage 3 proposal,
global
should be non-enumerable (but writable and configurable). This PR alters the property descriptor to be so.(If there's a better place this should be done, a pointer to it is most welcome)