Rationale of indirect eval and its subtle features

# Dmitry A. Soshnikov (15 years ago)

Have no idea what with mail sever again, there is no this message there (links in message?). The original question is below:

# Allen Wirfs-Brock (15 years ago)

Here is another way to think about it.

The built-in function object that is the initial value of the global "eval" (defined in 15.1.2.1) is a non-strict function. Hence anything it does defaults to "non-strict" and it uses the global environment as the VariableEnvironment for the code it evaluates. However, if the argument code contains an use strict directive, then it uses a new child environment of the global environment as the VariableEnvironment. This is all specific defined behavior of the built-in eval function object and is completely independent of who calls the function or how the caller accessed the function.

An "indirect eval" is any invocation of the above function.

A "direct eval" is essentially a special syntactic from in the ES5 language with its own specific semantics. Some people have talked about it as the "eval" operator. A direct eval does not call the built-in eval function object. Instead, it has its own slightly different semantics that can be directly implemented at the call site. These semantics include access to the immediately surrounding LexicalEnvironment and the propagation of strictness into the eval code.

# Dmitry A. Soshnikov (15 years ago)

On 18.02.2011 20:04, Allen Wirfs-Brock wrote:

Here is another way to think about it.

The built-in function object that is the initial value of the global "eval" (defined in 15.1.2.1) is a non-strict function. Hence anything it does defaults to "non-strict" and it uses the global environment as the VariableEnvironment for the code it evaluates. However, if the argument code contains an use strict directive, then it uses a new child environment of the global environment as the VariableEnvironment. This is all specific defined behavior of the built-in eval function object and is completely independent of who calls the function or how the caller accessed the function.

