eval
I'm trying to implement an ES4-compliant version of eval, but I'm
having trouble understanding what the specified behavior is.
In ES4,
eval(x)
is distinct from all of
window.eval(x)
eval.call(myThisObject, x)
frames[0].eval(x)
in that the first form is an operator, and the last three forms are
function calls.
Right?
OK, for the eval operator, the eval function, the eval function called
with a specified 'this' object, and the eval function called on
another global object, respectively:
What scope chain should be used?
What variable object should be used?
What value should 'this' take on?
What gets called if window.eval has been overridden, as in (at global
scope):
eval = function() { return "overridden"; } eval(x);
or
window.eval = function() { return "overridden"; } window.eval(x);
What gets called if the "eval" identifier has been shadowed by a
variable in scope, as in:
with({ eval: function() { return "overridden"; } }) { eval(x); }
or
try { throw function() { return "overridden"; }; } catch(eval) { eval(x); }
?
Thanks, Geoff
Lars,
Could you comment on this?
I see from your post @ www.nabble.com/Eval-invisible-let-bindings-td14182651.html#a14182651
that you have definite ideas about "the current ES4 proposal" for
eval. What is that proposal? Is it written down anywhere?
I tried the reference implementation, but it seems to implement eval
as a simple string identity operation:
~/src/es4$ ./es4 ECMAScript Edition 4 RI v0.0M2 (Fri Feb 15 13:37:13 2008)
x=1
1
eval('x')
x
I see some notes written down @ proposals:resurrected_eval
and doku.php?
id=discussion:resurrected_eval, but they make no mention of "this",
and both posts point to open questions, not a finished proposal.
I searched bugs.ecmascript.org but the best thing I found was ticket
#226, which discusses when ES4-style eval should kick in, not what it
is.
Is there anywhere else I should be looking?
Thanks, Geoff
-----Original Message----- From: es4-discuss-bounces at mozilla.org [mailto:es4-discuss-bounces at mozilla.org] On Behalf Of Geoffrey Garen Sent: 5. mars 2008 19:26 To: es4-discuss Discuss Subject: eval
Hi all.
I'm trying to implement an ES4-compliant version of eval, but I'm having trouble understanding what the specified behavior is.
In ES4,
eval(x)
is distinct from all of
window.eval(x) eval.call(myThisObject, x) frames[0].eval(x)
in that the first form is an operator, and the last three forms are function calls.
Right?
Yes. This is a function call form too:
w = eval w(x)
(Note that only the operator form is required by the ES3 spec.)
OK, for the eval operator, the eval function, the eval function called with a specified 'this' object, and the eval function called on another global object, respectively:
What scope chain should be used?
What variable object should be used?
What value should 'this' take on?
What gets called if window.eval has been overridden, as in (at global scope):
eval = function() { return "overridden"; } eval(x);
or
window.eval = function() { return "overridden"; } window.eval(x);
What gets called if the "eval" identifier has been shadowed by a variable in scope, as in:
with({ eval: function() { return "overridden"; } }) { eval(x); }
or
try { throw function() { return "overridden"; }; } catch(eval) { eval(x); }
?
In my opinion the following is roughly right:
obj.eval(x)
if eval is the original global eval function then
if obj is an ES global object (window, frame) then
invoke eval as follows:
the scope chain holds that window (global) object only
the variable object is that window object
the value of "this" is that window object
else
this is an error
else
we don't care; invoke the method eval on obj
eval(x) look up "eval" if the found value v is the original eval function and the binding object x holding eval is an ES global object and the global object on the scope chain for v is x then invoke eval as follows: the scope chain is the lexical chain in effect at the point of invocation the variable object is the innermost variable object in effect (which is to say that it excludes binding objects introduced for "let", "catch", named function expressions, "switch type", and note also that code at the top level of a class is static initialization code so the variable object is the global object) the value of "this" is the global object x else we don't care; invoke v as a normal function
Note a couple of things. In the case obj.eval(x) all the logic can be moved into the eval function itself, and in the case of a "masquerading eval" as I showed earlier that is exactly what you want too. Also, to handle eval(x) the generated code needs to look roughly like what's outlined above, ie, it becomes a wee bit bulky. Probably not a problem in practice.
Geoff,
I hope my answer to your previous post helps you along. Since I had to write that, though, I should just take the prose and copy it into a draft proposal and we could zero in on something definite and useful in short order. Stay tuned.
--lars
-----Original Message----- From: Geoffrey Garen [mailto:ggaren at apple.com] Sent: 6. mars 2008 14:00 To: Lars Hansen Cc: es4-discuss Discuss Subject: Re: eval
Lars,
Could you comment on this?
I see from your post @ www.nabble.com/Eval-invisible-let-bindings-td14182651.h
tml#a14182651
On 06/03/2008, Lars Hansen <lhansen at adobe.com> wrote:
eval(x) look up "eval" if the found value v is the original eval function and the binding object x holding eval is an ES global object and the global object on the scope chain for v is x then
Is this the same x as in "eval(x)" above? The way the sentence runs, that seems not to be the case. If this is going into spec text, another letter should probably be used to avoid any ambiguity.
invoke eval as follows: the scope chain is the lexical chain in effect at the point of
invocation the variable object is the innermost variable object in effect (which is to say that it excludes binding objects introduced for "let", "catch", named function expressions, "switch type", and note also that code at the top level of a class is static initialization code so the variable object is the global object) the value of "this" is the global object x
And here again.
Both good points. Thanks.
Lars Hansen wrote:
In my opinion the following is roughly right:
obj.eval(x) if eval is the original global eval function then if obj is an ES global object (window, frame) then invoke eval as follows: the scope chain holds that window (global) object only the variable object is that window object the value of "this" is that window object
else this is an error else we don't care; invoke the method eval on objeval(x) look up "eval" if the found value v is the original eval function and the binding object x holding eval is an ES global object and the global object on the scope chain for v is x then invoke eval as follows: the scope chain is the lexical chain in effect at the point of invocation the variable object is the innermost variable object in effect (which is to say that it excludes binding objects introduced for "let", "catch", named function expressions, "switch type", and note also that code at the top level of a class is static initialization code so the variable object is the global object) the value of "this" is the global object x else we don't care; invoke v as a normal function
Seems reasonable, although anything dealing with eval evil can get tricky.
What about the other cases such as:
obj.foo(x) foo(x) new foo(x)
where foo happens to contain the original eval function. Do you try to do the eval, throw an EvalError, or do something else? ES3 gave the implementations a choice of either doing the eval or throwing an EvalError.
Waldemar
-----Original Message----- From: Waldemar Horwat [mailto:waldemar at google.com] Sent: 12. mars 2008 19:08 To: Lars Hansen Cc: Geoffrey Garen; es4-discuss Discuss Subject: Re: eval
Lars Hansen wrote:
In my opinion the following is roughly right:
obj.eval(x) ...
eval(x) ...
Seems reasonable, although anything dealing with eval evil can get tricky.
No question about that.
What about the other cases such as:
obj.foo(x) foo(x) new foo(x)
where foo happens to contain the original eval function. Do you try to do the eval, throw an EvalError, or do something else?
obj.foo(x) is fine if obj is the same global object that foo was closed in; foo(x) is always fine. In both cases, and like for obj.eval, the environment that will be used for evaluation is the global one.
On the other hand, new foo(x) will not be allowed because the 'this' object is not a global object.
(EvalError is thrown at run-time in all these cases if an illegal use is detected.)
In summary, if it is trivially lexically detectable that something could be an invocation of 'eval' (by the expression having the abstract form M(E, ...) where M is "eval" or "intrinsic::eval") then the invocation, if found at run-time to be an invocation of the pre-defined eval function, will have access to the lexical environment. In all other cases an invocation of eval is just a function/constructor call, which only has access to the global environment it was closed in; the eval function body tests that its 'this' object is the global object it was closed in and throws an EvalError if it was not.
All of this is covered in greater detail (with background materials, rationale, etc) in the draft spec for the global object. What's written there is consistent with what I wrote here earlier but probably better developed.
Hello, I've been lurking here for a couple of weeks now. I put together an ECMAScript 3-in-ECMAScript 3 CPS transformer so I could use javascript to do some heavy-lifting for a commercial application, and decided I would try my hand at writing an ECMAScript 4 compiler/interpreter in Haskell using the "hsplugins" interface and Template Haskell to dynamically compile code fragments as needed.
To that end, I have been combing through the public export of the es4 wiki, and while I have seen mention of a comment or two recommending that eval be turned into an expression level keyword, I am curious to know if a more tenable resolution has been proposed. As has been noted, eval can't be written as a function in the language presently. * *It is currently noted as a function on the global object, but this leaves open to question what the behavior of a function variable assigned to 'eval' is.
Investigating yields:
var myeval = eval; var foo = function() { var foo = 12; myeval("foo = 13"); print(foo); } foo(); print(foo);*
*Spidermonkey prints: 12 13
while JScript.NET, jscript.net prints (with an appropriate
definition for print) 13 function() { ... }
While one could argue that the JScript.NET implementation is more correct for the interpretation of *"*eval as a function," it is not conducive to being able to construct any form of optimized compiled form for the language in strict mode.
I also realize that eval can't be granted full keyword status without breaking legacy code that relies on the old Object.eval behavior or the ability to run across windows, but I am curious if the Spidermonkey behavior exhibited above might be formalized or allowed by making it so that eval occuring in a primary expression context to be treated like a keyword, much like the other contextual keywords get, set, each, etc.
This would allow us to code to skim the AST for "eval expression" as a primary expression or "with" and optimize the representation of lexical scopes if they are not present in the subtree of a given function/block. This wouldn't violate the one-pass rule because it would be a strict optimization anyways and is almost invisible, and would appear to affect the vast majority of lexical scopes.
Making it 'eval expression' instead of a function call makes the difference in semantics clear, and still allows the legacy syntax, because the expression can be parenthesized.
The existing "otherwindow.eval" cases can still function by way of invoking a member function with the same name as the non-reserved keyword.
Consequences and variations:
Using it formally as a non-reserved keyword in the grammar might or might not be more headache than just skimming for the appropriate identifier in 'primary' position, but either way this approach might be a viable partial resolution to the efficiency problem it poses. ** Attempting to rename 'eval' to 'evaluate' by "var evaluate = eval;" would grab a reference to the member function eval on the global object, and calls to evaluate would resolve in the global scope rather than the local scope. On the other hand, from the spidermonkey example above, it seems like some implementations out there do something like this way already, so any existing code out there that relies on the jscript behavior won't work in a good fraction of existing browsers anyways.
An alternative behavior would be to make eval a bound member function of the current lexical scope, then the statement 'var evaluate = eval;' actualy gains the power to say that i want to evaluate the expression in whatever the current lexical scope is. This would allow you to return the result function and use it multiple times accessing the appropriate closure, and you don't pay for the feature in the scopes that don't use eval. One advantage of this is that it would allow function decorator hacks that need to evaluate their output as a string in the function's originating context like: eval((function (...) {...}).cps()) to be reexpressed as (function(..){...}).cps(eval); where the eval function passed in grants access to the current lexical scope, and since its in primary position, it is clear that the current function scope needs to support eval.
How should either of these interact with a user who redefines the global object's eval function or creates a function named eval that is in scope?
The above approach would let actionscript-style strict implemetations continue to enjoy the compiled speed advantage except where the flexibility of eval is needed.
The alternative behavior mentioned above lets you extend the power of eval to evaluate in a particular scope chain repeatedly as a playground of sorts, but might be needlessly complex and doesn't follow the existing behavior of any existing implementation.
Anyways, am I totally off the mark here? In either case, I think perhaps "what you can assume about eval" should at least be specified.
-Edward Kmett slipwave.info