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

Functions defined in nested blocks in sloppy mode not recognised as in scope #224

Open
overlookmotel opened this issue Jul 13, 2021 · 3 comments
Labels
bug Something isn't working instrumentation Relates to instrumentation maybe fixed Maybe fixed but needs tests

Comments

@overlookmotel
Copy link
Owner

Binding location of functions nested in blocks is dependent on strict/sloppy mode.

Function declarations are bound:

  • Strict mode: to statement block they are in.
  • Sloppy mode: to body of enclosing function or program.
// Sloppy mode
{
  function f() {}
}
module.exports = () => typeof f; // 'function'
'use strict';
{
  function f() {}
}
module.exports = () => typeof f; // 'undefined'

Livepack does not currently handle the sloppy mode case. Babel plugin does not recognise that f is in scope of the exported function and so f does not get captured.

This is at least in part down to a bug in Babel. babel/babel#13549

Probably also a bug in Livepack.

@overlookmotel overlookmotel added the bug Something isn't working label Jul 13, 2021
@overlookmotel overlookmotel added the instrumentation Relates to instrumentation label Sep 30, 2021
@overlookmotel
Copy link
Owner Author

There's a further complication. Even in sloppy mode, a function will be bound within its own block if there's an existing var in the scope it would otherwise be hoisted to defined with const, let, a class declaration, or a function param.

In these cases, the inner x is bound to it's "home" block:

// Sloppy mode
const x = () => 1;
{
  {
    function x() { return 2; }
    console.log(x()); // Logs 2
  }
  console.log(x()); // Logs 1
}
// Sloppy mode
let x = () => 1;
{
  {
    function x() { return 2; }
    console.log(x()); // Logs 2
  }
  console.log(x()); // Logs 1
}
// Sloppy mode
class x {
  constructor() { this.y = 1; }
}

{
  {
    function x() { return 2; }
    console.log(x()); // Logs 2
  }
  console.log(new x().y); // Logs 1
}
function outer(x) {
  {
    function x() { return 2; }
    console.log(x()); // Logs 2
  }
  console.log(x()); // Logs 1
}
outer(() => 1);

Whereas in these cases it's hoisted to the outer block:

// Sloppy mode
var x = () => 1;
{
  {
    function x() { return 2; }
    console.log(x()); // Logs 2
  }
  console.log(x()); // Logs 2
}
// Sloppy mode
function x() { return 1; }
{
  {
    function x() { return 2; }
    console.log(x()); // Logs 2
  }
  console.log(x()); // Logs 2
}

These observations made on Node v16.10.0. Have not checked behavior on previous Node versions.

@overlookmotel
Copy link
Owner Author

A const, let or class declaration in an intermediate scope also prevents function being hoisted.

// Sloppy mode
{
  const x = () => 1;
  {
    function x() { return 2; }
    console.log(x()); // Logs 2
  }
  console.log(x()); // Logs 1
}
console.log(typeof x); // Logs undefined
// Sloppy mode
{
  let x = () => 1;
  {
    function x() { return 2; }
    console.log(x()); // Logs 2
  }
  console.log(x()); // Logs 1
}
console.log(typeof x); // Logs undefined
// Sloppy mode
{
  class x {
    constructor() { this.y = 1; }
  }

  {
    function x() { return 2; }
    console.log(x()); // Logs 2
  }
  console.log(new x().y); // Logs 1
}
console.log(typeof x); // Logs undefined

In all cases, whether the other var is in TDZ when function is defined makes no difference:

// Sloppy mode
{
  {
    function x() { return 2; }
    console.log(x()); // Logs 2
  }
  const x = () => 1;
  console.log(x()); // Logs 1
}
console.log(typeof x); // Logs undefined

@overlookmotel
Copy link
Owner Author

I think fixed in b2a0a09. Needs tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working instrumentation Relates to instrumentation maybe fixed Maybe fixed but needs tests
Projects
None yet
Development

No branches or pull requests

1 participant