indirect eval spec too severe?
On Sun, Jan 18, 2009 at 12:47 PM, Mark S. Miller <erights at google.com> wrote:
The Mountain View draft says:
15.1.2 Function Properties of the Global Object 15.1.2.1 eval (x) When the eval function is called with one argument x, the following steps are taken: [...] If the value of the eval property is used in any way other than a direct call (that is, other than by the explicit use of its name as an Identifier which is the MemberExpression in a CallExpression), or if the eval property is assigned to, an EvalError exception may be thrown.
15.1.2.1.1 Strict Mode Restrictions If strict mode code uses the value of the eval property any way other than as a direct call (that is, other than by the explicit use of its name as an Identifier which is the MemberExpression in a CallExpression), or if the eval property is assigned to, an EvalError exception is thrown.
A related difficulty, not addressed by the above spec, is shadowing. Does the following program contain a direct call to eval (i.e., a use of the "eval operator")?
function foo(eval, str) { return eval(str); }
The eval variable being called here is not an alias for the "eval" property of the global object, though it may or may not have the same value. If the answer is that this is not a direct call, since the defining occurrence is local, then what about the second "eval" in the following code?
function foo(str1, str2) { eval(str1); eval(str2); }
Since foo may be called as foo('var eval = f', ..), the second eval may refer to a local or to a global eval. These ambiguities defeat the goal of distinguishing direct from indirect calls. Though the above specific problem cannot occur if foo is strict, I propose that, for all code, one may not define a local variable named "eval".
On Jan 18, 2009, at 6:03 PM, Mark S. Miller wrote:
A related difficulty, not addressed by the above spec, is shadowing.
Does the following program contain a direct call to eval (i.e., a
use of the "eval operator")?function foo(eval, str) { return eval(str); }
I realize the spec may have challenges for want of "compile time" vs.
"runtime" separation, but it's important to avoid overspecifying
direct eval. Here's why.
Implementations should not have to analyze name bindings and flow-
sensitive assignments. The standard approach is to check for a callee
named 'eval' and emit a distinguished instruction which acts like the
generic call instruction. The eval implementation can then interrogate
the current instruction (if any) to see whether it was called via the
"operator" form.
Since the implementation can tell when it has been called directly,
the compiler can cooperate with the eval implementation and optimize
aggressively any function body that does not contain such an 'eval'
callee in a call expression, without having to do analysis beyond
checking for a known identifer when parsing call expressions, and
conditionally setting a flag inspected by a later pass (or doing fixup
if one-pass and earlier assumptions were invalidated).
So the above eval call would be "apparently-direct", but at runtime,
if foo is called with a funarg other than the global eva function as
its first argument, then the call would be to some other function (or
callable -- or an error for non-callabl first arg, of course).
Note that there's no way at runtime to change an apparently-direct
eval into an indirect eval call. But an apparently-direct eval can
turn into a plain old call.
The eval variable being called here is not an alias for the "eval"
property of the global object, though it may or may not have the
same value. If the answer is that this is not a direct call,
That's the wrong answer -- it should be direct purely because the
callee name is 'eval'.
since the defining occurrence is local, then what about the second
"eval" in the following code?function foo(str1, str2) { eval(str1); eval(str2); }
Same answer -- these are two "direct" eval calls by the compiler's
lights.
Since foo may be called as foo('var eval = f', ..), the second eval
may refer to a local or to a global eval. These ambiguities defeat
the goal of distinguishing direct from indirect calls.
Not in practical implementations that nevertheless do no expensive
analyasis, as sketched above.
Though the above specific problem cannot occur if foo is strict, I
propose that, for all code, one may not define a local variable
named "eval".
This is unnecessary, and I'm not sure it's sufficient given your
assumptions. But we shouldn't have to worry about any shadowing or
rebinding issues if we can specify what popular implementations do.
On Sun, Jan 18, 2009 at 8:47 PM, Brendan Eich <brendan at mozilla.com> wrote:
On Jan 18, 2009, at 6:03 PM, Mark S. Miller wrote:
A related difficulty, not addressed by the above spec, is shadowing. Does
the following program contain a direct call to eval (i.e., a use of the "eval operator")?
function foo(eval, str) { return eval(str); }
[...] The standard approach is to check for a callee named 'eval' and emit a distinguished instruction which acts like the generic call instruction. [...] checking for a known identifer when parsing call expressions, [...] So the above eval call would be "apparently-direct", [...]
Note that there's no way at runtime to change an apparently-direct eval into an indirect eval call. But an apparently-direct eval can turn into a plain old call.
Good.
The eval variable being called here is not an alias for the "eval" property
of the global object, though it may or may not have the same value. If the answer is that this is not a direct call,
That's the wrong answer -- it should be direct purely because the callee name is 'eval'.
So the use of "property" in the spec:
If strict mode code uses the value of the eval property any way other than
as a direct call (that is, other than by the explicit use of its name as an Identifier which is the MemberExpression in a CallExpression), or if the eval property is assigned to, an EvalError exception is thrown.
means the original value of the global eval property (in which case the statement of the assignment constraint is wrong)? And "its name" above means specifically "eval", whether referring to a property or a local variable?
since the defining occurrence is local, then what about the second "eval"
in the following code?
function foo(str1, str2) { eval(str1); eval(str2); }
Same answer -- these are two "direct" eval calls by the compiler's lights.
Good.
Since foo may be called as foo('var eval = f', ..), the second eval may
refer to a local or to a global eval. These ambiguities defeat the goal of distinguishing direct from indirect calls.
Not in practical implementations that nevertheless do no expensive analyasis, as sketched above.
Given your other answers, I don't think we have a problem here.
Though the above specific problem cannot occur if foo is strict, I propose
that, for all code, one may not define a local variable named "eval".
This is unnecessary, and I'm not sure it's sufficient given your assumptions.
I think I'm happy with your earlier assumptions above; in which case it would be sufficient to prohibit defining local variables named "eval".
But we shouldn't have to worry about any shadowing or rebinding issues if we can specify what popular implementations do.
What Firefox current does (3.0.5):
function foo(a, b, c) { return a(b); }
foo(eval, 'c', 42); // returns 42
On Sun, Jan 18, 2009 at 10:17 PM, Mark S. Miller <erights at google.com> wrote:
Though the above specific problem cannot occur if foo is strict, I propose
that, for all code, one may not define a local variable named "eval".
This is unnecessary, and I'm not sure it's sufficient given your assumptions.
I think I'm happy with your earlier assumptions above; in which case it would be sufficient to prohibit defining local variables named "eval".
And (I agree) this restriction is unnecessary, so sufficiency is pointless. Right.
On Jan 18, 2009, at 10:17 PM, Mark S. Miller wrote:
So the use of "property" in the spec:
If strict mode code uses the value of the eval property any way
other than as a direct call (that is, other than by the explicit use
of its name as an Identifier which is the MemberExpression in a
CallExpression), or if the eval property is assigned to, an
EvalError exception is thrown.means the original value of the global eval property (in which
case the statement of the assignment constraint is wrong)? And "its
name" above means specifically "eval", whether referring to a
property or a local variable?
I don't know, I didn't write that language. It seems wrong at a
glance, not just because -- as you point out -- EvalError is too severe.
What Firefox current does (3.0.5):
function foo(a, b, c) { return a(b); } foo(eval, 'c', 42); // returns 42
From my understanding of your statements above (as well as previous
conversations), can we agree that FF's current behavior violates
both what ES3.1 does and should specify?
Yes, we've been over this before:
It's a bug we are eagerly planning to fix, probably not in Firefox 3.1
however -- next major release after.
Mark S. Miller wrote:
function foo(str1, str2) { eval(str1); eval(str2); }
Since foo may be called as foo('var eval = f', ..), the second eval may refer to a local or to a global eval. These ambiguities defeat the goal of distinguishing direct from indirect calls. Though the above specific problem cannot occur if foo is strict, I propose that, for all code, one may not define a local variable named "eval".
Note that even with that restriction, it is possible to introduce variables named 'eval' using 'with' (including in a strict function, if the 'with' construct was in a non-strict function).
Additional restrictions on eval may well be useful, but we need to make sure that we're actually achieving the desired language properties.
On Jan 18, 2009, at 12:47 PM, Mark S. Miller wrote:
The Mountain View draft says:
15.1.2 Function Properties of the Global Object 15.1.2.1 eval (x) When the eval function is called with one argument x, the following
steps are taken: [...] If the value of the eval property is used in any way other than a
direct call (that is, other than by the explicit use of its name as
an Identifier which is the MemberExpression in a CallExpression), or
if the eval property is assigned to, an EvalError exception may be
thrown.15.1.2.1.1 Strict Mode Restrictions If strict mode code uses the value of the eval property any way
other than as a direct call (that is, other than by the explicit use
of its name as an Identifier which is the MemberExpression in a
CallExpression), or if the eval property is assigned to, an
EvalError exception is thrown.I believe this spec is too severe, both for nonstrict and strict
code. My sense of the committee consensus is that an indirect eval
call should be unconditionally allowed, in which case the execution
context should be the global execution context, as already
adequately specified in 10.4.2. (In the committee's conversations,
we've referred to the "direct calls" of the spec language as the
"eval operator", and the spec's "indirect calls" as the "eval
function". I'm happy with either terminology.)
Certainly, actually raising exceptions in the cases cited is not
compatible with the Web, and is not much easier than treating indirect
calls (or apparently direct calls that end up being just normal calls)
as calls. That's what ES4 required and WebKit at least implements this
behavior.
, Maciej
2009/1/18 Mark S. Miller <erights at google.com>:
The Mountain View draft says:
15.1.2.1.1 Strict Mode Restrictions If strict mode code uses the value of the eval property any way other than as a direct call (that is, other than by the explicit use of its name as an Identifier which is the MemberExpression in a CallExpression), or if the eval property is assigned to, an EvalError exception is thrown.
I believe this spec is too severe
The intention of the spec is very close to saying that in the strict mode eval is a keyword. So why not to say so? To compensate for the severity of this restriction that prevents useful indirect eval calls the spec may add an explicit function with the semantics of the indirect eval, like compute.
, Igor
It's a bit late for this for 3.1, first point. Second, it's a
migration tax from non-strict mode that I'm pretty sure we do not
need. Indirect eval using only global scope creates no hazards other
than the usual ones associated with the extensible top level object.
Mark S. Miller wrote:
The Mountain View draft says:
15.1.2 Function Properties of the Global Object 15.1.2.1 eval (x) When the eval function is called with one argument x, the following steps are taken: [...] If the value of the eval property is used in any way other than a direct call (that is, other than by the explicit use of its name as an Identifier which is the MemberExpression in a CallExpression), or if the eval property is assigned to, an EvalError exception may be thrown.
15.1.2.1.1 Strict Mode Restrictions If strict mode code uses the value of the eval property any way other than as a direct call (that is, other than by the explicit use of its name as an Identifier which is the MemberExpression in a CallExpression), or if the eval property is assigned to, an EvalError exception is thrown.
I believe this spec is too severe, both for nonstrict and strict code. My sense of the committee consensus is that an indirect eval call should be unconditionally allowed, in which case the execution context should be the global execution context, as already adequately specified in 10.4.2. (In the committee's conversations, we've referred to the "direct calls" of the spec language as the "eval operator", and the spec's "indirect calls" as the "eval function". I'm happy with either terminology.)
As for restrictions on assignment to "eval", so long as the global "eval" might be assigned by nonstrict code, it doesn't help much to prevent it's assignment by strict code. Rather, in a "frame" (global object context) in which we wish to prevent assignment of global "eval", one can Object.defineProperty(window, "eval", {writable: false, configurable: false}), preventing anyone from assigning it.
If the spec were changed to require that the global 'eval' property is initially mutable, then that would imply that an implementation that makes it initially immutable (using a setter that throws EvalError) would not be conformant to ES3.1.
I see no reason why such an implementation should be considered nonconformant. Do we really want any code to be mutating the global 'eval' property? Do we have any evidence that real-world code is doing so? If not, then why change the spec to require it to work?
On Sat, Jan 24, 2009 at 3:36 PM, David-Sarah Hopwood <david.hopwood at industrial-designers.co.uk> wrote:
Mark S. Miller wrote:
As for restrictions on assignment to "eval", so long as the global "eval" might be assigned by nonstrict code, it doesn't help much to prevent it's assignment by strict code.
If the spec were changed to require that the global 'eval' property is initially mutable, then that would imply that an implementation that makes it initially immutable (using a setter that throws EvalError) would not be conformant to ES3.1.
I see no reason why such an implementation should be considered nonconformant. Do we really want any code to be mutating the global 'eval' property? Do we have any evidence that real-world code is doing so? If not, then why change the spec to require it to work?
The current spec language allows the global eval property to be, in effect, either mutable or not, benefiting no one. If we need to allow correct programs to be able to mutate it (in an initial environment in which it has not yet been frozen), then we should change the spec to require it to be initially mutable, so that correct programs can rely on this. If you are correct that we do not need to allow this possibility, then we should change the spec to require it to be initially immutable, so that correct programs can rely on that instead. However, in that latter case, a failed assignment should result in the normal behavior of a failed assignment, rather than throwing an EvalError.
On Jan 24, 2009, at 4:51 PM, Mark Miller wrote:
The current spec language allows the global eval property to be, in effect, either mutable or not, benefiting no one. If we need to allow correct programs to be able to mutate it (in an initial environment in which it has not yet been frozen), then we should change the spec to require it to be initially mutable, so that correct programs can rely on this.
ES3 already requires eval to be a mutable global property, chapter 15
intro:
Every other property described in this section has the attribute
{ DontEnum } (and no others) unless otherwise specified.
Postel's Law means real code depends on this. Proving it is impossible
but you can't cite absence of evidence to prove evidence of absence of
eval overwriting.
The idea of changing eval's attributes seems unmotivated, since we
don't need to incompatibly change ES3 to preserve the "operator eval"
behavior everyone wants. Unless there is a real problem not solved by
the notions in the current spec (based on some implementations and on
ES4) then we should not change global property attributes. There's too
much opportunity for incompatible (silent failure to overwrite)
behavior.
Brendan Eich wrote:
On Jan 24, 2009, at 4:51 PM, Mark Miller wrote:
The current spec language allows the global eval property to be, in effect, either mutable or not, benefiting no one. If we need to allow correct programs to be able to mutate it (in an initial environment in which it has not yet been frozen), then we should change the spec to require it to be initially mutable, so that correct programs can rely on this.
ES3 already requires eval to be a mutable global property, [...]
No, it does not, because attempting to assign global 'eval' may instead throw EvalError (ES3 section 15.1.2.1).
On Jan 25, 2009, at 12:24 AM, David-Sarah Hopwood wrote:
Brendan Eich wrote:
On Jan 24, 2009, at 4:51 PM, Mark Miller wrote:
The current spec language allows the global eval property to be, in effect, either mutable or not, benefiting no one. If we need to
allow correct programs to be able to mutate it (in an initial
environment in which it has not yet been frozen), then we should change the spec to require it to be initially mutable, so that correct programs can
rely on this.ES3 already requires eval to be a mutable global property, [...]
No, it does not, because attempting to assign global 'eval' may
instead throw EvalError (ES3 section 15.1.2.1).
Right you are -- thanks. That makes it a standard option for
implementations, unlike the option of silently failing to assign to
eval due to a ReadOnly attribute (what I took Mark to be proposing if
eval assignment were not something required for compatibility).
The fact remains that no implementation I know of throws EvalError on
assignment to the eval property of the global object. The spec words
allowing EvalError to be thrown are probably dead letters.
IIRC, Opera implementations in the past threw EvalError for indirect
evals (not sure about assignment to eval), but doing so was too web-
incompatible. Cc'ing Chris Pine to confirm.
Brendan Eich wrote:
IIRC, Opera implementations in the past threw EvalError for indirect evals (not sure about assignment to eval), but doing so was too web-incompatible. Cc'ing Chris Pine to confirm.
Correct. Google Docs was the hay bale that broke this camel's back.
Chris
The Mountain View draft says:
I believe this spec is too severe, both for nonstrict and strict code. My sense of the committee consensus is that an indirect eval call should be unconditionally allowed, in which case the execution context should be the global execution context, as already adequately specified in 10.4.2. (In the committee's conversations, we've referred to the "direct calls" of the spec language as the "eval operator", and the spec's "indirect calls" as the "eval function". I'm happy with either terminology.)
As for restrictions on assignment to "eval", so long as the global "eval" might be assigned by nonstrict code, it doesn't help much to prevent it's assignment by strict code. Rather, in a "frame" (global object context) in which we wish to prevent assignment of global "eval", one can Object.defineProperty(window, "eval", {writable: false, configurable: false}), preventing anyone from assigning it.
Given
and that eval has it's original value, then
bar's x foo('"use strict; var x = 7;'); // returns 16 foo('x = 7;'); // returns 14 foo('"use strict"; x = 7;'); // returns 14 // in all cases, global x is unaffected
whereas
and
For these latter cases, it should make no difference whether bar() is strict.