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

Correctly associate hoisted variables with parameters #2456

Merged
merged 4 commits into from
Sep 14, 2018

Conversation

lukastaegert
Copy link
Member

@lukastaegert lukastaegert commented Sep 12, 2018

This PR contains:

  • bugfix
  • feature
  • refactor
  • documentation
  • other

Are tests included?

  • yes (bugfixes and features will not be merged without tests)
  • no

Breaking Changes?

  • yes (breaking changes will not be merged unless absolutely necessary)
  • no

List any relevant issue numbers:
Resolves #2451

Description

This was a tricky one. Apparently, hoisted variables in function copes are identified with parameters if the names match. The difficulty comes from the fact that those variables are not available for parameter defaults while other parameters are:

var a = 1;
var d = 4;
function fn(a, b = a, c = d) {
  var a; // is associated with the parameter a and thus the default for b
  var d; // is not associated with the default
  console.log(a, b, c, d);
}
fn(1); // logs "1 1 4 undefined"

Another difficulty arose from the fact that Rollup's logic first initializes child AST nodes + declared variables before initializing their parents thus we cannot check for hoisted variables if there already is a parameter of the same name.

The solution I went with is to collect all hoisted variables in a special scope which is available to parameters when they are initialized. If they find there exists a hoisted variable of the same name, they reuse this variable instead of creating a new one.

To make this possible, there also is no longer a distinction between parameters and other local variables which also simplifies the code.

Copy link

@robpalme robpalme left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for pursuing this tough bug. This looks great.

I ran into the same ordering issues you mentioned in the description; children initialized before the parameter variables are known. The hoistedBodyVarScope solution seems reasonable.

Two minor style comments attached.

One general comment: given you've identified that var-declarations are not truly hoisted into the parameter scope and therefore are unavailable as default parameters, maybe we should add a test case for this in case later refactoring hits it? Something like:

function throwsReferenceError (a = b) {
   var b;
}

function test () {
  try {
    throwsReferenceError();
  } catch (e) {
    return;
  }
  throw Error("Failed to throw ReferenceError");
}

test();

src/ast/scopes/ParameterScope.ts Show resolved Hide resolved
test/function/samples/redeclare-parameter/main.js Outdated Show resolved Hide resolved
@lukastaegert
Copy link
Member Author

Thanks for the detailed review, this is a huge help and always highly appreciated!

given you've identified that var-declarations are not truly hoisted into the parameter scope and therefore are unavailable as default parameters, maybe we should add a test case for this in case later refactoring hits it?

I am not sure about the value of such a test because this would mostly test that the runtime environment behaves correctly. At least at the moment I fail to envision a situation where optimizations done by rollup could brake this. For rollup, in order to make wrong variable associations visible, the most effective way is to trigger a deconflicting situation via scope-hoisting, so instead I added a test that does just that and checks the combinatorics around parameters used as defaults for other parameters. Which promptly delivered a test case for your other comment.

Copy link

@robpalme robpalme left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test changes look good.

Your test for deconflicting parameter defaults make a lot more sense than my suggestions. Thanks for adding!

@lukastaegert lukastaegert merged commit 125e6ed into master Sep 14, 2018
@lukastaegert lukastaegert deleted the hoisted-variable-parameters branch September 14, 2018 04:28
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

Successfully merging this pull request may close these issues.

Invalid optimisation: var-declared parameters become undefined
3 participants