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

the var declaration is missing with the default function parameters scope #10798

Closed
guox191 opened this issue Dec 3, 2019 · 10 comments · Fixed by #11158
Closed

the var declaration is missing with the default function parameters scope #10798

guox191 opened this issue Dec 3, 2019 · 10 comments · Fixed by #11158
Assignees
Labels
Has PR i: bug outdated A closed issue/PR that is archived due to age. Recommended to make a new issue

Comments

@guox191
Copy link

guox191 commented Dec 3, 2019

Bug Report

Current Behavior

Playground:
https://babeljs.io/repl#?browsers=&build=&builtIns=false&spec=false&loose=false&code_lz=GYVwdgxgLglg9mABFApgZygClJRBeRTASnwD5EBvAKEUQDcBDAJ0Qf0QEYBuGxCBNHAA2KAHRC4Ac0wMiPAL4lqtRiwAOTFHXgg07AAwKuQA&debug=false&forceAllTransforms=false&shippedProposals=false&circleciRepo=&evaluate=false&fileSize=false&timeTravel=false&sourceType=module&lineWrap=true&presets=es2015&prettier=false&targets=&version=7.7.4&externalPlugins=

Input Code

function test(func = () => {
  var a = 1;
  console.log(a);
}) {
  var previous = 0;
};

Translate these with es2015 presets, the result is:

"use strict";

function test() {
  var func = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {
    var a = 1;
    console.log(a);
  };
  return function () {
    previous = 0;
  }();
}

;

We can see that the generated function returns a closure, and the various previous is not declared in the closure.

in strict mode, we will get the exception:
Uncaught ReferenceError: previous is not defined

Expected behavior/code
The var expression won't be removed.

Babel Configuration (babel.config.js, .babelrc, package.json#babel, cli command, .eslintrc)

Babel playground with es2015 preset config.

Additional context/Screenshots
image

@babel-bot
Copy link
Collaborator

Hey @guox191! We really appreciate you taking the time to report an issue. The collaborators on this project attempt to help as many people as possible, but we're a limited number of volunteers, so it's possible this won't be addressed swiftly.

If you need any help, or just have general Babel or JavaScript questions, we have a vibrant Slack community that typically always has someone willing to help. You can sign-up here for an invite."

@smelukov
Copy link
Contributor

I'm trying to research and solve it ☺️

@smelukov
Copy link
Contributor

smelukov commented Dec 13, 2019

I think that I've found the reason:

@nicolo-ribaudo I have a question here (cause, for now, I'm not so familiar with babel internals):
If hoistVariables replaces VariableDeclaration with multiple AssignmentExpression then it should add another VariableDeclaration with empty init prop on VariableDeclarator to the top of the function? Where is it happen?

Thanks

@nicolo-ribaudo
Copy link
Member

nicolo-ribaudo commented Dec 16, 2019

id => scope.push({ id })

This code, which is passed as a parameter to VariableDeclaration should already add the variables without initializer to the scope. Maybe that function isn't always called by hoistVariables, or it has the wrong scope?

@JLHwung
Copy link
Contributor

JLHwung commented Dec 28, 2019

@smelukov Looking into the code

hoistVariables(path, id => scope.push({ id }));

and

state.emit(t.identifier(name), name, declar.node.init !== null);

I think the timing of registering declaration is incorrect. While state.emit looks like a callback executed after the path is replaced by assignment expressions

path.replaceWithMultiple(nodes);

it is actually executed before the path is replaced. Therefore the inserted variable declarators from scope.push is also replaced.

We can delay the timing of scope.push after we collect an array of declared variables.

I composed a unit test when digging this issue, you may work from this branch: https://github.com/JLHwung/babel/tree/add-test-10798

JLHwung added a commit to JLHwung/babel that referenced this issue Dec 28, 2019
@JLHwung
Copy link
Contributor

JLHwung commented Dec 28, 2019

@homobel Thanks for the reproduction case! Yes it is the same issue.

@JLHwung
Copy link
Contributor

JLHwung commented Jan 17, 2020

@smelukov Are you still working on this issue? If you have any questions about developing a fix, feel free to ask in #development channel.

@openorclose
Copy link
Contributor

Tried on this pr #11158,

and got this which should be right:

function test() {
  var func = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {
    var a = 1;
    console.log(a);
  };
  return function () {
    var previous = 0;
  }();
}

;

@homobel
Copy link

homobel commented May 18, 2020

@nicolo-ribaudo @JLHwung Unfortunately #11158 fix not resolved issue with default parameter with "this"(7.9.6) I've mentioned above.

class B {}

class A extends B {
	constructor(name, cb = () => console.log(this.name)) {
		super();

		this.name = name;
		this.cb = cb;
	}
}

const a = new A('Foo');
a.cb();

Produces:

let B = function B() {
  _classCallCheck(this, B);
};

let A = /*#__PURE__*/function (_B) {
  _inherits(A, _B);

  var _super = _createSuper(A);

  function A(name) {
    var _this2 = this;

    let cb = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : () => console.log(_this.name);
    return function () {
      var _this;

      _classCallCheck(_this2, A);

      _this = _super.call(_this2);
      _this.name = name;
      _this.cb = cb;
      return _this;
    }();
  }

  return A;
}(B);

const a = new A('Foo');
a.cb();

With same "_this is not defined" error.

I'm not sure about approaches here used but I've checked example on playground with only @babel/plugin-transform-classes enabled and it produces also invalid code(same error):

let B = function B() {
  _classCallCheck(this, B);
};

let A = /*#__PURE__*/function (_B) {
  _inherits(A, _B);

  var _super = _createSuper(A);

  function A(name, cb = () => console.log(_this.name)) {
    var _this;

    _classCallCheck(this, A);

    _this = _super.call(this);
    _this.name = name;
    _this.cb = cb;
    return _this;
  }

  return A;
}(B);

const a = new A('Foo');
a.cb();

@github-actions github-actions bot added the outdated A closed issue/PR that is archived due to age. Recommended to make a new issue label Aug 18, 2020
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 18, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Has PR i: bug outdated A closed issue/PR that is archived due to age. Recommended to make a new issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants