Strict mode eval code cannot instantiate variables or functions

J

Julian Turner

Hi

I am trying to understand the meaning of "Strict mode eval code cannot
instantiate variables or functions in the variable environment of the
caller to eval" in ECMASCRIPT 5.

Does it mean that if you have something like:-

var v;
eval("v = 2");

v remains undefined / uninitialised?

Julian
 
J

Julian Turner

Hi

I am trying to understand the meaning of "Strict mode eval code cannot
instantiate variables or functions in the variable environment of the
caller to eval" in ECMASCRIPT 5.

Does it mean that if you have something like:-

var v;
eval("v = 2");

v remains undefined / uninitialised?

Julian

Sorry

By which I mean:-

var v;
eval("v = 2; alert(v);"); // 2
alert(v); // undefined

Julian
 
M

Michael Haufe (\TNO\)

Quoted from the Final Draft of ES5 (10.4.2.1):

"The eval code cannot instantiate variable or function bindings in the
variable environment of the calling context that invoked the eval if
either the code of the calling context or the eval code is strict
code. Instead such bindings are instantiated in a new
VariableEnvironment that is only accessible to the eval code."
 
J

Julian Turner

 From what I can tell, this assignment should work. It is variable (and
function) *declarations* that specification is talking about.

In other words:

   'use strict';
   eval('var x;')
   typeof x; // should be "undefined"

and:

   'use strict';
   eval('function x(){}');
   typeof x; // should be "undefined" too

but:

   'use strict';
   var x;
   eval('x = 2');
   typeof x; // should be "number"

Also note that eval code is strict not only when it is called from
within strict code (as a direct call), but also when it begins with a
Use Strict Directive.

For example:

   eval('"use strict";var x = 1;');
   typeof x; // should be "undefined"

And of course it's worth mentioning that ES5 is still draft, so
potentially is a subject to change.

Thank you kangax. That does make sense.
 
D

Dmitry A. Soshnikov

[...]
 From what I can tell, this assignment should work. It is variable (and
function) *declarations* that specification is talking about.

Why? `eval` in strict mode doesn't inherit `LexicalEnvironment` from
the calling context (10.4.2, draft ES-5, v.043), but creates own
`LexicalEnvironment` and `VariableEnvironment`.

So, this code in strict mode shouldn't affect on `v` from the calling
context:

var v;
eval("v = 2; alert(v);"); // 2
alert(v); // undefined

This should be correct behavior. Actually, it should be treated as `v`
property of the global environment record.

/ds
 
A

Asen Bozhilov

Dmitry said:
var v;
eval("v = 2; alert(v);"); // 2
alert(v); // undefined

This should be correct behavior. Actually, it should be treated as `v`
property of the global environment record.

I have a question about eval in ECMA 5 in strict mode. If eval doesn't
inherit LexicalEnvironment` and `VariableEnvironment` from calling
execution context. What is properly behavior of code like that:

(function(){
var v;
eval('window.alert(v);'); //undefined or Reference Error?
window.alert(v); //undefined
}());

In `eval' from where will be resolving property `v'. In other words,
what exactly contain Scope Chain of `eval' in strict mode?

Regards.
 
D

Dmitry A. Soshnikov

Dmitry said:
 From what I can tell, this assignment should work. It is variable (and
function) *declarations* that specification is talking about.
Why? `eval` in strict mode doesn't inherit `LexicalEnvironment` from
the calling context (10.4.2, draft ES-5, v.043), but creates own
`LexicalEnvironment` and `VariableEnvironment`.

This isn't how I understand it, but I might be wrong ;)

No, you're not wrong but absolutely right ;)

Now, NewDeclarativeEnvironment
takes its parameter — LexicalEnvironment of current execution context —
and assigns it as an outer lexical environment of newly created
environment record (10.2.2.2).

There's your "inheritance".

So, eval code now has newly created (empty) lexical environment with its
"outer" environment referencing that of current execution context.

If that's what it is, `v` should resolve to a property of outer
environment (as per GetIdentifierReference — 10.2.2.1), not eval inner
one, and assign `2` to that property.

Yep, true, I didn't take in mind carefully 10.2.2.2 (but I sure read
it and knew that by step 4 outer environment is set). By the "doesn't
inherit" I meant that eval's `LexicalEnvironment` in strict mode won't
be the *same* as `LexicalEnvironment` of the calling context, but,
yep, that's true, that eval's `LexicalEnvironment` "inherits"
`LexicalEnvironment` of the calling context as `outer` property. Which
means - eval's code in this case cannot affect on it by `var`s or
`FD`s, but still can modify resolved properties of it's
`LexicalEnvironment` which is "Scope chain" now as has `outer`
property.
And, by the way, if I'm wrong and eval code results in environment that
doesn't "inherit" its outer environments, then `v = 2` should really
result in an error, since it would be an undeclared assignment (and
undeclared assignments result in ReferenceError in strict mode).

Yes, also true by note of 11.13.1.

/ds
 
D

Dmitry A. Soshnikov

If eval doesn't inherit LexicalEnvironment` and
`VariableEnvironment` from calling execution context.

Well, it does, and exactly "inherits" but not "borrows" as in non-
strict mode, when `LexicalEnvironment` of the calling context becomes
`LexicalEnvironment` of the eval context. In strict mode
`LexicalEnvironment` of the calling context saves in `outer` property
of the newly created `DeclarativeEnvironment` for eval context.
What is properly behavior of code like that:

(function(){
var v;
eval('window.alert(v);'); //undefined or Reference Error?
window.alert(v); //undefined

}());

`undefined` as will be found in `outer` lexical environment.
In `eval' from where will be resolving property `v'. In other words,
what exactly contain Scope Chain of `eval' in strict mode?

In current draft there's no "Scope chain" terminology anymore. There's
`LexicalEnvironment` (10.2) which contains `EnvironmentRecord` and a
possibly null reference to an `outer` `LexicalEnvironment`. So
`LexicalEnvironment` is sort of scope chain.

/ds
 
A

Asen Bozhilov

Dmitry said:
Asen Bozhilov wrote:
Well, it does, and exactly "inherits" but not "borrows" as in non-
strict mode, when `LexicalEnvironment` of the calling context becomes
`LexicalEnvironment` of the eval context. In strict mode
`LexicalEnvironment` of the calling context saves in `outer` property
of the newly created `DeclarativeEnvironment` for eval context.




`undefined` as will be found in `outer` lexical environment.

Ok understood. But what will be happen with function declaration,
function statement and function expression in code passed to `eval' in
strict mode. What will be refer internal [[scope]] property of these
objects?

e.g.

var v = true;
eval('var v = false; (function(){window.alert(v)})();'); //true or
false. That is the question :~)

Regards.
 
D

Dmitry A. Soshnikov

Dmitry said:
Asen Bozhilov wrote:
Well, it does, and exactly "inherits" but not "borrows" as in non-
strict mode, when `LexicalEnvironment` of the calling context becomes
`LexicalEnvironment` of the eval context. In strict mode
`LexicalEnvironment` of the calling context saves in `outer` property
of the newly created `DeclarativeEnvironment` for eval context.
`undefined` as will be found in `outer` lexical environment.

Ok understood. But what will be happen with function declaration,
function statement and function expression in code passed to `eval' in
strict mode. What will be refer internal [[scope]] property of these
objects?

e.g.

var v = true;
eval('var v = false; (function(){window.alert(v)})();'); //true or
false. That is the question :~)

Should be `false`, abstractly it looks like:

// global's LE
LexicalEnvironment = {
outer: null,
EnvironmentRecord: {
v: true
}
}

// eval's LE
LexicalEnvironment = {
outer: globalLE,
EnvironmentRecord: {
v: false
}
}

// anonymous FE
LexicalEnvironment = {
outer: evalLE,
EnvironmentRecord: {}
}

So, `Identifier Resolution` (10.3.1) which calls
`GetIdentifierReference` (10.2.2.1) has recursive analysis of
`LexicalEnvironment` depending on `outer` property starting from
`LexicalEnvironment` of the anonymous FE.

P.S.: that's abstract algorithm independent on concrete type of the
environment record (declarative, object).

/ds
 
A

Asen Bozhilov

Dmitry said:
Should be `false`, abstractly it looks like:

// global's LE
LexicalEnvironment = {
  outer: null,
  EnvironmentRecord: {
    v: true
  }

}

// eval's LE
LexicalEnvironment = {
  outer: globalLE,
  EnvironmentRecord: {
    v: false
  }

}

// anonymous FE
LexicalEnvironment = {
  outer: evalLE,
  EnvironmentRecord: {}

}

Thank you for response. If i properly understood you answer, internal
[[scope]] property of anonymous function expression refer
`EnvironmentRecord` created from eval lexical environment. So i can
form *closure* in this way in code passed to `eval'.

var global = this, f;
eval([
'"use strict";',
'global.f = function(){',
'/*function body*/',
'};'
].join(''));
window.alert(f);
 
D

Dmitry A. Soshnikov

[...]
Technically, there are no function statements in ES5. Function
declarations are still /SourceElement/'s and so are only allowed in
/Program/ and /FunctionBody/.

What happens to "Function Statements" is still *not formally specified*.
As a matter of fact, if you look at Chapter 12, you can see this note:

"NOTE      Several widely used implementations of ECMAScript are known to
support the use of FunctionDeclaration as a Statement. However there are
significant and irreconcilable variations among the implementations in
the semantics applied to such FunctionDeclarations. Because of these
irreconcilable difference, the use of a FunctionDeclaration as a
Statement results in code that is not reliably portable among
implementations. It is recommended that ECMAScript implementations
either disallow this usage of FunctionDeclaration or issue a warning
when such a usage is encountered. Future editions of ECMAScript may
define alternative portable means for declaring functions in a Statement
context."

Yeah, already discussed regarding ECMA-262-3. And nevertheless,
implementation of Spidermonkey (and other newer *monkeys) still seems
to me more logical (e.g. for if-else statements) regardless
specification. I don't see the big sense in words they say "an
ExpressionStatement cannot start with the `function` keyword because
that might make it ambiguous with a FunctionDeclaration"? If it's in
statement position let's treat is as special FD which won't be created
on entering the context, but in runtime like FE and let's call it
Function Statement (FS). I thought ECMA-262-5 should borrow this
Spidermonkey's `FS`s. But no, they again have restriction for FD-style
function's in statements and I don't see the useful goal from it. ;)

/ds
 
D

Dmitry A. Soshnikov

Dmitry said:
Should be `false`, abstractly it looks like:
// global's LE
LexicalEnvironment = {
  outer: null,
  EnvironmentRecord: {
    v: true
  }

// eval's LE
LexicalEnvironment = {
  outer: globalLE,
  EnvironmentRecord: {
    v: false
  }

// anonymous FE
LexicalEnvironment = {
  outer: evalLE,
  EnvironmentRecord: {}

Thank you for response. If i properly understood you answer, internal
[[scope]] property of anonymous function expression refer
`EnvironmentRecord` created from eval lexical environment. So i can
form *closure* in this way in code passed to `eval'.

var global = this, f;
eval([
    '"use strict";',
    'global.f = function(){',
        '/*function body*/',
    '};'
].join(''));
window.alert(f);

Yep, sure this way it should be possible as a closure. ;)

/ds
 
A

abozhilov

Dmitry said:
Yep, sure this way it should be possible as a closure. ;)

Ok.The last question is. How determinate `this' value in
`LexicalEnvironment` created from eval in strict mode of ECMA5
implementations?

Another thing is, how can do it similar behavior of `eval' in ECMA3
implementations:

- Create new execution context for eval code.
- Create VO for variable instantiation to eval code.
- Scope Chain contain AO/VO associated with created execution context
for eval code, followed by AO/VO of calling execution context.
- This value. Here we have two alternative.
a) this refer Global Object
b) this value can inherit from calling execution context this value.

All of that we can do it, with wrapping eval calling in function
expression:

var v;
(function(){
eval([
'var v = 10;',
'window.alert(v);', //10
'window.alert(this);' //refer to Global Object
].join(''));
}());
window.alert(v); //undefined

Inherit this value from calling execution context:

var v;
(function(){
eval([
'var v = 10;',
'window.alert(v);', //10
'window.alert(this);' //refer to object reffered from `this'
in calling execution context
].join(''));
}.call(this));
window.alert(v); //undefined
 
D

Dmitry A. Soshnikov

Dmitry said:
Yep, sure this way it should be possible as a closure. ;)

Ok.The last question is. How determinate `this' value in
`LexicalEnvironment` created from eval in strict mode of ECMA5
implementations?

Another thing is, how can do it similar behavior of `eval' in ECMA3
implementations:

- Create new execution context for eval code.
- Create VO for variable instantiation to eval code.
- Scope Chain contain AO/VO associated with created execution context
for eval code, followed by AO/VO of calling execution context.
- This value. Here we have two alternative.
  a) this refer Global Object
  b) this value can inherit from calling execution context this value.

All of that we can do it, with wrapping eval calling in function
expression:

var v;
(function(){
    eval([
        'var v = 10;',
        'window.alert(v);', //10
        'window.alert(this);' //refer to Global Object
    ].join(''));}());

window.alert(v); //undefined

Inherit this value from calling execution context:

var v;
(function(){
    eval([
        'var v = 10;',
        'window.alert(v);', //10
        'window.alert(this);' //refer to object reffered from `this'
in calling execution context
    ].join(''));}.call(this));

window.alert(v); //undefined

Yep, behavior of providing `ThisBinding` (that's how it called now)
the same including strict mode for the eval, so it will be or global
object or borrowed from the calling context.

/ds
 
L

Lasse Reichstein Nielsen

Dmitry A. Soshnikov said:
Yeah, already discussed regarding ECMA-262-3. And nevertheless,
implementation of Spidermonkey (and other newer *monkeys) still seems
to me more logical (e.g. for if-else statements) regardless
specification. I don't see the big sense in words they say "an
ExpressionStatement cannot start with the `function` keyword because
that might make it ambiguous with a FunctionDeclaration"? If it's in
statement position let's treat is as special FD which won't be created
on entering the context, but in runtime like FE and let's call it
Function Statement (FS).

What would be the scope of the name introduced by this function
statement?

Would it, essentially, be a shorthand of:
var name = function name(args) { body };
or
var name = function(args) { body };
If so, why not use that?

/L
 
D

Dmitry A. Soshnikov

What would be the scope of the name introduced by this function
statement?

Suggest the same as for `FD` and `var`, the only difference is that
such function objects will be created conditionally in runtime.
Would it, essentially, be a shorthand of:
  var name = function name(args) { body };
or
  var name = function(args) { body };
If so, why not use that?

That's another question "why not use that?". Sure we can. The same can
be asked - why not use `FS` suggested by Gecko?

/ds
 
D

Dmitry A. Soshnikov

Dmitry said:
Technically, there are no function statements in ES5. Function
declarations are still /SourceElement/'s and so are only allowed in
/Program/ and /FunctionBody/.
What happens to "Function Statements" is still *not formally specified*.
As a matter of fact, if you look at Chapter 12, you can see this note:
"NOTE      Several widely used implementations of ECMAScript are known to
support the use of FunctionDeclaration as a Statement. However there are
significant and irreconcilable variations among the implementations in
the semantics applied to such FunctionDeclarations. Because of these
irreconcilable difference, the use of a FunctionDeclaration as a
Statement results in code that is not reliably portable among
implementations. It is recommended that ECMAScript implementations
either disallow this usage of FunctionDeclaration or issue a warning
when such a usage is encountered. Future editions of ECMAScript may
define alternative portable means for declaring functions in a Statement
context."
Yeah, already discussed regarding ECMA-262-3. And nevertheless,
implementation of Spidermonkey (and other newer *monkeys) still seems
to me more logical (e.g. for if-else statements) regardless
specification. I don't see the big sense in words they say "an
ExpressionStatement cannot start with the `function` keyword because
that might make it ambiguous with a FunctionDeclaration"? If it's in

That's because it would be useless (as it is specified now).Let's
assume Function Expression is allowed in statement:

  if (true) {
    function(){}
  }

or:

  if (true) {
    function f(){}
  }

You see what happens?

We end up with essentially "dead" code: Identifier (if present) is never
made available to the outer scope (as per function expression rules);
function is instantiated and can immediately be garbage collected. ;)

What's the point? Surely, it's better to disallow this kind of scenario
earlier — on a syntactic level.

But it should be treated as `FE` for not to be useless. It should be
treated as a special form, sort of combination of `FD + FE`, which
means: affecting on the scope by the *same* rules as `var` and `FD`,
but creating in runtime just like `FE`. Maybe it won't be so useful
(or even useless) in some statements blocks, but e.g. in `if-else`
blocks it's very useful. And in discussion of the link you gave
bellow, I agree wit Igor Bukanov and Brendan Eich.

So, the main reason is not "it's useless", but "because that might
make it ambiguous with a FunctionDeclaration", which means scanner/
parser can't resolve with what kind of object it deals: with FD which
should be created on entering the context (what most implementations
do) or FE which should be created in runtime.

It's absolutely should be same as for:

function test() {}();

Here scanner/parser can't determinate what's this: FD or FE, so it
truly throws an exception. Just then, when we surround this case with
grouping operator (as one of the ways to transform it to `FE`),
scanner knows that it's `FE`:

(function test() {})();

Meanwhile, in this case, it knows it without our manually instruction
(with grouping operator):

var o = {
bar: function () {
return 'test';
}();
};

alert(o.bar); // "test"

To make it easier, they wrote: `[lookahead ∉ {{, function}]
Expression ;` - and that's the reason, but not "that's useless". And
Spidermonkey still got its right (by section 16) and make extension -
`FS` (but you sure know it all yourself).

/ds
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,432
Messages
2,571,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top