Yes, it's clear how it looks/implemented technically, however doesn't explain a practical rationale. So at the moment I have the following conclusion and understanding (please correct me if I'm mistaken):

An indirect eval introduced into ES spec not because of some/any security reason (what I mentioned earlier in my explanations), and even not because of some practical reasons, but just because of complexity of the implementation. I.e. initially indirect eval is related with the implementation level only (only after that JS devs adapted this technique for already mentioned getting of the global object in strict mode).

Is this explanation correct?

An "indirect eval" is any invocation of the above function.

This is a subtle case, thanks (taking into account your following explanation of with the inlining the code to the call site). So, abstractly (? -- I can't say precisely whether it's technically though, since I'm not the implementer), if syntactically we use this form:

eval(...)

it isn't even an eval call, but just an inlining. I can imagine it abstractly as (with providing a "sandbox" environment):

(function foo() { eval("var x = 10;") })();

is abstractly desugars (inline) into:

(function foo() { (function __directEval(configurableBindings?) { var x = 10; })(true); })();

From this position it looks just an optimization technique. And if we do this:

var myEval = eval; myEval("var x = 10;");

we already can't inline the eval at parsing stage (?), right? And if we'd to pass the information about strictness of the surrounding context, we just should pass additional argument the myEval, yep? Though, I don't see which issue this passing of this additional argument can lead (which by the way?).

A "direct eval" is essentially a special syntactic from in the ES5 language with its own specific semantics. Some people have talked about it as the "eval" operator. A direct eval does not call the built-in eval function object. Instead, it has its own slightly different semantics that can be directly implemented at the call site.

Yes, it was new for me (and for any JS dev I guess). And how it looks I tried to describe above (is that understanding is correct?).

These semantics include access to the immediately surrounding LexicalEnvironment and the propagation of strictness into the eval code.

Yep, if to accept the inheriting of the strictness by the inner functions (with which I desugared the eval inlining).

Thanks again, Allen, it seems clear now. But the main thing which I found out from it, is that initially it's not about JS-programming, it's just an optimization technique for the implementers.

Dmitry.

# Dmitry A. Soshnikov (15 years ago)

On 19.02.2011 3:13, Dmitry A. Soshnikov wrote:

On 18.02.2011 20:04, Allen Wirfs-Brock wrote:

Here is another way to think about it.

The built-in function object that is the initial value of the global "eval" (defined in 15.1.2.1) is a non-strict function. Hence anything it does defaults to "non-strict" and it uses the global environment as the VariableEnvironment for the code it evaluates. However, if the argument code contains an use strict directive, then it uses a new child environment of the global environment as the VariableEnvironment. This is all specific defined behavior of the built-in eval function object and is completely independent of who calls the function or how the caller accessed the function.

Yes, it's clear how it looks/implemented technically, however doesn't explain a practical rationale. So at the moment I have the following conclusion and understanding (please correct me if I'm mistaken):

An indirect eval introduced into ES spec not because of some/any security reason (what I mentioned earlier in my explanations), and even not because of some practical reasons, but just because of complexity of the implementation. I.e. initially indirect eval is related with the implementation level only (only after that JS devs adapted this technique for already mentioned getting of the global object in strict mode).

Is this explanation correct?

An "indirect eval" is any invocation of the above function.

This is a subtle case, thanks (taking into account your following explanation of with the inlining the code to the call site). So, abstractly (? -- I can't say precisely whether it's technically though, since I'm not the implementer), if syntactically we use this form:

eval(...)

it isn't even an eval call, but just an inlining. I can imagine it abstractly as (with providing a "sandbox" environment):

(function foo() { eval("var x = 10;") })();

is abstractly desugars (inline) into:

(function foo() { (function __directEval(configurableBindings?) { var x = 10; })(true); })();

Note: this abstract representation doesn't explain also dynamic eval's string. i.e. when some variable should be determined at runtime, when the environment is formed:

(function foo(x) { eval("var x = " + x + ";") })(10);

How here the inlining can work?

# Allen Wirfs-Brock (15 years ago)

The distinction between a direct eval and an eval first appears in Ecma-262, Edition 2 which said: "If 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."

Note that the ES2 definition left support for indirect eval as an implementation option. Some browsers tried to implement throwing on EvalError but they discovered that this was incompatible with the web. So they implemented globally scoped indirect eval which proved to be compatible with the web. By the time of the drafting of ES5 the situations was that that some browsers implemented indirect and direct evals identically and some browsers implemented globally scoped indirect eval. No browser made use of the specification permitted EvalError.

For ES5 we wanted to eliminate any implementation options from the eval spec and to make it compatible with the web. Globally scoped indirect eval served was the solution we choose. The rest of the details including the treatment of strict code follow from that decision.

Also note that the term "indirect eval" is never used in the normative parts of the ES5 specification. It only distinguishes "direct calls to eval" as the special case. The term "indirect eval" is only used in Annex D.

On Feb 19, 2011, at 6:29 AM, Dmitry A. Soshnikov wrote:

On 19.02.2011 3:13, Dmitry A. Soshnikov wrote:

On 18.02.2011 20:04, Allen Wirfs-Brock wrote:

Here is another way to think about it.

The built-in function object that is the initial value of the global "eval" (defined in 15.1.2.1) is a non-strict function. Hence anything it does defaults to "non-strict" and it uses the global environment as the VariableEnvironment for the code it evaluates. However, if the argument code contains an use strict directive, then it uses a new child environment of the global environment as the VariableEnvironment. This is all specific defined behavior of the built-in eval function object and is completely independent of who calls the function or how the caller accessed the function.

Yes, it's clear how it looks/implemented technically, however doesn't explain a practical rationale. So at the moment I have the following conclusion and understanding (please correct me if I'm mistaken):

An indirect eval introduced into ES spec not because of some/any security reason (what I mentioned earlier in my explanations), and even not because of some practical reasons, but just because of complexity of the implementation. I.e. initially indirect eval is related with the implementation level only (only after that JS devs adapted this technique for already mentioned getting of the global object in strict mode).

Is this explanation correct?

It's not how I would state it. As a general design principle the behavior of any function, when called should only depend upon the environment it was defined in and the arguments that are explicitly passed to it. eval, as a built-in function, is defined within the global environment and it has no explicit arguments that expose the scope of its caller. Hence, it only is allowed to manipulate the global scope.

Direct eval, is the "odd duck". There is no normal function mechanism that permits its behavior. Rather than extending the semantics of function calls in general (any call may be an indirect eval call) we special case the handling of direct eval in 10.4.2 step 2.

Implementation impact is something we're alway considering, but it isn't needed to justify this behavior. If you are going to treat eval as a normal function object then it needs to be callable as such.

An "indirect eval" is any invocation of the above function.

This is a subtle case, thanks (taking into account your following explanation of with the inlining the code to the call site). So, abstractly (? -- I can't say precisely whether it's technically though, since I'm not the implementer), if syntactically we use this form:

eval(...)

it isn't even an eval call, but just an inlining. I can imagine it abstractly as (with providing a "sandbox" environment):

(function foo() { eval("var x = 10;") })();

is abstractly desugars (inline) into:

(function foo() { (function __directEval(configurableBindings?) { var x = 10; })(true); })();

Note: this abstract representation doesn't explain also dynamic eval's string. i.e. when some variable should be determined at runtime, when the environment is formed:

(function foo(x) { eval("var x = " + x + ";") })(10);

How here the inlining can work?

The declaration instantiation rules get in the way of these simple inlining models. Consider this: function outer() { var x=0; (function () { x=1; var x=2} )(); return x; //returns 0 }

compared to

function outer() { var x=0; (function () { x=1; eval(" var x=2");} )(); return x; //returns 1 }

# Dmitry A. Soshnikov (15 years ago)

On 19.02.2011 21:23, Allen Wirfs-Brock wrote:

The distinction between a direct eval and an eval first appears in Ecma-262, Edition 2 which said: "If 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."

Note that the ES2 definition left support for indirect eval as an implementation option. Some browsers tried to implement throwing on EvalError but they discovered that this was incompatible with the web. So they implemented globally scoped indirect eval which proved to be compatible with the web. By the time of the drafting of ES5 the situations was that that some browsers implemented indirect and direct evals identically and some browsers implemented globally scoped indirect eval. No browser made use of the specification permitted EvalError.

Yeah, I see. So this is all about the lexical (static) scope. I.e. the closured lexical environment of the built-in eval is the global environment, therefore it has access only for the global bindings, right?

It seems also explains that the Function's code is always evaluated in the global context -- it's in contrast with direct eval's which isn't a function call at all. Is it so (regarding Function)?

For ES5 we wanted to eliminate any implementation options from the eval spec and to make it compatible with the web. Globally scoped indirect eval served was the solution we choose. The rest of the details including the treatment of strict code follow from that decision.

Yep, it's clear.

Also note that the term "indirect eval" is never used in the normative parts of the ES5 specification. It only distinguishes "direct calls to eval" as the special case. The term "indirect eval" is only used in Annex D.

Yes, though, exactly the term "indirect" became the most popular in this case.

On 18.02.2011 20:04, Allen Wirfs-Brock wrote:

Here is another way to think about it.

The built-in function object that is the initial value of the global "eval" (defined in 15.1.2.1) is a non-strict function. Hence anything it does defaults to "non-strict" and it uses the global environment as the VariableEnvironment for the code it evaluates. However, if the argument code contains an use strict directive, then it uses a new child environment of the global environment as the VariableEnvironment. This is all specific defined behavior of the built-in eval function object and is completely independent of who calls the function or how the caller accessed the function.

Yes, it's clear how it looks/implemented technically, however doesn't explain a practical rationale. So at the moment I have the following conclusion and understanding (please correct me if I'm mistaken):

An indirect eval introduced into ES spec not because of some/any security reason (what I mentioned earlier in my explanations), and even not because of some practical reasons, but just because of complexity of the implementation. I.e. initially indirect eval is related with the implementation level only (only after that JS devs adapted this technique for already mentioned getting of the global object in strict mode).

Is this explanation correct?

It's not how I would state it. As a general design principle the behavior of any function, when called should only depend upon the environment it was defined in and the arguments that are explicitly passed to it.

Yes, as I mentioned -- a normal lexical scope implementation.

eval, as a built-in function, is defined within the global environment and it has no explicit arguments that expose the scope of its caller. Hence, it only is allowed to manipulate the global scope.

Yes, only if an implementation can handle its invocation differently. I see that you say that built-in eval is just a simple function which obey the same lexical scoping rules and therefore all the identifiers lookup is made in statically saved environment.

On the other hand, why the implementation cannot treat the eval's activation differently? Why not to save its original lexical environment (that is, global), set it to the environment of the caller and after the evaluation again restore it?

Direct eval, is the "odd duck". There is no normal function mechanism that permits its behavior. Rather than extending the semantics of function calls in general (any call may be an indirect eval call) we special case the handling of direct eval in 10.4.2 step 2.

Hm.. and this is the answer on my previous question. No special semantics with replacing saved lexical environment.

Implementation impact is something we're alway considering, but it isn't needed to justify this behavior. If you are going to treat eval as a normal function object then it needs to be callable as such.

Yes, it's clear now -- the lexical scope and a common functions' behavior -- that's the main reason.

An "indirect eval" is any invocation of the above function.

This is a subtle case, thanks (taking into account your following explanation of with the inlining the code to the call site). So, abstractly (? -- I can't say precisely whether it's technically though, since I'm not the implementer), if syntactically we use this form:

eval(...)

it isn't even an eval call, but just an inlining. I can imagine it abstractly as (with providing a "sandbox" environment):

(function foo() { eval("var x = 10;") })();

is abstractly desugars (inline) into:

(function foo() { (function __directEval(configurableBindings?) { var x = 10; })(true); })();

Note: this abstract representation doesn't explain also dynamic eval's string. i.e. when some variable should be determined at runtime, when the environment is formed:

(function foo(x) { eval("var x = " + x + ";") })(10);

How here the inlining can work?

The declaration instantiation rules get in the way of these simple inlining models. Consider this: function outer() { var x=0; (function () { x=1; var x=2} )(); return x; //returns 0 }

compared to

function outer() { var x=0; (function () { x=1; eval(" var x=2");} )(); return x; //returns 1 }

Yeah, I know that the "hoisting" breaks my simplified model of eval's inlining, but I wanted just to imagine how it is made. If to exclude the "hoisting" (i.e. all definitions are made sequentially in runtime), then direct eval's desugars quite normally.

Thanks, again Allen, I think at this step it became completely clear. (probably I'll write a small blog-post note with explaining all this suble cases and especially why Function("...") in contrast with (direct) eval executes in the global scope).

Dmitry.