Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Block-level functions and web extensions #73

Open
jrvidal opened this issue Jul 17, 2015 · 4 comments
Open

Block-level functions and web extensions #73

jrvidal opened this issue Jul 17, 2015 · 4 comments

Comments

@jrvidal
Copy link

jrvidal commented Jul 17, 2015

Context: http://stackoverflow.com/questions/31419897/what-are-the-precise-semantics-of-block-level-functions-in-es6

I'm not well versed with the ES6 spec details, but from my understanding, there is some optional behavior for block-level function declarations that escope does not take into account.

In the following code:

// ES6

function foo() {

    {
        function bar() {};
    }

    bar(1);
}

the call to bar would not throw a ReferenceError if the web compatibility semantics extension is in effect.

The escope output for this code with {ecmaVersion: 6} does not link the bar reference with the bar declaration at all. Maybe an additional webExtensions flag for escope.analyze is needed.

@bakkot
Copy link

bakkot commented Jul 18, 2015

The behavior of the compatibility semantics (as I understand it) is as follows:
When a function body is instantiated, it looks for functions declared in blocks inside of the body which could be replaced with var declarations of the same name without causing an early error. This replacement does not actually occur, but an additional binding may be created. Essentially this requirement means that the new binding may not conflict with any lexical bindings in the block's scope or any containing scope, up to and including the scope in which the function's parameters are declared.

For such functions, a var-scoped binding is created in the function body's scope, in addition to the lexical binding created in the block containing the function declaration. This additional binding is initially undefined. When the function declaration statement is executed, instead of the statement doing nothing (recall that function declarations are hoisted to the top of the containing block), the statement will fetch the value of the normal, lexical binding created by the declaration and set the additional var-scoped binding to that value.

This can can lead to some surprising behavior. For example, the following snippet prints 0 with no errors (per spec, although I'm not aware of any correct implementations at the moment):

(function(){
  {
    f = 0;
    function f(){};
  }
  console.log(f);
})();

Note also that there is currently a spec bug, which I just reported, which means that none of this will actually happen.

@mysticatea
Copy link
Contributor

It looks like a bug to me.

I guess escope should use this.currentScope().variableScope instead of this.currentScope() at this line: https://github.com/estools/escope/blob/master/src/referencer.js#L292

@mysticatea
Copy link
Contributor

Ah, there is a comment above the line...

@bergus
Copy link

bergus commented Aug 27, 2015

I don't think there's anything wrong with the current behaviour. Even if it may work while not being recognised by this tool, you should strife to write valid code that does not rely on being executed in a legacy environment anyway.
So if this feature should be built into escope, it should definitively be hidden behind a flag.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants