Strict mode eval

classic Classic list List threaded Threaded
20 messages Options
Reply | Threaded
Open this post in threaded view
|

Strict mode eval

Andreas Rossberg-4
I'm a bit puzzled regarding the meaning of "use strict" in the context
of higher-order uses of eval.

Consider the following program. What is the expected result of the latter calls?

---------------------
var x = 3

function call_eval() { x = 3; eval("var x = 4"); return x }
function call_eval_strict() { "use strict"; x = 3; eval("var x = 4"); return x }

function get_eval() { return eval }
function get_eval_strict() { "use strict"; return eval }

function call_f(f) { x = 3; f("var x = 4"); return x }
function call_f_strict(f) { "use strict"; x = 3; f("var x = 4"); return x }

call_eval()  // 4
call_eval_strict()  // 3

call_f(eval)  // 4
call_f(get_eval())  // 4
call_f(get_eval_strict())  // ?

call_f_strict(eval)  // ?
call_f_strict(get_eval())  // ?
call_f_strict(get_eval_strict())  // ?

(function() { "use strict"; return call_f_strict(get_eval_strict()) })()  // ?
---------------------

V8 bleeding edge currently returns 4 for all of the latter calls, but
that does not seem quite right to me. Especially for the last two
cases
that would practically amount to a loop hole in strict mode. But where
does the spec say differently?

I'd be happy for any enlightenment.

Thanks,
/Andreas
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Strict mode eval

Oliver Hunt-2

On May 10, 2011, at 10:16 AM, Andreas Rossberg wrote:

> I'm a bit puzzled regarding the meaning of "use strict" in the context
> of higher-order uses of eval.
>
> Consider the following program. What is the expected result of the latter calls?
>
> ---------------------
> var x = 3
>
> function call_eval() { x = 3; eval("var x = 4"); return x }
sets global x to 3, eval operator introduces a local binding for x and sets it to 4.  We return the local binding (4) global x is 3

> function call_eval_strict() { "use strict"; x = 3; eval("var x = 4"); return x }
sets global x binding to 3, eval operator gets its own environment record with a binding for x, it sets it to 4, function returns value of global x binding
>
> function get_eval() { return eval }
> function get_eval_strict() { "use strict"; return eval }
Both return the same eval function (not the operator)

>
> function call_f(f) { x = 3; f("var x = 4"); return x }
> function call_f_strict(f) { "use strict"; x = 3; f("var x = 4"); return x }
>
> call_eval()  // 4
> call_eval_strict()  // 3
>

All your other calls are calls to the eval function, not the eval operator, and on those cases the containing environment record is the global scope.

> ---------------------
>
> V8 bleeding edge currently returns 4 for all of the latter calls, but
> that does not seem quite right to me. Especially for the last two
> cases
> that would practically amount to a loop hole in strict mode. But where
> does the spec say differently?

eval is a little gnarly in ES5, essentially there are two modes: eval the function and eval the operator.

Any piece of code (ignoring overrides/shadowing) that does
eval(....)

Is using the eval operator, in which case the containing environment record of the code executed by eval, is the environment record in which the call is made.

Any form of indirection to eval that leads to it being called by a name other than eval means you're calling the function form.  In the function form the containing environment record for the code executed by eval is the global env. record.

I can't recall off the top of my head whether
function f(eval) { eval(...) }
will result in behaviour that's considered to be the operator or function form (i'can't recall whether th semantics define the env. record the resolved eval must be boun

--Oliver

>
> I'd be happy for any enlightenment.
>
> Thanks,
> /Andreas
> _______________________________________________
> es-discuss mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Strict mode eval

kangax
In reply to this post by Andreas Rossberg-4


On Tue, May 10, 2011 at 1:16 PM, Andreas Rossberg <[hidden email]> wrote:
I'm a bit puzzled regarding the meaning of "use strict" in the context
of higher-order uses of eval.

Consider the following program. What is the expected result of the latter calls?

---------------------
var x = 3

function call_eval() { x = 3; eval("var x = 4"); return x }
function call_eval_strict() { "use strict"; x = 3; eval("var x = 4"); return x }

function get_eval() { return eval }
function get_eval_strict() { "use strict"; return eval }

function call_f(f) { x = 3; f("var x = 4"); return x }
function call_f_strict(f) { "use strict"; x = 3; f("var x = 4"); return x }

call_eval()  // 4
call_eval_strict()  // 3

This is correct.
 

call_f(eval)  // 4
call_f(get_eval())  // 4
call_f(get_eval_strict())  // ?

The last one should return 4 as well. There's no difference between `eval`, `get_eval()`, and `get_eval_strict()` in this context. All of them are essentially a reference to a built-in, global `eval` function. Strict mode only affects calling behavior of `eval`. The retrieval of `eval` object is still the same.

But something else comes into play here, and that something is indirect eval call.
 

call_f_strict(eval)  // ?
call_f_strict(get_eval())  // ?
call_f_strict(get_eval_strict())  // ?

When `call_f_strict` executes and `eval` is called from within it, it is actually an indirect eval call. Why? Because the reference name is not `eval`, but `f` and so it doesn't satisfy direct eval call requirement (see http://es5.github.com/#x15.1.2.1.1)

