Eval-invisible let bindings

# Igor Bukanov (18 years ago)

Currently ES4 allows to access from eval scripts the names introduced by the let statements and expressions. It leads to implementation complexity since the let bindings cannot be implemented as a pure compilation-time feature and the runtime must be able to expose the names for eval scripts.

Thus I suggest to consider making let bindings invisible to the eval scripts. That is, the idea is to exclude any let-induced name from the scope chain passed to the eval script. For example, given:

let a; function f(b) { var c; let d; eval(eval_source); }

the script from eval_source when executed would not see a and d and would be able to access/modify only b and c.

This not only simplifies implementations, but would also give a possibility to prevent eval-injections from discovering the internal state of a closure as long as the closure uses let for its internal state. Although ES4 mitigates that with restrictions on the indirect eval, for compatibility implementations may be forced to support it.

, Igor

# Lars Hansen (18 years ago)

-----Original Message----- From: es4-discuss-bounces at mozilla.org [mailto:es4-discuss-bounces at mozilla.org] On Behalf Of Igor Bukanov Sent: 5. desember 2007 23:53 To: es4-discuss at mozilla.org Subject: Eval-invisible let bindings

Hi,

Currently ES4 allows to access from eval scripts the names introduced by the let statements and expressions. It leads to implementation complexity since the let bindings cannot be implemented as a pure compilation-time feature and the runtime must be able to expose the names for eval scripts.

Welcome to the wonderful world of "eval"!

Thus I suggest to consider making let bindings invisible to the eval scripts. That is, the idea is to exclude any let-induced name from the scope chain passed to the eval script. For example, given:

let a; function f(b) { var c; let d; eval(eval_source); }

the script from eval_source when executed would not see a and d and would be able to access/modify only b and c.

My opinion is that this would make things unnecessarily confusing. Unless I'm mistaken we already have a couple of cases where names are resolved at compile-time without taking into account shadowing bindings introduced by "with" or "eval". (The issue needs to be clarified further.) Adding more of those would not be good.

This not only simplifies implementations, but would also give a possibility to prevent eval-injections from discovering the internal state of a closure as long as the closure uses let for its internal state. Although ES4 mitigates that with restrictions on the indirect eval, for compatibility implementations may be forced to support it.

It simplifies some kinds of implementations. Other implementations may not be particularly affected by this problem. My opinion is that people who use the eval operator deserve what they get, and that the compiler should feel free to generate slow code in any scope affected by a use of the eval operator. Such an implementation only needs to detect direct uses of eval, as specified in ES3 and elaborated in ES4. (ES4 specifies the meaning of strictly more uses of eval than ES3, and requries that EvalError is thrown in other cases.)

# Igor Bukanov (18 years ago)

On 06/12/2007, Lars Hansen <lhansen at adobe.com> wrote:

Such an implementation only needs to detect direct uses of eval, as specified in ES3 and elaborated in ES4. (ES4 specifies the meaning of strictly more uses of eval than ES3, and requries that EvalError is thrown in other cases.)

This is good in theory, but for compatibility reasons the implementation may be forced to support the indirect eval. For example, at least recently some Goggle's pages contained var e=eval; to compress the code. So I do not see how specifying more stricter cases for ES4 would change the situation in practice. Hence this idea to make at least new ES4 features immune from eval universality.

, Igor

# Lars Hansen (18 years ago)

On Dec 6, 2007 12:25 PM, Igor Bukanov <igor at mir2.org> wrote:

On 06/12/2007, Lars Hansen <lhansen at adobe.com> wrote:

Such an implementation only needs to detect direct uses of eval, as specified in ES3 and elaborated in ES4. (ES4 specifies the meaning of strictly more uses of eval than ES3, and requries that EvalError is thrown in other cases.)

This is good in theory, but for compatibility reasons the implementation may be forced to support the indirect eval. For example, at least recently some Goggle's pages contained var e=eval; to compress the code.

And that use case is explicitly allowed by ES4, which assigns specific semantics to it, namely that the eval function performs evaluation in the global scope of its definition, not in the local scope of its call. Only "eval" as an operator performs evaluation in the local scope of its call, and in that case it is always lexically apparent whether a call may be an invocation of the eval operator. (Modulo surrounding uses of "with" and "eval", and open namespaces. In practice, it may be necessary to perform a lookup to see if the name "eval" actually references the initial binding of the eval function. This should be straightforward.)

So I do not see how specifying more stricter cases for ES4 would change the situation in practice. Hence this idea to make at least new ES4 features immune from eval universality.

The issues, including issues of backward compatibility, have been discussed extensively in TG1, and even though there may exist some ES3 implementations that allow eval to be used in a way that is not conformant with the current ES4 proposal, and even though there may be programs that depend on that behavior, it is not believed to be a problem in practice. There are ES3 user agents, like Opera, that restrict eval in various ways. Opera employees have been participating in this discussion and based on their experience with bug reports resulting from (or rather not resulting from) restrictions in Opera, appear to believe that the current ES4 spec is sufficient.

(IMO the problem with eval cannot be worked out satisfactorily by making language features invisible to it, but by restricting its applicability as much as possible. Backward compatibility is the real test, to be sure. So far, TG1 believes we have that under control.)

# Igor Bukanov (18 years ago)

On 06/12/2007, Lars Hansen <lhansen at adobe.com> wrote:

Opera employees have been participating in this discussion and based on their experience with bug reports resulting from (or rather not resulting from) restrictions in Opera, appear to believe that the current ES4 spec is sufficient.

That is really good sign that the current ES4 specs are compatible with the web as seen at least by Opera. I have missed that. It indeed makes this proposal unnecessary.

, Igor

# Thomas Reilly (18 years ago)

Don't you have to do scope chain dance for function closures anyways or did I miss something? How is eval different from function closures?

The notion piques my curiousity though. We here at Adobe have large AS3 code bases where it is frowned upon to use function closures b/c local variable scope chain reference leaks have caused so much consternation. Having a way to hide variables from eval and function closures might be useful in that regard. I'm curious what the language folks think about it. I guarantee these types of reference leaks will be high on the list of very bad bugs written by Java programmer turned ES4 programmer using function closures w/o realizing that they are doing. In fact I can already see the Slashdot posting "ES4 memory leak crashes University X's autonomous vehicle".

# Lars Hansen (18 years ago)

-----Original Message----- From: es4-discuss-bounces at mozilla.org [mailto:es4-discuss-bounces at mozilla.org] On Behalf Of Thomas Reilly Sent: 7. desember 2007 01:16

Don't you have to do scope chain dance for function closures anyways or did I miss something? How is eval different from function closures?

I think Igor's point is that if let bindings are invisible to eval then you can mostly boil them away at compile time to rib/offset pairs, no names required. This does not change with closures, and clever techniques for compiling "with" probably also does not require names to be available. But eval does.

The notion piques my curiousity though. We here at Adobe have large AS3 code bases where it is frowned upon to use function closures b/c local variable scope chain reference leaks have caused so much consternation. Having a way to hide variables from eval and function closures might be useful in that regard. I'm curious what the language folks think about it.

If the language does not provide safe-for-space guarantees then the implementation is not doing anything wrong here, though it may be considered to be of low quality, of course. To do better the implementation might need to use closure representations that incorporate a display (so that a function only closes over what it might reference). That may in turn require heap-allocating individual captured storage cells in order to avoid capturing entire rib objects, which in its turn may cause overall slowdowns in code that does use closures.

On the web there may also be an issue with needing to know the defining global environment of a function in order to perform a security check; that will itself prevent global environments from being garbage-collected while any functions created in that environment are still live. (One can probably imagine other mechanisms for that functionality.)

I guarantee these types of reference leaks will be high on the list of very bad bugs written by Java programmer turned ES4 programmer using function closures w/o realizing that they are doing. In fact I can already see the Slashdot posting "ES4 memory leak crashes University X's autonomous vehicle".

I doubt ES4 will provide the necessary safe-for-space guarantees to prevent this (though ES3 programmers are the more likely culprits IMO :-)

# P T Withington (18 years ago)

On 2007-12-07, at 05:06 EST, Lars Hansen wrote:

That may in turn require heap-allocating individual captured storage cells in order to avoid capturing entire rib objects, which in its turn may cause overall slowdowns in code that does use closures.

My experience is that closures are poorly implemented in current es3
runtimes because they don't do this analysis and instead capture the
entire environment, making them much more expensive than allocating
an instance. My Lisp experience is that the compiler can warn when an
'indefinite extent' (upward) closure is being created to help the
programmer avoid those (and the compiler can stack-allocate the
captured state for 'dynamic extent' (downward) ones). Some languages
eschew closures altogether because they are isomorphic to instances,
but with explicit allocation. Personally, I find downward closures a
powerful structuring tool, so I am glad we have them; but upward
closures can be difficult for even the expert to spot, so I hope
implementors will give us a hand there.

# Mike Shaver (18 years ago)

On Dec 7, 2007 11:00 AM, P T Withington <ptw at pobox.com> wrote:

My Lisp experience is that the compiler can warn when an 'indefinite extent' (upward) closure is being created to help the programmer avoid those (and the compiler can stack-allocate the captured state for 'dynamic extent' (downward) ones). Some languages eschew closures altogether because they are isomorphic to instances, but with explicit allocation. Personally, I find downward closures a powerful structuring tool, so I am glad we have them; but upward closures can be difficult for even the expert to spot, so I hope implementors will give us a hand there.

Upward closures, if I understand the terminology correctly, are quite common on the web, such as when passed as an argument to setTimeout or installed as an event handler. Doing a better job of handling the structuring uses of downward closures would indeed be quite righteous, but I don't think we're going to stamp out upward closures any time soon.

Mike