On Feb 14, 2014, at 5:49 AM, André Bargull wrote:
>> 
>> How about this? 
>> 
>> let x= 0;
>> if (1) eval("let x= 42; alert(x);"); //Is this in its own block?
>> alert(x);
> 
> `eval()` hasn't yet been updated to work with the new lexical declaration 
> forms, but I hope the example from above will be evaluated in a new block. 
> See https://bugs.ecmascript.org/show_bug.cgi?id=1788 for a related bug report.

Goood point and this is something I need to specify in the next couple weeks so 
let's look at the alternatives.

First a quick refresher on ES5 era eval.

In ES5, the binding behavior of direct eval differs between strict and 
non-strict modes.

In strict mode, each eval instantiates all declarations in a new environment 
that is immediately nested within the current LexicalEnvironment. The scoping 
behavior is essentially the same as if the eval code was the body of an iife 
that occurred at the same place as the eval call.  Bindings introduced by the 
eval code disappear after completion of the eval.

In non-strict mode, each eval instantiates all declarations in the current 
VariableEnvironment; that is the most immediately enclosing function or global 
environment.  Bindings introduced by the eval code remain accessible from that 
VariableEnvironment after completion of the eval.

For example:
(function() {
      "use strict";
      eval("var answer=42");
      console.log(answer);  // ReferenceError: answer is not defined
})();

(function() {     
      eval("var answer=42");
      console.log(answer);  // 42
})();

For ES6, it makes sense for strict mode evals to behave in this exact same way. 
Each eval takes place in its own environment and no bindings survive the 
completion of the eval.

For ES6, non-strict evals of code containing only var or function declarations 
must  have exactly the ES5 behavior  in order to maintain compatibility.  But 
what about eval code that contains new declaration forms (let/const/class) 
exclusively or in combination with var/function declarations? Three 
possibilities come to mind:

1) Extend the ES5 semantics to include the new declaration forms.  For example:

(function() {     
      eval("let answer=42");
      console.log(answer);  // 42
})();

2) Use the strict mode binding semantics  if the eval code directly contains 
any of the new declaration forms:

(function() {     
      eval("
          var answer=42;
          let forceSeprateEnvironment = true;
       ");
      console.log(answer);  // ReferenceError: answer is not defined
})();

3) Combination.  use ES5 non-strict binding semantics for var/function 
declarations but place let/const/class bindings into a per eval environment:

(function() {     
      eval("
          var answer=42;
          let localToEval = true;
       ");
      console.log(answer);  // 42
      console.log(localToEval);  // ReferenceError: localToEval is not defined
)();


It would certainly be possible to specify #1, but I don't like it. Other than 
for the global environment it would be cleaner if the new block scope-able 
declarations  were never dynamically added to the environment.

I think either #2 or #3 is plausible.  #2 is a simpler story but introduces a 
refactoring hazard. If you have some existing eval code that  defines some 
"global" functions or variables, then simply adding a let/const/class 
declaration to the eval code ruins those global declarations. 

I prefer the simplicity of #2, but I also worry about the WTF impact it might 
have on evolving existing code.

Can we get away with #2, or are we going to have to go with #3?  Are there 
other alternatives?

Allen







_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to