In ES5 indirect eval calls execute in global execution context (http://es5.github.com/#x10.4.2) and so all 3 last calls actually end up executing `var x = 4` in global scope (overwriting global `x`).

You can see that if you were to create local `x` variable in `call_f_strict` that `x` would be unaffected by eval calls.

function call_f_strict(f) { 
  "use strict"; 
  var x = 3;
  f("var x = 4");
  return x; // should return `3`, since `x = 4` was executed outside of this local scope
}


[...]

-- 
kangax

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Strict mode eval

Lasse Reichstein
In reply to this post by Andreas Rossberg-4
On Tue, 10 May 2011 19:16:40 +0200, Andreas Rossberg <[hidden email]>  
wrote:

> I'm a bit puzzled regarding the meaning of "use strict" in the context
> of higher-order uses of eval.
>
> Consider the following program. What is the expected result of the  
> latter calls?
>
> ---------------------
> var x = 3
>
> function call_eval() { x = 3; eval("var x = 4"); return x }
> function call_eval_strict() { "use strict"; x = 3; eval("var x = 4");  
> return x }
>
> function get_eval() { return eval }
> function get_eval_strict() { "use strict"; return eval }

These two, get_eval and get_eval_strict, should be equivalent. They both  
return
the same value, the eval function.

> function call_f(f) { x = 3; f("var x = 4"); return x }
> function call_f_strict(f) { "use strict"; x = 3; f("var x = 4"); return  
> x }
>
> call_eval()  // 4
> call_eval_strict()  // 3

Agree.

>
> call_f(eval)  // 4
> call_f(get_eval())  // 4
> call_f(get_eval_strict())  // ?

All equivalent, they call the same function with the same argument.

> call_f_strict(eval)  // ?
> call_f_strict(get_eval())  // ?
> call_f_strict(get_eval_strict())  // ?

And again equivalent, so the question is what the result is.

The call to f in call_f_strict is an indirect call to eval in a strict
context.
That means that the eval code is not strict code (10.1.1) since it's
not a direct call, and it doesn't contain the "use strict" directive.

I.e., its evaluated as a normal indirect call to eval, which uses the
global environment as VariableEnvironment and LexicalEnvironment, and
the "var x = 4;" will overwrite the global variable x.

I.e., 4 is the correct answer.

> (function() { "use strict"; return call_f_strict(get_eval_strict())  
> })()  // ?

And this is the same (same function called with same argument). That
it happens in a strict environment doesn't change anything.

> ---------------------
>
> V8 bleeding edge currently returns 4 for all of the latter calls, but
> that does not seem quite right to me. Especially for the last two
> cases

Seems this is correct.

> that would practically amount to a loop hole in strict mode. But where
> does the spec say differently?

It's only a loop hole in strict mode if the strict code has access to the
unmodified eval function. You can get out of strict mode too, if you have
access to the original Function constructor.
What you can't do is to execute non-strict code inside a strict-mode  
context.

/L
--
Lasse Reichstein Holst Nielsen - [hidden email]
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Strict mode eval

Allen Wirfs-Brock
In reply to this post by Oliver Hunt-2

On May 10, 2011, at 10:50 AM, Oliver Hunt wrote:

> I can't recall off the top of my head whether
> function f(eval) { eval(...) }
> will result in behaviour that's considered to be the operator or function form (i'can't recall whether th semantics define the env. record the resolved eval must be boun
>
> --Oliver

This s a direct eval, if f is declared in a non-strict context and the original built-in eval function is passed as the argument to f.  Put simply, to be a "direct eval" the invocation of the function must use the identifier name "eval" and the value the identifier must be the built-in eval  function.  However, the binding for "eval" doesn't need to come from the global environment record.

If f is defined in a strict context,  the use of eval as a formal parameter name is an early error.

Allen


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Strict mode eval

Andreas Rossberg-4
Thanks to everybody for clearing up my confusion. The thing I had
missed in the spec was Section 10.4.2. And of course, my example was
too simplistic because it couldn't distinguish caller and global
context.

At the risk of reviving old discussions, are there any sources
explaining the rationale behind the current design? The "obvious"
solution to me would have been having two internal variants/modes of
(or "entry points" to) the eval function, one strict, one non-strict.
And depending on the lexical strict mode, the identifier "eval" would
be bound to the right one.

Thanks,
/Andreas
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Strict mode eval

Mark S. Miller-2


On Wed, May 11, 2011 at 4:42 AM, Andreas Rossberg <[hidden email]> wrote:
Thanks to everybody for clearing up my confusion. The thing I had
missed in the spec was Section 10.4.2. And of course, my example was
too simplistic because it couldn't distinguish caller and global
context.

At the risk of reviving old discussions, are there any sources
explaining the rationale behind the current design? The "obvious"
solution to me would have been having two internal variants/modes of
(or "entry points" to) the eval function, one strict, one non-strict.
And depending on the lexical strict mode, the identifier "eval" would
be bound to the right one.

I don't think I understand the suggestion. What would the following code do:

    // non-strict outer context

    function f(eval) {
      var f = eval;
      function g() {
        "use strict";
        eval(str); // [1]
        (1,eval)(str); // [2]
        f(str); // [3]
      }
    }

[1] If the outer eval is bound to the global eval function then this is a direct eval, which therefore lexically inherits strictness. So no problem here.

[2] The 'eval' identifier is bound in non-strict code and used in strict code.

[3] Strict code makes no use here of a lexical binding of the identifier "eval".


A previous approach which we rejected was to make strictness dynamically scoped, so all three of the above calls would do strict evals. This was rejected to avoid the problems of dynamic scoping. Are you suggesting a rule that would affect #2 but not #3? If so, IIRC no such rule was previously proposed.


Thanks,
/Andreas
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss



--
    Cheers,
    --MarkM

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Strict mode eval

Andreas Rossberg-4
On 11 May 2011 18:31, Mark S. Miller <[hidden email]> wrote:

> On Wed, May 11, 2011 at 4:42 AM, Andreas Rossberg <[hidden email]>
> wrote:
>>
>> Thanks to everybody for clearing up my confusion. The thing I had
>> missed in the spec was Section 10.4.2. And of course, my example was
>> too simplistic because it couldn't distinguish caller and global
>> context.
>>
>> At the risk of reviving old discussions, are there any sources
>> explaining the rationale behind the current design? The "obvious"
>> solution to me would have been having two internal variants/modes of
>> (or "entry points" to) the eval function, one strict, one non-strict.
>> And depending on the lexical strict mode, the identifier "eval" would
>> be bound to the right one.
>
> I don't think I understand the suggestion. What would the following code do:
>     // non-strict outer context
>     function f(eval) {
>       var f = eval;
>       function g() {
>         "use strict";
>         eval(str); // [1]
>         (1,eval)(str); // [2]
>         f(str); // [3]
>       }
>     }
> [1] If the outer eval is bound to the global eval function then this is a
> direct eval, which therefore lexically inherits strictness. So no problem
> here.
> [2] The 'eval' identifier is bound in non-strict code and used in strict
> code.
> [3] Strict code makes no use here of a lexical binding of the identifier
> "eval".
>
> A previous approach which we rejected was to make strictness dynamically
> scoped, so all three of the above calls would do strict evals. This was
> rejected to avoid the problems of dynamic scoping. Are you suggesting a rule
> that would affect #2 but not #3? If so, IIRC no such rule was previously
> proposed.

I'm actually suggesting plain lexical scoping. :)

Basically, what I'm saying is that the directive "use strict" could
simply amount to shadowing the global eval via an implicit "var eval =
eval_strict" (unless global eval has already been shadowed lexically).
So all uses of the identifier `eval' in that scope would refer to its
strict version, no matter when, where, or how you ultimately invoke
it. Consequently, all your three examples would behave the same, only
depending on the argument to f.

Re dynamic scoping: in fact, I would claim that the current rule _is_
a form of dynamic scoping -- eval behaves differently when its caller
is strict. Except that this rule only applies when the syntactic
identifier in the call happens to be "eval". Regardless of the above,
I wonder why this special case was introduced. Why is the rule not
applied to all applications uniformly? I feel that I am missing
something.

/Andreas
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Strict mode eval

Mark S. Miller-2


On Wed, May 11, 2011 at 10:31 AM, Andreas Rossberg <[hidden email]> wrote:
On 11 May 2011 18:31, Mark S. Miller <[hidden email]> wrote:
> On Wed, May 11, 2011 at 4:42 AM, Andreas Rossberg <[hidden email]>
> wrote:
>>
>> Thanks to everybody for clearing up my confusion. The thing I had
>> missed in the spec was Section 10.4.2. And of course, my example was
>> too simplistic because it couldn't distinguish caller and global
>> context.
>>
>> At the risk of reviving old discussions, are there any sources
>> explaining the rationale behind the current design? The "obvious"
>> solution to me would have been having two internal variants/modes of
>> (or "entry points" to) the eval function, one strict, one non-strict.
>> And depending on the lexical strict mode, the identifier "eval" would
>> be bound to the right one.
>
> I don't think I understand the suggestion. What would the following code do:
>     // non-strict outer context
>     function f(eval) {
>       var f = eval;
>       function g() {
>         "use strict";
>         eval(str); // [1]
>         (1,eval)(str); // [2]
>         f(str); // [3]
>       }
>     }
> [1] If the outer eval is bound to the global eval function then this is a
> direct eval, which therefore lexically inherits strictness. So no problem
> here.
> [2] The 'eval' identifier is bound in non-strict code and used in strict
> code.
> [3] Strict code makes no use here of a lexical binding of the identifier
> "eval".
>
> A previous approach which we rejected was to make strictness dynamically
> scoped, so all three of the above calls would do strict evals. This was
> rejected to avoid the problems of dynamic scoping. Are you suggesting a rule
> that would affect #2 but not #3? If so, IIRC no such rule was previously
> proposed.

I'm actually suggesting plain lexical scoping. :)

Basically, what I'm saying is that the directive "use strict" could
simply amount to shadowing the global eval via an implicit "var eval =
eval_strict" (unless global eval has already been shadowed lexically).
So all uses of the identifier `eval' in that scope would refer to its
strict version, no matter when, where, or how you ultimately invoke
it. Consequently, all your three examples would behave the same, only
depending on the argument to f.

I don't understand. If "use strict" implicitly introduces a "var eval = eval_strict;" at the position it occurs, then wouldn't #3 in my example still evaluate non-strict? 

I'll answer the rest once I understand your proposal.


Re dynamic scoping: in fact, I would claim that the current rule _is_
a form of dynamic scoping -- eval behaves differently when its caller
is strict. Except that this rule only applies when the syntactic
identifier in the call happens to be "eval". Regardless of the above,
I wonder why this special case was introduced. Why is the rule not
applied to all applications uniformly? I feel that I am missing
something.

/Andreas



--
    Cheers,
    --MarkM

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Strict mode eval

Felix-54
I think he's proposing that the 'eval' function would be a magical
function that can inspect its calling environment to determine whether
it's being called in a lexically strict context or not, and based on
that it would act like the strict eval operator, or not.

On Wed, May 11, 2011 at 11:30, Mark S. Miller <[hidden email]> wrote:

>
>
> On Wed, May 11, 2011 at 10:31 AM, Andreas Rossberg <[hidden email]>
> wrote:
>>
>> On 11 May 2011 18:31, Mark S. Miller <[hidden email]> wrote:
>> > On Wed, May 11, 2011 at 4:42 AM, Andreas Rossberg <[hidden email]>
>> > wrote:
>> >>
>> >> Thanks to everybody for clearing up my confusion. The thing I had
>> >> missed in the spec was Section 10.4.2. And of course, my example was
>> >> too simplistic because it couldn't distinguish caller and global
>> >> context.
>> >>
>> >> At the risk of reviving old discussions, are there any sources
>> >> explaining the rationale behind the current design? The "obvious"
>> >> solution to me would have been having two internal variants/modes of
>> >> (or "entry points" to) the eval function, one strict, one non-strict.
>> >> And depending on the lexical strict mode, the identifier "eval" would
>> >> be bound to the right one.
>> >
>> > I don't think I understand the suggestion. What would the following code
>> > do:
>> >     // non-strict outer context
>> >     function f(eval) {
>> >       var f = eval;
>> >       function g() {
>> >         "use strict";
>> >         eval(str); // [1]
>> >         (1,eval)(str); // [2]
>> >         f(str); // [3]
>> >       }
>> >     }
>> > [1] If the outer eval is bound to the global eval function then this is
>> > a
>> > direct eval, which therefore lexically inherits strictness. So no
>> > problem
>> > here.
>> > [2] The 'eval' identifier is bound in non-strict code and used in strict
>> > code.
>> > [3] Strict code makes no use here of a lexical binding of the identifier
>> > "eval".
>> >
>> > A previous approach which we rejected was to make strictness dynamically
>> > scoped, so all three of the above calls would do strict evals. This was
>> > rejected to avoid the problems of dynamic scoping. Are you suggesting a
>> > rule
>> > that would affect #2 but not #3? If so, IIRC no such rule was previously
>> > proposed.
>>
>> I'm actually suggesting plain lexical scoping. :)
>>
>> Basically, what I'm saying is that the directive "use strict" could
>> simply amount to shadowing the global eval via an implicit "var eval =
>> eval_strict" (unless global eval has already been shadowed lexically).
>> So all uses of the identifier `eval' in that scope would refer to its
>> strict version, no matter when, where, or how you ultimately invoke
>> it. Consequently, all your three examples would behave the same, only
>> depending on the argument to f.
>
> I don't understand. If "use strict" implicitly introduces a "var eval =
> eval_strict;" at the position it occurs, then wouldn't #3 in my example
> still evaluate non-strict?
> I'll answer the rest once I understand your proposal.
>>
>> Re dynamic scoping: in fact, I would claim that the current rule _is_
>> a form of dynamic scoping -- eval behaves differently when its caller
>> is strict. Except that this rule only applies when the syntactic
>> identifier in the call happens to be "eval". Regardless of the above,
>> I wonder why this special case was introduced. Why is the rule not
>> applied to all applications uniformly? I feel that I am missing
>> something.
>>
>> /Andreas
>
>
>
> --
>     Cheers,
>     --MarkM
>
> _______________________________________________
> es-discuss mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/es-discuss
>
>
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Strict mode eval

Mark S. Miller-2


On Wed, May 11, 2011 at 11:41 AM, felix <[hidden email]> wrote:
I think he's proposing that the 'eval' function would be a magical
function that can inspect its calling environment to determine whether
it's being called in a lexically strict context or not, and based on
that it would act like the strict eval operator, or not.

That's exactly what I'm trying to determine. Such magical dynamic inspection of the caller's lexical context would be a form of dynamic scoping. Nowhere else in the language can a callee detect a caller's strictness.

 

On Wed, May 11, 2011 at 11:30, Mark S. Miller <[hidden email]> wrote:
>
>
> On Wed, May 11, 2011 at 10:31 AM, Andreas Rossberg <[hidden email]>
> wrote:
>>
>> On 11 May 2011 18:31, Mark S. Miller <[hidden email]> wrote:
>> > On Wed, May 11, 2011 at 4:42 AM, Andreas Rossberg <[hidden email]>
>> > wrote:
>> >>
>> >> Thanks to everybody for clearing up my confusion. The thing I had
>> >> missed in the spec was Section 10.4.2. And of course, my example was
>> >> too simplistic because it couldn't distinguish caller and global
>> >> context.
>> >>
>> >> At the risk of reviving old discussions, are there any sources
>> >> explaining the rationale behind the current design? The "obvious"
>> >> solution to me would have been having two internal variants/modes of
>> >> (or "entry points" to) the eval function, one strict, one non-strict.
>> >> And depending on the lexical strict mode, the identifier "eval" would
>> >> be bound to the right one.
>> >
>> > I don't think I understand the suggestion. What would the following code
>> > do:
>> >     // non-strict outer context
>> >     function f(eval) {
>> >       var f = eval;
>> >       function g() {
>> >         "use strict";
>> >         eval(str); // [1]
>> >         (1,eval)(str); // [2]
>> >         f(str); // [3]
>> >       }
>> >     }
>> > [1] If the outer eval is bound to the global eval function then this is
>> > a
>> > direct eval, which therefore lexically inherits strictness. So no
>> > problem
>> > here.
>> > [2] The 'eval' identifier is bound in non-strict code and used in strict
>> > code.
>> > [3] Strict code makes no use here of a lexical binding of the identifier
>> > "eval".
>> >
>> > A previous approach which we rejected was to make strictness dynamically
>> > scoped, so all three of the above calls would do strict evals. This was
>> > rejected to avoid the problems of dynamic scoping. Are you suggesting a
>> > rule
>> > that would affect #2 but not #3? If so, IIRC no such rule was previously
>> > proposed.
>>
>> I'm actually suggesting plain lexical scoping. :)
>>
>> Basically, what I'm saying is that the directive "use strict" could
>> simply amount to shadowing the global eval via an implicit "var eval =
>> eval_strict" (unless global eval has already been shadowed lexically).
>> So all uses of the identifier `eval' in that scope would refer to its
>> strict version, no matter when, where, or how you ultimately invoke
>> it. Consequently, all your three examples would behave the same, only
>> depending on the argument to f.
>
> I don't understand. If "use strict" implicitly introduces a "var eval =
> eval_strict;" at the position it occurs, then wouldn't #3 in my example
> still evaluate non-strict?
> I'll answer the rest once I understand your proposal.
>>
>> Re dynamic scoping: in fact, I would claim that the current rule _is_
>> a form of dynamic scoping -- eval behaves differently when its caller
>> is strict. Except that this rule only applies when the syntactic
>> identifier in the call happens to be "eval". Regardless of the above,
>> I wonder why this special case was introduced. Why is the rule not
>> applied to all applications uniformly? I feel that I am missing
>> something.
>>
>> /Andreas
>
>
>
> --
>     Cheers,
>     --MarkM
>
> _______________________________________________
> es-discuss mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/es-discuss
>
>



--
    Cheers,
    --MarkM

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Strict mode eval

Allen Wirfs-Brock

On May 11, 2011, at 11:51 AM, Mark S. Miller wrote:



On Wed, May 11, 2011 at 11:41 AM, felix <[hidden email]> wrote:
I think he's proposing that the 'eval' function would be a magical
function that can inspect its calling environment to determine whether
it's being called in a lexically strict context or not, and based on
that it would act like the strict eval operator, or not.

That's exactly what I'm trying to determine. Such magical dynamic inspection of the caller's lexical context would be a form of dynamic scoping. Nowhere else in the language can a callee detect a caller's strictness.

This is the key point that I haven't seen otherwise mentioned on this thread.   Classically JS eval as implemented by browsers  required knowledge about its calling environment that generally isn't passed by a normal function call.  To make this work, either this extra information has to be passed in every call to any function (any call might be to an aliased eval call) or there has to be a dynamic scoping mechanism that allows eval to reach up the stack and get out of band information from it caller.  Neither of these were things we wanted to require of implementations in ES5.

"Direct eval"  (or the eval operator, as Oliver refers to it) is a way to (mostly) statically identify eval calls and to do special case processing to make the caller environment information available for eval processing.  "indirect evals" are just regular function calls and not special environment information is passed or otherwise made available.  So, the built-in eval function when indirectly involved is limited to using the global environment.

This has nothing to do with strict mode.

Allen

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Strict mode eval

Andreas Rossberg-4
In reply to this post by Mark S. Miller-2
On 11 May 2011 20:30, Mark S. Miller <[hidden email]> wrote:

>
> On Wed, May 11, 2011 at 10:31 AM, Andreas Rossberg <[hidden email]>
> wrote:
>>
>> On 11 May 2011 18:31, Mark S. Miller <[hidden email]> wrote:
>> > On Wed, May 11, 2011 at 4:42 AM, Andreas Rossberg <[hidden email]>
>> > wrote:
>> >>
>> >> Thanks to everybody for clearing up my confusion. The thing I had
>> >> missed in the spec was Section 10.4.2. And of course, my example was
>> >> too simplistic because it couldn't distinguish caller and global
>> >> context.
>> >>
>> >> At the risk of reviving old discussions, are there any sources
>> >> explaining the rationale behind the current design? The "obvious"
>> >> solution to me would have been having two internal variants/modes of
>> >> (or "entry points" to) the eval function, one strict, one non-strict.
>> >> And depending on the lexical strict mode, the identifier "eval" would
>> >> be bound to the right one.
>> >
>> > I don't think I understand the suggestion. What would the following code
>> > do:
>> >     // non-strict outer context
>> >     function f(eval) {
>> >       var f = eval;
>> >       function g() {
>> >         "use strict";
>> >         eval(str); // [1]
>> >         (1,eval)(str); // [2]
>> >         f(str); // [3]
>> >       }
>> >     }
>> > [1] If the outer eval is bound to the global eval function then this is
>> > a
>> > direct eval, which therefore lexically inherits strictness. So no
>> > problem
>> > here.
>> > [2] The 'eval' identifier is bound in non-strict code and used in strict
>> > code.
>> > [3] Strict code makes no use here of a lexical binding of the identifier
>> > "eval".
>> >
>> > A previous approach which we rejected was to make strictness dynamically
>> > scoped, so all three of the above calls would do strict evals. This was
>> > rejected to avoid the problems of dynamic scoping. Are you suggesting a
>> > rule
>> > that would affect #2 but not #3? If so, IIRC no such rule was previously
>> > proposed.
>>
>> I'm actually suggesting plain lexical scoping. :)
>>
>> Basically, what I'm saying is that the directive "use strict" could
>> simply amount to shadowing the global eval via an implicit "var eval =
>> eval_strict" (unless global eval has already been shadowed lexically).
>> So all uses of the identifier `eval' in that scope would refer to its
>> strict version, no matter when, where, or how you ultimately invoke
>> it. Consequently, all your three examples would behave the same, only
>> depending on the argument to f.
>
> I don't understand. If "use strict" implicitly introduces a "var eval =
> eval_strict;" at the position it occurs, then wouldn't #3 in my example
> still evaluate non-strict?

Assume that

- we distinguish two variants of the eval function, strict and
non-strict -- let's call these values EVAL_s and EVAL_ns.
- initially (in global scope), the identifier `eval' is bound to EVAL_ns.
- in a strict mode scope it will be considered rebound to EVAL_s
instead (unless it has already been shadowed by user code anyway).

(In addition, at least in strict mode, the only calls to `eval' that
are considered _direct_ calls would be those where `eval' statically
refers to the initial binding or one of the implicit strict-mode
rebindings -- i.e., where it has not been shadowed by the user.)

In your example, the `eval' identifier is already shadowed by the
function parameter, so the inner "use strict" would have no effect on
it -- in that scope `eval' is just an ordinary identifier.
Consequently, all 3 examples would behave alike and are non-direct
calls. Whether strict or not solely depends on what you pass in to f:

  // non-strict scope
  f(eval)   // EVAL_ns
  (function() { "use strict"; f(eval) })()  // EVAL_s
  f((function() { "use strict"; return eval })())  // EVAL_s

Does that make sense? The idea is that strict/non-strict is resolved
w.r.t. the static scope where the identifier `eval' occurs. With this
semantics, there would be no way in strict mode to access non-strict
eval, unless it is explicitly provided by someone. With the current
rules that is not the case, because you can easily defeat strict mode
by a random indirection, e.g.:

  "use strict";
  var e = eval
  e("var oops = 666")  // pollutes the global object, although the
whole program is in strict mode

I'm not sure whether that was intentional or not, but it feels strange.

/Andreas
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Strict mode eval

Andreas Rossberg-4
In reply to this post by Allen Wirfs-Brock
On 11 May 2011 23:15, Allen Wirfs-Brock <[hidden email]> wrote:
> "Direct eval"  (or the eval operator, as Oliver refers to it) is a way to
> (mostly) statically identify eval calls and to do special case processing to
> make the caller environment information available for eval processing.
>  "indirect evals" are just regular function calls and not special
> environment information is passed or otherwise made available.  So, the
> built-in eval function when indirectly involved is limited to using the
> global environment.
> This has nothing to do with strict mode.

Sorry, I should have been clearer. To clarify: my follow-up question
was only tangentially related to the question of direct calls -- it
mainly was about how eval inherits strict mode.

The one bit where these questions are somewhat related is the
"(mostly)" bit in your reply. Is there a reason for this "mostly",
which, I would argue, is a form of dynamic scoping?

/Andreas
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Strict mode eval

Allen Wirfs-Brock

On May 12, 2011, at 2:10 AM, Andreas Rossberg wrote:
>
> The one bit where these questions are somewhat related is the
> "(mostly)" bit in your reply. Is there a reason for this "mostly",
> which, I would argue, is a form of dynamic scoping?
>
> /Andreas

The "mostly" refers to the fact the the actual value of the eval binding has to be checked at runtime to be sure  that  a call via the identifier "eval" is an actual direct eval operation.


For example:

function foo() {
      var x=5;
      eval("x");  //this looks like a direct eval. Is it really?
}

Needs to "compile" into something like:

function foo() {
      var x=5;
      if (eval===__builtinEvalFunction__) {
             //perform an inline eval operation using the current lexical environment and strict mode
     } else eval("x");  //do a regular call to some function that isn't the built-9in eval
}


This is necessary, because the global binding for "eval" might be changed or because there might be a  binding of "eval" in an surrounding scope.

Allen
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Strict mode eval

Andreas Rossberg-4
On 12 May 2011 17:47, Allen Wirfs-Brock <[hidden email]> wrote:
>
> On May 12, 2011, at 2:10 AM, Andreas Rossberg wrote:
>>
>> The one bit where these questions are somewhat related is the
>> "(mostly)" bit in your reply. Is there a reason for this "mostly",
>> which, I would argue, is a form of dynamic scoping?
>
> [...]
> This is necessary, because the global binding for "eval" might be changed

Ouch, right.

> or because there might be a  binding of "eval" in an surrounding scope.

I see -- which, specifically, might have been introduced after the
fact via `with' or non-strict eval.

I guess I still have a blind spot on the various ways of messing up
static scoping. But I'm working on it. :)  Anyway, thanks for pointing
it out.

/Andreas
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Strict mode eval

Mark S. Miller-2
In reply to this post by Andreas Rossberg-4
On Thu, May 12, 2011 at 2:08 AM, Andreas Rossberg <[hidden email]> wrote:
[...]

Assume that

- we distinguish two variants of the eval function, strict and
non-strict -- let's call these values EVAL_s and EVAL_ns.
- initially (in global scope), the identifier `eval' is bound to EVAL_ns.
- in a strict mode scope it will be considered rebound to EVAL_s
instead (unless it has already been shadowed by user code anyway).

(In addition, at least in strict mode, the only calls to `eval' that
are considered _direct_ calls would be those where `eval' statically
refers to the initial binding or one of the implicit strict-mode
rebindings -- i.e., where it has not been shadowed by the user.)

I think the core insight here is good, and had it been made in time, could have led to a better semantics than what we adopted into ES5. I like the idea that ' "use strict";' effectively inserts a DeclarativeEnvironmentRecord binding 'eval' to EVAL_s, though I'd put this record on the stack at the strict/non-strict boundary rather than just above the global object. IIRC, no one has previous suggested anything remotely like this. Of course, it's too late to fix ES5, but perhaps we can still apply your insights to improve the eval situation in ES-next and SES.

This should be doable because both ES-next and SES effectively remove the global object from the bottom of the scope chain and prevent local rebinding of 'eval'. Thus the outer binding for eval in scope for ES-next and SES code can differ from window.eval. SES-on-ES5 does this already, but at the cost of breaking direct eval inside SES code, since 'eval' in that code no longer has its original binding to EVAL_ns.

The key to preserving direct eval is your next insight above: that the set of such built-in eval variants form some kind of equivalence class, so that an apparent direct eval call is actually a direct eval call if its binding for 'eval' is any of the eval functions in that equivalence class. 

Even better, since 'eval' cannot be rebound by ES5/strict, ES-next, or SES code, and since "eval(str)" is effectively a special form anyway, why not remove the dynamic "and if 'eval' is bound to the original global eval function" condition from direct eval? Why not just treat "eval(str)" as a direct eval special form independent of what 'eval' is bound to in that scope? Then, module loaders could be used to introduce different top level bindings for 'eval', as used for indirect eval calls, without breaking the direct eval special form. 

The one downside is an extra burden on source-to-source rewriters: they would also need to rewrite the direct eval call 'eval(str)' into approximately 'eval(rewrite(str))', which requires that the rewriter itself be written in JavaScript and downloaded to the client. Given how large a parser needs to be to accurately parse JavaScript, this is an unfortunate burden. Despite this burden, I think we should consider removing this dynamic condition from the ES-next direct eval special form.


In your example, the `eval' identifier is already shadowed by the
function parameter, so the inner "use strict" would have no effect on
it -- in that scope `eval' is just an ordinary identifier.
Consequently, all 3 examples would behave alike and are non-direct
calls. Whether strict or not solely depends on what you pass in to f:

 // non-strict scope
 f(eval)   // EVAL_ns
 (function() { "use strict"; f(eval) })()  // EVAL_s
 f((function() { "use strict"; return eval })())  // EVAL_s

Does that make sense? The idea is that strict/non-strict is resolved
w.r.t. the static scope where the identifier `eval' occurs. With this
semantics, there would be no way in strict mode to access non-strict
eval, unless it is explicitly provided by someone. With the current
rules that is not the case, because you can easily defeat strict mode
by a random indirection, e.g.:

 "use strict";
 var e = eval
 e("var oops = 666")  // pollutes the global object, although the
whole program is in strict mode

I'm not sure whether that was intentional or not, but it feels strange.

/Andreas



--
    Cheers,
    --MarkM

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Strict mode eval

Andreas Rossberg-4
On 13 May 2011 01:50, Mark S. Miller <[hidden email]> wrote:

>> Assume that
>>
>> - we distinguish two variants of the eval function, strict and
>> non-strict -- let's call these values EVAL_s and EVAL_ns.
>> - initially (in global scope), the identifier `eval' is bound to EVAL_ns.
>> - in a strict mode scope it will be considered rebound to EVAL_s
>> instead (unless it has already been shadowed by user code anyway).
>>
>> (In addition, at least in strict mode, the only calls to `eval' that
>> are considered _direct_ calls would be those where `eval' statically
>> refers to the initial binding or one of the implicit strict-mode
>> rebindings -- i.e., where it has not been shadowed by the user.)
>
> I think the core insight here is good, and had it been made in time, could
> have led to a better semantics than what we adopted into ES5. I like the
> idea that ' "use strict";' effectively inserts a
> DeclarativeEnvironmentRecord binding 'eval' to EVAL_s, though I'd put this
> record on the stack at the strict/non-strict boundary rather than just above
> the global object.

Yes, my previous description of "shadowing" `eval' at the point of
"use strict" was meant to describe just that.

> Even better, since 'eval' cannot be rebound by ES5/strict, ES-next, or SES
> code, and since "eval(str)" is effectively a special form anyway, why not
> remove the dynamic "and if 'eval' is bound to the original global eval
> function" condition from direct eval? Why not just treat "eval(str)" as a
> direct eval special form independent of what 'eval' is bound to in that
> scope?

That's what I tried to suggest in the parenthesized paragraph above,
and it was the reason for my question to Allan.

The difficulty in ES5 would be that scoping is not really static --
not even in strict-mode code, which might still be surrounded by
non-script scopes shadowing `eval' dynamically (esp `with'). But for
Harmony it'd be nice.

Thanks,
/Andreas
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Strict mode eval

Mark S. Miller-2
I think this is the kind of incremental refinement of the details of existing features that we can legitimately consider after May without setting a bad precedent. Would you be interested in turning these ideas into a strawman for, say, the July meeting? 

Unless there's a problem with this approach I'm not noticing, I think it would be a welcome cleanup of a messy part of the language -- conditioned on an ES-next opt in of course.


On Fri, May 13, 2011 at 2:05 AM, Andreas Rossberg <[hidden email]> wrote:
On 13 May 2011 01:50, Mark S. Miller <[hidden email]> wrote:
>> Assume that
>>
>> - we distinguish two variants of the eval function, strict and
>> non-strict -- let's call these values EVAL_s and EVAL_ns.
>> - initially (in global scope), the identifier `eval' is bound to EVAL_ns.
>> - in a strict mode scope it will be considered rebound to EVAL_s
>> instead (unless it has already been shadowed by user code anyway).
>>
>> (In addition, at least in strict mode, the only calls to `eval' that
>> are considered _direct_ calls would be those where `eval' statically
>> refers to the initial binding or one of the implicit strict-mode
>> rebindings -- i.e., where it has not been shadowed by the user.)
>
> I think the core insight here is good, and had it been made in time, could
> have led to a better semantics than what we adopted into ES5. I like the
> idea that ' "use strict";' effectively inserts a
> DeclarativeEnvironmentRecord binding 'eval' to EVAL_s, though I'd put this
> record on the stack at the strict/non-strict boundary rather than just above
> the global object.

Yes, my previous description of "shadowing" `eval' at the point of
"use strict" was meant to describe just that.

> Even better, since 'eval' cannot be rebound by ES5/strict, ES-next, or SES
> code, and since "eval(str)" is effectively a special form anyway, why not
> remove the dynamic "and if 'eval' is bound to the original global eval
> function" condition from direct eval? Why not just treat "eval(str)" as a
> direct eval special form independent of what 'eval' is bound to in that
> scope?

That's what I tried to suggest in the parenthesized paragraph above,
and it was the reason for my question to Allan.

The difficulty in ES5 would be that scoping is not really static --
not even in strict-mode code, which might still be surrounded by
non-script scopes shadowing `eval' dynamically (esp `with'). But for
Harmony it'd be nice.

Thanks,
/Andreas



--
    Cheers,
    --MarkM

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Strict mode eval

Andreas Rossberg-4
Sure, sounds good. I will look into it.

Thanks,
/Andreas


On 14 May 2011 03:18, Mark S. Miller <[hidden email]> wrote:

> I think this is the kind of incremental refinement of the details of
> existing features that we can legitimately consider after May without
> setting a bad precedent. Would you be interested in turning these ideas into
> a strawman for, say, the July meeting?
> Unless there's a problem with this approach I'm not noticing, I think it
> would be a welcome cleanup of a messy part of the language -- conditioned on
> an ES-next opt in of course.
>
> On Fri, May 13, 2011 at 2:05 AM, Andreas Rossberg <[hidden email]>
> wrote:
>>
>> On 13 May 2011 01:50, Mark S. Miller <[hidden email]> wrote:
>> >> Assume that
>> >>
>> >> - we distinguish two variants of the eval function, strict and
>> >> non-strict -- let's call these values EVAL_s and EVAL_ns.
>> >> - initially (in global scope), the identifier `eval' is bound to
>> >> EVAL_ns.
>> >> - in a strict mode scope it will be considered rebound to EVAL_s
>> >> instead (unless it has already been shadowed by user code anyway).
>> >>
>> >> (In addition, at least in strict mode, the only calls to `eval' that
>> >> are considered _direct_ calls would be those where `eval' statically
>> >> refers to the initial binding or one of the implicit strict-mode
>> >> rebindings -- i.e., where it has not been shadowed by the user.)
>> >
>> > I think the core insight here is good, and had it been made in time,
>> > could
>> > have led to a better semantics than what we adopted into ES5. I like the
>> > idea that ' "use strict";' effectively inserts a
>> > DeclarativeEnvironmentRecord binding 'eval' to EVAL_s, though I'd put
>> > this
>> > record on the stack at the strict/non-strict boundary rather than just
>> > above
>> > the global object.
>>
>> Yes, my previous description of "shadowing" `eval' at the point of
>> "use strict" was meant to describe just that.
>>
>> > Even better, since 'eval' cannot be rebound by ES5/strict, ES-next, or
>> > SES
>> > code, and since "eval(str)" is effectively a special form anyway, why
>> > not
>> > remove the dynamic "and if 'eval' is bound to the original global eval
>> > function" condition from direct eval? Why not just treat "eval(str)" as
>> > a
>> > direct eval special form independent of what 'eval' is bound to in that
>> > scope?
>>
>> That's what I tried to suggest in the parenthesized paragraph above,
>> and it was the reason for my question to Allan.
>>
>> The difficulty in ES5 would be that scoping is not really static --
>> not even in strict-mode code, which might still be surrounded by
>> non-script scopes shadowing `eval' dynamically (esp `with'). But for
>> Harmony it'd be nice.
>>
>> Thanks,
>> /Andreas
>
>
>
> --
>     Cheers,
>     --MarkM
>
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss