indirect eval spec too severe?

# Mark S. Miller (16 years ago)

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.

Given

var x = 5;
function foo(str) {
  var x = 8;
  function bar() {
    eval(str);
    x = x*x;
  }
  bar();
  return x;
}

and that eval has it's original value, then

foo('');                        // returns 16
foo('var x = 7');               // returns 8, since 14 is assigned to

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

var x = 5;
function foo(str) {
  var x = 8;
  function bar() {
    "use strict";
    eval(str);
    x = x*x;
  }
  bar();
  return x;
}

foo('');                        // returns 16
foo('var x = 7');               // returns 16
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

and

var x = 5;
function foo(str) {
  var x = 8;
  function bar() {
    (true && eval)(str);
    x = x*x;
  }
  bar();
  return x;
}

foo('');                        // global x in unaffected
foo('var x = 7');               // sets global x to 7
foo('"use strict; var x = 7;'); // sets global x to 7
foo('x = 7;');                  // sets global x to 7
foo('"use strict"; x = 7;');    // sets global x to 7
// in all cases, return 16

For these latter cases, it should make no difference whether bar() is strict.

# Mark S. Miller (16 years ago)

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".

# Brendan Eich (16 years ago)

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.

# Mark S. Miller (16 years ago)

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
# Mark S. Miller (16 years ago)

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.

# Brendan Eich (16 years ago)

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:

esdiscuss/2008-October/007961

It's a bug we are eagerly planning to fix, probably not in Firefox 3.1
however -- next major release after.

# David-Sarah Hopwood (16 years ago)

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.

# Maciej Stachowiak (16 years ago)

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

# Igor Bukanov (16 years ago)

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

# Brendan Eich (16 years ago)

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.

# David-Sarah Hopwood (16 years ago)

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?

# Mark Miller (16 years ago)

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.

# Brendan Eich (16 years ago)

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.

# David-Sarah Hopwood (16 years ago)

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).

# Brendan Eich (16 years ago)

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.

# Chris Pine (16 years ago)

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