tl;dr
The special __parent__
property has been removed from SpiderMonkey and is no longer in nightly builds. If you don’t use __parent__
or don’t know what the property does, you didn’t miss much.
If you use __parent__
, you have a couple replacements. If you were using it to determine the global object for another object, use Components.utils.getGlobalForObject
instead. If you were using it only to test its value against an expected value, use nsIDOMWindowUtils.getParent
instead (but do note that its semantics are not absolutely identical to those of __parent__
). If you were using it from unprivileged web scripts as a potential vector for security exploits, I feel your pain and will take no steps to assuage it. If you were using it some other way, comment and we’ll figure something out for your use case.
If you think you understood the __parent__
property (you probably don’t), or if you’re interested in the nitty-gritty details of JavaScript semantics, read on for the details of this esoteric property and the reasons for its removal.
Scoping in JavaScript
In the following example code, when the function g
is invoked, what stores the v
variable?
function f(a) { var v = a; function g() { return v; } return g; } var fun = f(2); fun();
The variable is accessed in an enclosing scope, created when the enclosing function was invoked. In the ECMAScript standard, the location of such variables is an object, stored as an internal [[Scope]]
property of the function being called. In ES3 this object was a standard JavaScript object; ES5 tightens semantics slightly and uses a simpler structure, but the idea’s the same.
Meet the __parent__
It’s not possible to access the object stored as [[Scope]]
in ECMAScript proper, but it has been possible to access it in SpiderMonkey and Mozilla-based browsers. The magical __parent__
property can often provide access to this value:
this.toString = function() { return "global"; } var q = 17; function foo() { return q; } print(foo.__parent__); // prints "global"
“Often”? When does __parent__
not expose this value?
__parent__
doesn’t always reflect [[Scope]]
because ECMAScript requires that certain objects not be made available to scripts. Among such objects are what SpiderMonkey refers to as With objects, Call objects, and Block objects.
With objects
SpiderMonkey creates With objects to handle the esoteric, non-lexical name lookup required by the semantics of the with
statement in JavaScript. These semantics require that a name, depending on the runtime object used in the with
, refer to a property of that object or to a variable in an enclosing scope. It’s impossible to know in general which will be the case before runtime, and therefore it’s impossible to speed up a lot of script that runs inside a with
. (Incidentally, this is why you should never ever ever [ever] use with
in performance-critical code. Use a two-letter abbreviation variable to save typing, if verbosity is your concern.) We can’t simply use the with
block’s object as the scope, because if we miss on a lookup there we want to fall back to the normal scope; therefore we introduce a With object. Here’s an example of a situation where a With object is created:
with ({ toString: function() { return "with object"; } }) { print(function() { return toString; }.__parent__); // prints "with object" }
__parent__
doesn’t actually give you the With object, because it’s a carefully-tuned internal value. With objects have behavior and functionality optimized for their particular purpose, and if we simply exposed them to scripts it would probably be possible to do Very Bad Things. Consequently, if you try to access __parent__
in a situation where you “should” get a With object, we instead give you back the object where the With object begins its search: the object used for the with
block. This may be what a developer superficially familiar with __parent__
might expect, but it is nevertheless a lie.
Call objects
SpiderMonkey’s Call objects represent the local variables of a function call. Therefore, Call objects correspond to the [[Scope]]
of functions. Returning to the original example, reproduced below, v
is stored in a Call object, which is the [[Scope]]
of the function g
:
function f(a) { var v = a; function g() { return v; } return g; } var fun = f(2); print(fun.__parent__);
When you attempt to access g
‘s __parent__
property, SpiderMonkey censors the [[Scope]]
and returns null
, because ECMAScript requires that Call objects not be exposed. This case, which is far more common than the with
case, completely prevents access to [[Scope]]
at all (rather than exposing a half-representation of the value). This situation is even more pervasive in light of the modern JavaScript encapsulation practice of enclosing libraries in closures for pseudo-namespacing purposes. In many libraries __parent__
on many functions of interest gives no value whatsoever.
Block objects
One of the first things every JavaScript developer learns is that JavaScript variables are not scoped to blocks. Language tutorials usually emphasize this point, because it differs from most other languages (and in particular, it differs from the C-ish family of languages, the syntax of which JavaScript borrows to a fair degree). The conventional wisdom is that names in functions are always scoped to enclosing functions or to the global scope. If you want a locally-defined name, you have to wrap it up in a function.
The conventional wisdom is wrong.
Here’s proof: what does this example print?
var v = "global"; function test(frob) { try { if (frob === 0) return v + " try"; if (frob === 1) throw "local"; } catch (v) { return v + " catch"; } finally { if (frob === 2) return v + " finally"; } return v + " after finally"; } print("not throwing, in try: " + test(0)); print("throwing, in catch: " + test(1)); print("not throwing, in finally: " + test(2)); print("not throwing, after finally: " + test(3));
Behavior depends on whether the binding for v
as introduced by the catch
is scoped to the function or to something else. JavaScript specifies that v
, referring to the value potentially thrown while executing the try
block, is scoped only to the catch
block. Thus v
in the catch
refers to the thrown value; all other uses of v
are outside the catch
block and refer to the global variable v
. Therefore the output is this:
not throwing, in try: global try throwing, in catch: local catch not throwing, in finally: global finally not throwing, after finally: global after finally
Any function which referred to v
inside the test
function would have a Call object as its [[Scope]]
— except for a function defined inside the catch
block. Such a function at that location would have to capture the thrown value, so there must be something else on the scope chain before the Call object. SpiderMonkey refers to these objects as Block objects, because they implement traditional block-level scoping.
Block objects have the same issues as Call objects, so SpiderMonkey censors them to null
as well. For optimization purposes we’d like to “boil them away” whenever possible, so as not to create an obnoxious little one-property object when we catch an exception and define a function that captures it. Exposing such an object directly prevents these optimizations, and would likely expose some security vulnerabilities.
But __parent__
‘s issues don’t stop merely at those induced by complex language semantics. Even perfectly simple code — simpler even than nested functions — has potential for unpredictability.
If certain optimizations are implemented, __parent__
may be a lie
Consider this script in the global scope:
function fun() { return 17; } print(fun.__parent__);
Does fun
‘s [[Scope]]
need to be the global object?
It’s OK to cheat if you never get caught.
ECMAScript provides no way for script to access [[Scope]]
. It’s a construct used to ease specification, and it need not even exist in implementations. fun
never uses any values from its enclosing scope, so there’s no reason that [[Scope]]
must be global object. An implementation that carried around [[Scope]]
with each function might simply make [[Scope]]
be null
, which could potentially speed up some garbage collection algorithms. Similarly, a function which refers only to never-modified variables in enclosing scopes might simply copy those variables’ values and, again, make [[Scope]]
lie. Should a specification-artifact internal property constrain implementations looking to improve performance, or code size, or any number of other measures of desirability? Given where JavaScript use is heading now, the answer must be no.
For certain types of functions, there are very good reasons, even in simple cases, to make [[Scope]]
a lie. SpiderMonkey implements some optimizations along these lines, but those optimizations don’t affect the value we store for [[Scope]]
at the present. If it were advantageous to change that, we would do so in a heartbeat. Therefore, while __parent__
may have a reliable value now, it’s possible it would not in the future.
__parent__
is available even where you think it isn’t
Oddly enough, __parent__
, despite its representing [[Scope]]
, isn’t just applicable to function objects. Instead, its value is generalized to all objects, in a way even more difficult to describe than that above. (Very roughly, __parent__
on non-function objects corresponds to the object which would be the __parent__
of a function created in the same context. I think. Mostly the value is used for security checks; improper setting of __parent__
is a common cause of implementation-caused XSS vulnerabilities.) This generalization and its semantics have even less support in the specification, so its meaning is even less clear than for functions.
__parent__
doesn’t expose anything useful
__parent__
, beyond having unintuitive behavior, doesn’t tell the developer anything he’d care to know. Why would you need to access the enclosing scope of a function, as an object, in the first place? The very definition of the problem is obscure; it is almost a prerequisite to have read the ECMA-262 specification to be able to ask the question.
In the searching of various codebases that we’ve done, we’ve found only these use cases for __parent__
:
- Testing
- A small number of Mozilla tests use
__parent__
to verify proper scoping of objects. Since the value as we use it has security implications, this use case is reasonable — but it has extremely limited applicability. This use case can be supported by creating an alternate means of accessing an object’s parent, a means exposed through XPCOM, not through JavaScript itself, and certainly not by polluting every object with the functionality. We have implementednsIDOMWindowUtils.getParent(obj)
to support this testing-oriented use case. Beware: this method doesn’t censor like__parent__
does! Tread cautiously when using it where a With, Call, or Block object might be exposed, and don’t use the returned object except in immediate strict equality (===
) checks. (NB: we’ve implemented it this way out of expediency; if it turns out to be too great a responsibility for typical testers, we’ll likely change it to censor.) - Determining the global context (global object) where an object was created
- Since
__parent__
often (for any function not nested in another function) is simply the global object, some people have used__parent__
to retrieve the global object associated with an arbitrary object. By walking from__parent__
to__parent__
you can often reach the global object this way. However, since the__parent__
value is sometimes a lie, it’s not as simple asvar global = obj; while (global.__parent__) global = global.__parent__;
. Instead, you have to be careful to start from an object with a known__parent__
, one guaranteed not to be a nested function or similar. The easiest way to do this is to first walk up the prototype chain to eventually bottom out atObject.prototype
, then to walk the parent chain. This method is baroque and non-obvious, and it requires careful tiptoeing around incorrect__parent__
values. If you know the object whose global you want to retrieve is a DOM node, the fully-standardnode.ownerDocument.defaultView
property also provides global access. Otherwise, the use case would be better addressed by exposing a method somewhere to directly retrieve the corresponding global object. We have recently added such a method to support this use case:Components.utils.getGlobalForObject(obj)
. - Being evil to Firefox
- A fair number of security bugs reported against SpiderMonkey in recent years have worked by successfully confusing the engine into assigning a bad parent to an object. This incorrect security context can then be used as a vector to run code in the context of another website (resulting in an XSS hole, possibly usable against any website), or in some cases, in the context of the browser “chrome” itself (providing the capability for arbitrary code execution — the worst-case scenario as far as exploits are concerned).
__parent__
is immutable and can’t itself be used to confuse the engine (at least in the absence of bugs, and we have no way to demonstrate such). However, it provides greater visibility into the engine, and it can sometimes expose values to script which wouldn’t (and shouldn’t) be accessible any other way. - Being evil to sandboxing mechanisms
__parent__
exposes security holes in some websites as well as in the browser directly. Such websites are those which purport to “safely” execute scripts provided by other users, through sandboxing or other mechanisms. For example, Facebook uses FBJS, a script-rewriting plus runtime-check mechanism, to allow applications to include interactivity. Google‘s Caja system provides a rigorous capabilities-providing system (also through script-rewriting plus runtime checks) to do the same, and it too is used in public sites these days. One requirement of such systems is that the global object must never be directly exposed: if you have the global object, you have unfettered access to the document, you haveeval
, you haveFunction
, and you are utterly and thoroughly hosed.__parent__
provides easy access to this, so all these systems must censor access to__parent__
, both statically (obj.__parent__
) and dynamically (var p = "__parent__"; obj[p]
). Not everyone knows, or remembers, that this is necessary. Removing__parent__
reduces attack surface in JavaScript sandboxes.
The former two use cases are better addressed in other ways. The latter two are holes best removed entirely.
__parent__
is being removed
__parent__
is being removed from SpiderMonkey. The value it purports to expose is esoteric and has semantics defined by a specification which JavaScript developers shouldn’t really have to read to understand it. The identification of that value is non-intuitive. Sometimes the value it exposes is a lie. There are plausible reasons why it might be made to lie if potential performance-improving ideas were implemented. It’s exposed even in places where it makes little sense. The use cases for it can be and are better served in other ways — or explicitly not served, when it serves as a vector for security vulnerabilities. In sum __parent__
doesn’t pass muster, so it’s being removed.
You can experiment with a version of Firefox without support for __parent__
by downloading a nightly from nightly.mozilla.org. (Don’t forget to use the profile manager if you want to keep the settings you use with your primary Firefox installation pristine.) The next release is many months away, which should provide plenty of time for extension developers to update their extensions to not use __parent__
, if by chance they had managed to discover it and use it.
Thanks for the info, I updated my unit tests so that they work again – using getGlobalForObject if it exists or __parent__ otherwise. And it does work well with modules.
Comment by Wladimir Palant — 08.05.10 @ 09:00
Is __parent__ in any way related to jsdIValue.jsParent? Firebug uses jsParent.
Comment by John J Barton — 11.05.10 @ 12:57
At a cursory glance, it appears that the value that property exposes is roughly the same as what
__parent__
exposed, except that it doesn’t include the for-safety censoring that__parent__
does (because anyone calling it is already in privileged code). The__parent__
changes shouldn’t have any effect on it.Comment by Jeff — 11.05.10 @ 14:25
Interesting article; it’s nice to get such behind the scenes explanations, even if I’m limited in fully understanding it.
In developing http://phpjs.org which seeks to port the PHP API to JavaScript, besides the annoying lack of standardization on iteration order in ECMAScript (not allowing us to port ordered associative arrays fully across browsers), the two other areas where we could not effectively offer PHP’s features (though maybe these could be bugs for security) were in:
1) PHP’s ability to extract into or compact variables out of the current scope. http://php.net/manual/en/function.extract.php or http://www.php.net/manual/en/function.compact.php
2) our own inability in JavaScript to detect what the parent object or array of an item was (as JavaScript does not allow variables to be passed in wholly by reference regardless of type).
This would let us do things like http://php.net/manual/en/function.settype.php so that if say
arr[0]
ora.b
were passed in, we could reach back to obtain the parent object/array in order to alter the original variable.Although this kind of thing is not something I’m looking for in SpiderMonkey alone anyways, since it seemed related, and since these features can come in quite handy, I am curious to find out what kind of reasons such features weren’t made allowable in JavaScript, as I’m guessing maybe they could relate to the topics raised in this article.
Comment by Brett Zamir — 11.05.10 @ 20:37
Oh, and __key__ as below this would also be handy, esp. for debugging:
Comment by Brett Zamir — 11.05.10 @ 20:46
Any feature of the sort you propose requires some sort of slowing-down of code that doesn’t use it. If your
__mommyObject__
isn’t computed lexically (so that, simply by examining the source,a
orarr
would be returned), there’s a question of “which” parent to return. What would it do in this case?If you want it in the standard, it has to have well-defined semantics.
Further, doesn’t something like this violate abstraction guarantees like lexical scoping? This suddenly means that a purely local object, stored only in a local variable (if still accessible through nested functions in the same function), is exposed to any code which can get some value which is in the “sub-graph” of that local variable. That’s unacceptable; everything being reachable from everywhere is convenient at times, but in general it’s an unmanageable hack. Implementing that would hork secure-JavaScript variants even moreso than
__parent__
did.PHP may (it’s been years since I used PHP much, and even then I didn’t use it much except in passing in a few web development situations) have a history of exposing implementation guts and giving developers ultimate flexibility, just as JavaScript as implemented by SpiderMonkey has in the past (we used to have a
__call__
[?] property of functions which would expose local variables to script, until it got removed precisely because it meant you could never rely on local variables being completely locally-controlled). We’re moving away from that because it is directly at odds with implementing a performant JS engine and with providing a basic veneer of abstraction upon which developers can build “safe” functionality.Comment by Jeff — 12.05.10 @ 11:52
Okay, so this removal breaks my following addon use case:
Imagine some XUL elements somewhere in a chrome document, all having an onpopupshowing handler calling a function
X
, which is part of chrome code as well – we don’t even know how many handlers there are and where, so we can’t change their code. Now assume the addon overlays/replaces functionX
to do some modifications.The new
X
wants to work with the XUL element which originally defined the handler, but we are restricted to the data the old contract gives us. So I could just grabX.caller.__parent__
and was done – very convenient…I don’t think that this falls into one of your four categories above…
Comment by Karsten Düsterloh — 09.06.10 @ 16:16
It’s unclear to me what “the data the old contract gives us” is. What do these functions look like? What arguments are they passed? Why would
__parent__
in that case have been a XUL element? I’m afraid I don’t understand your description of the situation.Comment by Jeff — 10.06.10 @ 00:50
(Man, I hate this – all XUL code got deleted, even inside a code tag!)
Okay, using the real stuff from SM/TB:
My addon overlays/replaces the
FillMessageIdContextMenu
function. The new function adds attributes/properties to the popup element (or whatever element has an event handler callingFillMessageIdContextMenu
). It wasn’t necessary to touch the XUL markup, becauseFillMessageIdContextMenu.caller.__parent__
pointed to the popup in this case.Of course, I can use
now, I just wanted to point out that there were slightly inobscure uses for
__parent__
before. 😉Comment by Karsten Düsterloh — 10.06.10 @ 12:55
No, I still think your use case is obscure. 🙂 Relying on the parent of the event handler generated for an
on*
attribute on an element being that element itself is yet another oddity of how__parent__
worked, or arguably in this case didn’t work; in this case it had no relation to the[[Scope]]
property in the specification at all!I’m sometimes surprised WordPress’s commenting widget isn’t something rich-text-editor-ish, so that the formatting problem wouldn’t even exist. I’m guessing most commenters aren’t HTML authors these days anyway.
Comment by Jeff — 10.06.10 @ 13:57
What I don’t get is why it’s name is
__parent__
over__scope__
, to be more in line with the spec. If the var indeed should reflect[[Scope]]
, I’d expect the name to be that. Being called__parent__
seems to be confusing at best. I guess the point is moot now, since it’s being removed :pComment by Peter van der Zee — 25.12.10 @ 04:05
I suspect it’s probably the influence of the SpiderMonkey JSAPI, which exposes the value of
__parent__
(when that value isn’t censored, that is) throughJS_GetParent
(and, mutably, throughJS_SetParent
!), in the deleterious model (for the web, where bad ideas can almost never fade away) of expose-everything.Comment by Jeff — 27.12.10 @ 09:59
[…] multi-threaded objects and titles, multi-threaded strings, dormant stack frames, __count__, __parent__, non-identifier atoms, heap-doubles, null-is-an-object, the no-int-valued-doubles-in-values […]
Pingback by Old Dijkstra Essays Considered | Luke Wagner's Blog — 12.08.11 @ 11:28