Multiple globals and direct/indirect eval
Jeff,
I think your real question reduces to this:
//none strict mode code globalObj= function() {return this}(); print(otherGlobal.eval("this") === globalObj) //??
The two different calls and the "indirect" name in your example may make the question seen like it is about something else (direct/indirect eval).
A more generally, do built-in functions capture their global environment at the time of their creation or do they they operate in the dynamic context of an ambient global environment. I hope it is the former.
An example of the more general question would be: print((Object.getPrototypeOf(new otherGlobal.Array(0)) === Array.prototype)
I believe the second example prints false for all browser implementations.
On 03/03/2011 04:41 PM, Allen Wirfs-Brock wrote:
I think your real question reduces to this:
//none strict mode code globalObj= function() {return this}(); print(otherGlobal.eval("this") === globalObj) //??
The two different calls and the "indirect" name in your example may make the question seen like it is about something else (direct/indirect eval).
Not quite so. For the example I gave, yes -- but you could see the direct/indirect distinction by putting the code I provided inside a function (and a little more gussying to demonstrate behavior better):
var global = this; function f() { var indirect = otherGlobal.eval; eval = indirect; print(eval("this") === this); print(eval("this) === global); print(eval("this") === otherGlobal); print(indirect("this") === this); print(indirect("this") === global); print(indirect("this") === otherGlobal); } new f();
IE9/Opera prints false/false/true and false/false/true. Chrome/Safari throws EvalError every time. Firefox prints true/false/false and false/false/true if otherGlobal is same-origin, false/false/true and false/false/true if otherGlobal is different-origin-but-same-document.domain.
A more generally, do built-in functions capture their global environment at the time of their creation or do they they operate in the dynamic context of an ambient global environment. I hope it is the former.
An example of the more general question would be: print((Object.getPrototypeOf(new otherGlobal.Array(0)) === Array.prototype)
I believe the second example prints false for all browser implementations.
This is an orthogonal issue, I believe, but I might as well respond since it's being discussed. There's a Firefox 4 bug that makes this not the case, ran out of time to fix it for release. It'll be fixed in 5.0, and I could imagine I might get a fix for it in a 4.0 point release, although with a fast release cycle that may not be necessary. If I recall correctly Nitro may be buggy this same way as well. I think Chrome/IE9/Opera did not demonstrate the bug in my testing. But in any case, printing false there is in my opinion the correct behavior.
On Mar 3, 2011, at 5:45 PM, Jeff Walden wrote:
On 03/03/2011 04:41 PM, Allen Wirfs-Brock wrote:
I think your real question reduces to this:
//none strict mode code globalObj= function() {return this}(); print(otherGlobal.eval("this") === globalObj) //??
The two different calls and the "indirect" name in your example may make the question seen like it is about something else (direct/indirect eval).
Not quite so. For the example I gave, yes -- but you could see the direct/indirect distinction by putting the code I provided inside a function (and a little more gussying to demonstrate behavior better):
var global = this; function f() { var indirect = otherGlobal.eval; eval = indirect; print(eval("this") === this); print(eval("this) === global); print(eval("this") === otherGlobal); print(indirect("this") === this); print(indirect("this") === global); print(indirect("this") === otherGlobal); } new f();
I tried to simplify because the above depends upon too many possible points of variation including whether you correctly are/aren't treating the first three calls as direct evals. That decision, itself may be subject to the answer to the test I suggested. So it is probably best to resolve that one first.
the following would also be interesting to test:
new function f() { var indirectEval = eval; var indirectForeignEval = otherGlobal.eval; print(indirectEval === indirectForeignEval); //see if they are the same object print(indirectEval("this") === indirectForeignEval("this")); //do they evaluate to the same global object? eval=indirectForeign; print(eval("this")===this); }
IE9/Opera prints false/false/true and false/false/true. Chrome/Safari throws EvalError every time. Firefox prints true/false/false and false/false/true if otherGlobal is same-origin, false/false/true and false/false/true if otherGlobal is different-origin-but-same-document.domain.
A more generally, do built-in functions capture their global environment at the time of their creation or do they they operate in the dynamic context of an ambient global environment. I hope it is the former.
An example of the more general question would be: print((Object.getPrototypeOf(new otherGlobal.Array(0)) === Array.prototype)
I believe the second example prints false for all browser implementations.
This is an orthogonal issue, I believe, but I might as well respond since it's being discussed. There's a Firefox 4 bug that makes this not the case, ran out of time to fix it for release. It'll be fixed in 5.0, and I could imagine I might get a fix for it in a 4.0 point release, although with a fast release cycle that may not be necessary. If I recall correctly Nitro may be buggy this same way as well. I think Chrome/IE9/Opera did not demonstrate the bug in my testing. But in any case, printing false there is in my opinion the correct behavior.
I don't really agree that it is an orthogonal issue. The reason is because there are many places in the ES5 specification, including other built-in functions there the phrasing "standard built-in function" is used as well as the the phrase "the global object". When extending that specification to an environment that includes multiple global object, these phrases should still be consistently applied. If one built-in function works as if it was lexically bound to a specific global object while another use the current ambient global environment (whatever that might mean) then the implementation would seem to be internal inconsistent. The specification of direct eval is one of the places that use this phrasing in the spec.. so it is quite relevant whether an implementations is internally consistent in this regard.
I agree that the spec should deal with multiple global objects. I'm aware of a few of the subtleties of multiple globals, but I wouldn't be surprised if there are more. Thanks for raising this one. I created a placeholder strawman last week, because I've been intending to get into this topic. I've given it some contents now, but there's more work to be done. Here's what I've got so far:
http://wiki.ecmascript.org/doku.php?id=strawman:multiple_globals
The Chrome/Safari behavior would resurrect the vestigial EvalError, so I don't think it makes sense. It also contradicts the specification of the steps in the definition of the eval function.
I think I agree with this.
Firefox's behavior is inconsistent and seems not amenable to host-agnostic specification as ECMA would require.
I agree that we don't want things like the same-origin policy to show up in the ES spec. But I'm not sure I'm convinced there isn't a way to specify things loosely enough that the same-origin policy is consistent with whatever spec we end up with. I need to chew on that for a while, though.
Thus we are left with the IE9/Opera behavior, which seems sensible and natural to me: an eval function should always act in the context of the global from which it came.
Now this I think I don't agree with. The reason is that direct eval is really a static operator in ECMAScript, not an ordinary function call. It's more than a function call because, for example, it has access to the lexical environment. Now, it has this strange dynamic component where, if the value turns out dynamically not to be an evaluation function, then it degenerates into a function call. But this doesn't change the fact that direct eval is imbued with special static-operator powers not available to other function calls -- in particular, access to the lexical environment.
At any rate, I can see two semantics that seem reasonable to me. First let me make up some terminology: an "apparently direct eval" is a call that's of the right form to be a direct eval, but whether it actually turns out to be a direct eval depends on the dynamic value of the callee.
So the two alternatives:
- an apparently direct eval is a direct eval if its callee is any evaluation function
- an apparently direct eval is a direct eval only if its callee is the evaluation function of the same global context as the call site
In either alternative, though, the global object of the eval'ed code is the same global object as the call site. At least for same-origin, semantics #1 agrees with what Firefox is doing. But either one makes sense to me.
As for the same-origin stuff, I'm not yet sure what I think. I'll need to be schooled in the implications for the browser security model.
What needs to be done to standardize this behavior? And more generally, what needs to be done to begin standardizing multiple globals in ECMAScript, including issues like this one?
The above wiki page is a step in that direction, but there's more work to do. One thing that I think we have to do is nail down the notion of what callee values constitute an actual direct eval. It's not enough to say "the eval function," which is as bogus a phrase as "the global object."
[Adding WhatWG and public-script-coord (WebIDL) to the discussion. Discussion is ongoing :
- Start of thread : esdiscuss/2011-March/012915
- Strawman by Dave Herman: strawman:multiple_globals )]
Hi,
I am wondering if ECMAScript is the right "place" to standardize this. Web browsers face multiple global object via a mecanism which isn't under the ECMAScript scope (namely HTMLIFrameElements (www.whatwg.org/specs/web-apps/current-work/multipage/the-iframe-element.html#htmliframeelement) which aren't even DOM but HTMLDOM-specific). Other ECMAScript implementations could face the issue of having several globals but with other non-HTMLDOM use cases. For that reason, I think it'd be dangerous for ECMAScript to standardize anything on the topic.
To answer Jeff Walden's final questions, in my opinion, WebIDL would be a way better place to standardize this. ECMAScript could at most provide guidelines for ECMAScript implementors who would have to face this issue (gathering feedback and experience from implementations).
Cheers, David
Le 03/03/2011 23:39, Jeff Walden a écrit :
On Fri, Mar 4, 2011 at 5:53 AM, David Bruant <bruant at enseirb-matmeca.fr>wrote:
[Adding WhatWG and public-script-coord (WebIDL) to the discussion. Discussion is ongoing :
- Start of thread : esdiscuss/2011-March/012915
- Strawman by Dave Herman: strawman:multiple_globals )]
Hi,
I am wondering if ECMAScript is the right "place" to standardize this. Web browsers face multiple global object via a mecanism which isn't under the ECMAScript scope (namely HTMLIFrameElements ( www.whatwg.org/specs/web-apps/current-work/multipage/the-iframe-element.html#htmliframeelement ) which aren't even DOM but HTMLDOM-specific). Other ECMAScript implementations could face the issue of having several globals but with other non-HTMLDOM use cases. For that reason, I think it'd be dangerous for ECMAScript to standardize anything on the topic.
Hi David,
In general, if it is relevant to JS independent of hosting environment, i.e., applicable outside the browser, then it should be in ECMA rather than w3c/whatwg. For co-existence of multiple globals[1], as caused for example by heap sharing among same-origin iframes, most of the problematic language issues, such as the "eval" ambiguities Jeff raises, should be independent of hosting environment or the means by which multiple globals are created.
[1] or multiple "contexts" -- I agree with DaveH that we need better terminology!
OTOH, the so-called "semantics" of origins and co-existence of cross-origin globals should be kept on the w3c/whatwg side, as it is browser specific and should not pollute the rest of the language.
To answer Jeff Walden's final questions, in my opinion, WebIDL would be a way better place to standardize this. ECMAScript could at most provide guidelines for ECMAScript implementors who would have to face this issue (gathering feedback and experience from implementations).
I certainly agree that there needs to be a better interface boundary between w3c/whatwg specs and ECMA specs. Too many issues -- like the semantics of multiple globals -- fall in the gaps between the two organizations. The WebIDL's JS binding and public-script-coord are good places to work out these boundary issues. WebIDL, WebIDL's JS binding, and ES-next should co-evolve so that the rest of the w3c/whatwg specs ideally interface to JS only through WebIDL, and WebIDL's JS binding ideally depends only on standard elements of ES-next. Of course, there will always be a need to accommodate some special cases that can't be specified that way, like the "semantics" of cross-origin heap sharing.
My main regret in saying this is that WebIDL, having evolved from Corba IDL, is notationally almost pessimal for encouraging good JS interface design. We have all been suffering the consequences of that. I fear that WebIDL, and its entrenched place within the w3c/whatwg API design process, dooms us to a continual flood of browser APIs hostile to JS programmers.
On Mar 3, 2011, at 9:22 PM, David Herman wrote:
Thus we are left with the IE9/Opera behavior, which seems sensible and natural to me: an eval function should always act in the context of the global from which it came.
Now this I think I don't agree with. The reason is that direct eval is really a static operator in ECMAScript, not an ordinary function call. It's more than a function call because, for example, it has access to the lexical environment. Now, it has this strange dynamic component where, if the value turns out dynamically not to be an evaluation function, then it degenerates into a function call. But this doesn't change the fact that direct eval is imbued with special static-operator powers not available to other function calls -- in particular, access to the lexical environment.
Dave, I completely agree with this intent. "direct eval" was intended to perform as if it was an operator and hence has fixed call site specific semantics. We may quibble about the details of the specification regarding how we identify such direct evals but I don't think there is any question about how a direct eval is supposed to behave once you are sure it is a direct eval. If we think a call site is a direct eval but in fact it would access a foreign global object then it must have been misidentified as a direct eval as such access is not part of the intended direct eval behavior.
At any rate, I can see two semantics that seem reasonable to me. First let me make up some terminology: an "apparently direct eval" is a call that's of the right form to be a direct eval, but whether it actually turns out to be a direct eval depends on the dynamic value of the callee.
So the two alternatives:
- an apparently direct eval is a direct eval if its callee is any evaluation function
- an apparently direct eval is a direct eval only if its callee is the evaluation function of the same global context as the call site
In either alternative, though, the global object of the eval'ed code is the same global object as the call site. At least for same-origin, semantics #1 agrees with what Firefox is doing. But either one makes sense to me.
there is a third alternative: there is a single unique eval function object value taht is the initial value of the global "eval" binding in all global contexts.
A directly related questions is what do we expect for eval ===otherGlobal.eval assuming that the initial value of the global eval binding has not been changed in either context. Assuming that a script has executed: const originalEval = eval;
then consider the code: eval = otherGlobal.eval; if (eval !== originalEval) eval("");
Based upon original ES5 intent, the eval clause in the then clause should never be treated as a direct eval as it is clearly observable that eval has been tampered with. To me, this would preclude alternative 1 (assuming different eval functions are not ===) but it would allow for alternative 2 or my alternative 3.
It's when we look at the indirect eval case that other issues arise. A evaluation function when called (not a direct eval) has access to some global environment. Is it the global environment that the eval function originated from or is it the ambient global environment of the caller? Either is plausible. However, the global of origin option would preclude by alternative 3 above (single shared eval function) as it suggests that it would be possible for values that compare === to have observably different behavior when invoked. Not something we want. This seems to reduce us to two overall alternatives:
- each global context has a === unique evaluation function that captures that global context and which is used as the global context when the function is invoke.
- there is a single === unique evaluation function that is shared by all global contexts. When the function is called it uses the ambient global context of its caller.
The main problem with alternative 2 is that I don't believe that current browsers generally conform to the === requirement. Other interesting behavior to compare among browsers is: var a= new otherGobal.Array; var b= otherGobal.eval("new Array"); print(Object.getPrototypeOf([ ])===Object.getPrototypeOf(a)); print(Object.getPrototypeOf(a)===Object.getPrototypeOf(b));
I believe we will see that currently browser generally answer false to the first test and that results of the second test may vary. If built-ins generally behave as if they have captured their origin global environment, then it would seem consistent that the eval function should behave in the same consistent manner.
These all leads me to conclude that the likely resolution is:
- each global context has a === unique evaluation function that captures that global context and which is used as the global context when the function is called (not a direct eval).
As for the same-origin stuff, I'm not yet sure what I think. I'll need to be schooled in the implications for the browser security model.
What needs to be done to standardize this behavior? And more generally, what needs to be done to begin standardizing multiple globals in ECMAScript, including issues like this one?
The above wiki page is a step in that direction, but there's more work to do. One thing that I think we have to do is nail down the notion of what callee values constitute an actual direct eval. It's not enough to say "the eval function," which is as bogus a phrase as "the global object."
See above. The spec. actually uses the phrase "the standard built-in function defined in 15.1.2.1". This phrasing is used throughout the specification to mean the initial chapter 15 value of a built-in assuming a single such environment. This language is in ES5 to decouple references to these value from current mutable property values. It is totally specific in the context of the ES5 specification. The only thing bogus is how browsers inconsistently support multiple shared global ECMAScript environment. We probably will have to review each such "standard built-in" usage in the specification. I would hope that we find that there is a consistent multi-global interpretation that can be applied to (almost) all of them. But as we see in discussing eval in may be that reality forces some reality based inconsistency that we will need to pin down.
A few months back I noticed an interesting interaction between how direct eval is defined and multiple globals. What happens if, in one global, you call an eval from another global as if it were a direct eval?
var indirect = otherGlobal.eval; eval = indirect; print(eval("this") === this); print(indirect("this") === this);
Standards currently don't say what should happen here because it's multiple globals, so what should this do? IE9 and Opera print false both times for this. Firefox prints true, then false -- but only if the global and otherGlobal are from the same origin (so on pages with the same scheme/host/port, more or less). If they're from different origins (but have set document.domain to the same value) Firefox too prints false. Chrome and Safari throw an EvalError calling another window's eval (for both "direct" and indirect calls) without that window as |this| for the call.
The Chrome/Safari behavior would resurrect the vestigial EvalError, so I don't think it makes sense. It also contradicts the specification of the steps in the definition of the eval function. Firefox's behavior is inconsistent and seems not amenable to host-agnostic specification as ECMA would require. Thus we are left with the IE9/Opera behavior, which seems sensible and natural to me: an eval function should always act in the context of the global from which it came.
What needs to be done to standardize this behavior? And more generally, what needs to be done to begin standardizing multiple globals in ECMAScript, including issues like this one?