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

Add loose option for es2015-parameters transformation #5778

Closed
wants to merge 157 commits into from
Closed

Add loose option for es2015-parameters transformation #5778

wants to merge 157 commits into from

Conversation

maurobringolf
Copy link
Contributor

@maurobringolf maurobringolf commented May 26, 2017

Q A
Patch: Bug Fix? No
Major: Breaking Change? No
Minor: New Feature? Yes
Deprecations? No
Spec Compliancy? No
Tests Added/Pass? Added tests
Fixed Tickets Fixes #5776
License MIT
Doc PR No
Dependency Changes No

I tried to implement the loose option as described in the issue. Loose mode should replace default parameters with regular parameters and insert a conditional assignment to the default in the function body. Currently it is all wrapped in a single state.opts.loose condition. It might make more sense to merge it with the existing implementation of non-loose mode, I am not sure about that.

The tests I added and performed are still quite minimal as well, but I would like to get some feedback if I am going into the right direction.

Qantas94Heavy and others added 6 commits May 16, 2017 22:39
The arguments of a function would be unnecessarily copied if there was
a nested function that had a parameter with the same identifier as the
rest parameter for the outer function. This checks the scope of the
parameter is correct before deoptimising.

Fixes: #5656
Refs: #2091
Ignore just non-label break statements in a switch, and allow continue
statments and lablled break statements.

Fixes #5725
Broken by fix to switch statements in block scoping
@mention-bot
Copy link

@maurobringolf, thanks for your PR! By analyzing the history of the files in this pull request, we identified @hzoo, @existentialism and @jridgewell to be potential reviewers.

@codecov
Copy link

codecov bot commented May 26, 2017

Codecov Report

Merging #5778 into 7.0 will increase coverage by 0.03%.
The diff coverage is 95.23%.

Impacted file tree graph

@@            Coverage Diff            @@
##              7.0   #5778      +/-   ##
=========================================
+ Coverage   84.66%   84.7%   +0.03%     
=========================================
  Files         282     282              
  Lines        9869    9888      +19     
  Branches     2774    2778       +4     
=========================================
+ Hits         8356    8376      +20     
+ Misses        998     996       -2     
- Partials      515     516       +1
Impacted Files Coverage Δ
...-plugin-transform-es2015-parameters/src/default.js 90.54% <95.23%> (+1.44%) ⬆️
packages/babel-traverse/src/path/context.js 87.06% <0%> (+0.86%) ⬆️
packages/babel-traverse/src/visitors.js 86.66% <0%> (+0.95%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9e2ef0c...d857fe2. Read the comment docs.

@hzoo
Copy link
Member

hzoo commented May 26, 2017

If you want to be more sure would copy over some of the tests in the current params or make some exec tests - readme example should explain we lose function arity spec behavior

@maurobringolf
Copy link
Contributor Author

@hzoo I will add an explanation of the example in your issue to the readme and copy more tests over, thank you!

const param = params[i];
if (param.isAssignmentPattern()) {
body.push(buildLooseDefaultParam({
VARIABLE_NAME: param.get("left"),
Copy link
Member

Choose a reason for hiding this comment

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

What happens if left is a complex expression? function test({a: b} = {}) {...}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you I did not consider that case yet. Since destructuring has default values on its own I only have to handle the top-level default when parameter destructuring is nested right?

I am currently trying to figure out how to write the if condition in loose mode. Would the following be correct semantics?

function test({a: { b : c} = {}} = {}) {}

Will be transformed into:

function test(a) {
  if( a === undefined ) {
    var { a : { b : c }} = {}
  }
}

Just realized that a is a confusing name for the parameter in this example since the parameter in the original function has no name, so I should generate a uuid from scope I guess?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I copied over some more existing exec tests and realized the above is wrong because destructuring assignment is only executed when the default value is used, so I have to add an else clause. This led me to the following idea for the example above.

function test(_temp) {
  let {a: { b : c} = {}} = _temp === undefined ? {} : _temp;
}

What do you think? I am testing this now.

CORRECTION: There was an additional = {} that I removed now, see comment below.

Copy link
Member

Choose a reason for hiding this comment

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

We can go that route, but I think there's an error in the code. Should be:

  let {a: { b : c} = {}} = _temp === undefined ? {} : _temp;

Otherwise, we would have to do the defaults before the destructuring:

if (a === undefined) {
  a = {};
}
let { a: { b: c } = {} } = a;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You are right, I am sorry about this error, but the actual source code is the way you suggested. The template I defined for this is the following:

const buildLooseDestructuredDefaultParam = template(`
  let ASSIGMENT_IDENTIFIER = PARAMETER_NAME === UNDEFINED ? DEFAULT_VALUE : PARAMETER_NAME;
`);

this will be passed the corresponding values resulting in your version of the example above.

@@ -11,6 +11,12 @@ const buildDefaultParam = template(`
DEFAULT_VALUE;
`);

const buildLooseDefaultParam = template(`
if (VARIABLE_NAME === undefined) {
Copy link
Member

Choose a reason for hiding this comment

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

Do we need to worry about undefined being rewritten in loose code?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, should change that to be a variable and then pass scope.buildUndefinedNode()

@maurobringolf
Copy link
Contributor Author

I have included more tests and taken destructured default parameters into account. The readme of the plugin is updated as well. Looking forward to further feedback @hzoo @jridgewell, Thank you!


`boolean`, defaults to `false`.

In loose mode, parameters with default values will be counted into the arity of the function. This is not spec behavior where these parameters do not add to function arity:

Choose a reason for hiding this comment

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

An explanation of why you'd want this might be a good addition here:

This is a more performant solution as JavaScript engines will fully optimize a function that doesn't reference arguments. Please do your own benchmarking and determine if this option is the right fit for your application.

Mauro Bringolf and others added 2 commits May 30, 2017 21:18
hzoo and others added 3 commits July 11, 2017 22:53
* Fix bug incorrect dereferencing rest argument

* Fix pure path

* Minor refactor
@hzoo
Copy link
Member

hzoo commented Jul 12, 2017

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters - wondering if we need tests for parameters that reference earlier params and others? I guess it's still loose mode anyway

@maurobringolf
Copy link
Contributor Author

I think the linter is failing because I branched off 7.0 before prettier was applied? Not sure how to go about this. I also added one exec test where parameters default to earlier parameters as @hzoo suggested.

@maurobringolf
Copy link
Contributor Author

I messed up the Git history of this branch and will have to continue this change in a new PR, sorry!

@lock lock bot added the outdated A closed issue/PR that is archived due to age. Recommended to make a new issue label Oct 6, 2019
@lock lock bot locked as resolved and limited conversation to collaborators Oct 6, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
outdated A closed issue/PR that is archived due to age. Recommended to make a new issue PR: New Feature 🚀 A type of pull request used for our changelog categories
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet