-
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
Native class of global
changed in Node v7
#9274
Comments
Digging in /cc @nodejs/ctc |
I'm not sure if this is a change that we made or if it came from the v8 update. |
I can start bisecting this |
It's a change that comes from V8. We don't muck around with the global object's prototype. Example with v6:
With v7:
|
/cc @nodejs/v8 |
I can confirm that this change came in somewhere between 8a24728...96933df which leads me to believe this came with the V8 upgrade |
Btw, this can be fixed with |
Can, but I don't know if we should. If the use case is feature detection, then sniffing for |
@bnoordhuis A use case is feature detection. Another time when the internal class matters is when type checking or handling special cases; e.g., var toString = Object.prototype.toString;
function foo( bar ) {
var nativeClass = toString.call( bar );
if ( nativeClass === '[object global]' ) {
// ...do something...
} else if ( nativeClass === '[object process]' ) {
// ...do something...
} else {
// ...do something else
}
} And specifically re: environment detection: usually checking the internal class is combined with other checks including Regardless, the change introduces an (unintended) inconsistency between Node versions. |
I think I agree; if something like this is changed, it should be conscious decision we make. (I also agree that sniffing for |
I would go a step further and say that checking that |
@kgryte My point is more that checking for this particular behavior is not a very good indicator of a node.js runtime because you'd see the same behavior in, say, plv8, or any V8-backed runtime that doesn't override the default. |
Just to clarify and keep things focused: this issue is not about the right way to detect a Node environment, but about the fact that an unintended breaking change was introduced in Node v7 due to how V8 reports the internal class of |
This change should be kept. |
@kgryte I did extensive research for the |
@ljharb I am not aware of public But once again, whether or not Simply because |
Yes, only if they really want to keep that particular code in place, which would be unwise. Instead, affected code could just be corrected and "rely" on other means of 'environment' checks as mentioned above, 'process' etc. Though it's still not reliable as anyone can create, overwrite or delete those objects - if fooling someone is intended. Though I wonder why anyone would need to know in what environment their code runs - even 'process' in node.js doesn't guarantee the existence or behavior of anything - as node.js is thankfully evolving, and so is v8, as well as browsers. |
@dnalborczyk As someone who writes packages to work in Node, the browser, and electron, I regularly attempt to detect the runtime environment. For example, when running tests during CI, I often want to run a particular subset of tests only in a Node environment. Other times, I check because I want to expose a main export which is environment dependent. And yet other times I internally want to know the environment so I can branch accordingly; e.g., cluster-based numeric computing where I either want to use browser web workers or the But once again, an function isNode() {
return (
typeof global === 'object' &&
global === global.global &&
Object.prototype.toString.call( global ) === '[object global]' &&
typeof require === 'function' &&
typeof process === 'object' &&
Object.prototype.toString.call( process ) === '[object process]' &&
typeof process.versions === 'object' &&
typeof process.versions.node === 'string' &&
(
typeof process.release === 'undefined' ||
(
typeof process.release === 'object' &&
typeof process.release.name === 'string' &&
/node|io\.js/.test( process.release.name )
)
)
);
} So, yes, even the above is not foolproof, as someone could "fake" any of the above globals, pseudo-globals, properties, and values. But...that the function would be "fooled" becomes increasingly unlikely with each check. And again, I would like to state that this issue is not about environment detection. The original background example I used was merely illustrative and to state that this change may have (and has) consequences. At this point, I regret using my original example, as this continually gets more attention both here and in #9279 than that an unintended/unplanned/unannounced breaking change in Node behavior was introduced due to changes in V8. |
fwiw, the only truly reliable means to obtain the global object at the moment - in every version of every JS engine since the beginning of time - is To do environment detection could include comparing that object to |
@ljharb I know. I left that out of the above code snippet intentionally. The code is not meant to be authoritative, but illustrative. But a final time, the issue is that a change occurred. People may have come to depend on said change. I asked for clarification on whether this change was intentional and permanent. Based on participant reaction, the change was unexpected. This change, irrespective of how people used the feature The question that remains is: will Node ensure consistent behavior across all versions (including current LTS versions |
Seems better to me to backdate it as a breaking change, and follow v8's lead here. It broke across a major version, breaks are allowed, and node didn't document every detail of how v8 was different and possibly incompatible. Its true it wasn't intended specifically, but it was intended to update v8. Only @addaleax has suggested a possible fix, and it looked a bit questionable to me - sometime next year we are going to get another bug report asking why node is overriding the base v8 behaviour, and we'll be "well, it was like that in v0.10, and we decided to stay that way forever, even after v8 moved on" and that's not so compelling. |
@sam-github Yes, breaks are allowed in major updates. No one is disputing this. However, the break should be considered, not within the context of In general, if Node decides to accept a change introduced by V8, then would be nice to have a rationale why Node decided to follow V8's lead. If to ensure consistency and stability across the current Node ecosystem, including LTS releases, Node must break with/patch V8 at certain times, seems reasonable to believe such departures are permissible. I have yet to see a compelling reason why Node should forgo stability in this case. Unless V8 has a good reason to introduce this change, seems arbitrary and without rationale and introduces maintenance overhead for code currently using this feature. |
Been thinking, and I think this should be acceptable within the constraints that we already consider bumping v8 like this as a major. I'd like to hear exactly how much this effected, how widespread it is. I think if a lot of people are running into this then we should consider rolling it back but if only a few are, we should probably just document the change and accept it. |
fwiw, I tried running a npm grep for |
@addaleax thanks - now we have a list of which packages need PRs/issues, so they can stop checking for the global object the wrong way :-) |
This inadvertently changed to `[object Object]` with the V8 upgrade in 8a24728...96933df. Use `Symbol.toStringTag` to undo this particular change. Fixes: nodejs#9274
@ljharb I do not believe that is the lesson to draw here. The takeaway is that people have used the fact that, for all Node versions prior to |
I think this is the change that caused this: V8 is following the ES6 spec here, which does not leave much room for interpretation: In d8, we indeed define the toStringTag for the global object to be "global". In Chrome, window[Symbol.toStringTag] is set to "Window". Maybe this is the path node should take as well? |
@hashseed Which path do you suggest node take, setting it to |
To global to mimic the old behavior. |
This proposals https://github.com/tc39/proposal-global (stage 3) intention is to unify the 'global' object independent of the runtime environment. The name for such an object is 'global' (the same as node.js is currently using, as supposed to self, System.global etc.). Therefore, I suspect that if the proposal is being accepted, and browsers implement it, global.toString() will likely return - according to the spec - '[object Object]' as well. If we revert this now to mimic pre-v7-behavior, we might need to revisit it again in the future to revert the reverted, if we want consistency between environments. @ljharb Is that assumption correct? |
The JS spec will continue to not contain or require (nor prohibit, it must be noted) that So, despite my belief that there should be no such behavior, I must still acknowledge that there's no spec compliance issue here, either way. Browsers may and will continue to return I also don't think there's a compatibility issue here either way, because with the advent of ES6 and |
Thanks @ljharb I should have been more specific, I meant the spec regarding https://tc39.github.io/ecma262/#sec-object.prototype.tostring referred above. |
@dnalborczyk that spec says that if |
@ljharb Never using My understanding is that one motivation, and intended byproduct, of
That this feature can also be used for ill is not necessarily a reason to avoid using the behavior. And people will use this behavior for identifying their own custom class instances, just as Chrome sets Truth is, similar to modifying a built-in prototype (that you can do so, by your definition, would make using prototypical methods "brittle" and to be avoided whenever possible, including for builtins!) modifying a foreign object's |
I think we can safely assume that pre-ES6 code that assumes String(global)=="[object global]" won't interfere with toStringTag, since the latter is also an ES6 feature. |
@kgryte none the less, it's the reality since ES6 erased the unforgeability of Certainly the case you're describing - as a publicly available opt-in, is a fine use of it. But using it as a brand check is no longer reliable, no matter how much code is doing it. @hashseed except that in newer environments, |
Sure. We would have to weigh cases that rely on the old behavior against ones that use toStringTag for other purposes. |
This inadvertently changed to `[object Object]` with the V8 upgrade in 8a24728...96933df. Use `Symbol.toStringTag` to undo this particular change. Fixes: #9274 PR-URL: #9279 Reviewed-By: Prince John Wesley <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Franziska Hinkelmann <[email protected]>
Just noting something curious, but I'm in Node 8 and some functions that I create with Look at the strange output I see when debugging:
|
In Node.js version
<= 6.x.x
,In Node.js version
7.0
,Based on the changelog, this is not listed as a notable and/or breaking change. Why did the internal class change and is this a permanent/intentional change?
Background: one reason why this is a notable change is that environment sniffers (i.e., packages which try and detect if the runtime is Node versus, say, a browser) commonly use the internal class of
global
as means to identify a Node environment.The text was updated successfully, but these errors were encountered: