Ticket #243 (new defect)

Opened 1 year ago

Last modified 5 months ago

scope of nested functions need to be captured before hoisting

Reported by: jeffdyer Assigned to: anonymous
Type: defect Priority: major
Milestone: Component: RefImpl
Version: 4 Keywords:
Cc: lth, graydon, brendan, waldemar@google.com

Description

var x = 15
function f () {
   let x = 10
   function g () { return x }  // returns 15, not 10
}

This is surprising to everyone, save the occasional language implementor.

Seems to me that the fix is to capture the scope of g before hoisting to the variable object of f.

This change is compatible with ES3 since it only changes the meaning of programs with block scoped variables that shadow outer ones visible from the next outer variable object scope.

Jd

Attachments

Change History

Changed 1 year ago by brendan

JS1.7 in Firefox 2 returns 10 from g; full example here:

js> var x = 15
js> function f() {
    let x = 10
    function g() { return x }
    return g
}
js> g = f()
function g() {
    return x;
}
js> g()
10

I'll admit this was not as simple to implement as we had hoped at first, but the ultimate code cost is small, and eval requires most of that code anyway:

js> function f() {
    let x = 10
    eval("print(x)")
}
js> f()
10

Of course, eval's a different case, and one could afford to take a performance hit more there than in the g-inside-f-with-let-declarations-in-the-body-block case.

SpiderMonkey? does not try to optimize non-local lexical scope (i.e., let bindings in outer functions used by inner function bodies) using displays or the like, it reifies scopes at runtime as objects (based on compile-time prototype objects), lazily enough to avoid runtime object construction for easy cases. The above is a "hard" case.

Anyone masochistic and interested, see

http://mxr.mozilla.org/mozilla/search?string=JSOP_DEFLOCALFUN

and look at JSOP_ENTERBLOCK too.

/be

Changed 5 months ago by brendan

At the April 2008 Newton meeting, Lars proposed that let at body level in a function, or at top level in a program, bind in the same scope as var. This simplifies things both for implementors of the semantics, and programmers using the language.

It does mean that let is not entirely lexical, since arguments[0] aliases a in

function f(a){let a;...}

or so it seemed to me. Since we are deprecating arguments, and let is new, we could make that an error. But compatibility requires that var a restating formal parameter a is allowed, and if let and var bind in the same scope...

Comments welcoe, would like to get this resolved.

/be

Note: See TracTickets for help on using tickets.