Skip to content

Have es2015 rest transform safely use arguments - #2833

Merged
sebmck merged 3 commits into
babel:masterfrom
vhf:rest-arguments-use
Dec 11, 2015
Merged

sebmck merged 3 commits into
babel:masterfrom
vhf:rest-arguments-use

Conversation

@vhf

@vhf vhf commented Nov 5, 2015

Copy link
Copy Markdown
Contributor

Hi, I audited some code generated by Babel and noticed that in some cases some improvements could be made to ensure that V8 optimiser (Crankshaft) will not bail out a little too soon. I focused on the use of arguments: using arguments in an unsafe way prevents V8 (and therefore Node.js) to optimise a function. Here is a first patch in the direction of using arguments safely in Babel es2015-transformed code. Although far from perfect in terms of implementation (first time contributor here, still learning Babel internals, happy to receive your comments and/or to chat on slack or by email) this patch will probably give an idea of what could be done, and could be followed by other patches aiming towards the goal of avoiding generation of V8-unoptimisable code.

With this PR I'm only focusing on the very first and easiest part of my findings. In the future, I'll try to fix some other unsafe uses of arguments in every transformation and then try to focus on other possible V8 optimisations.

From what I understood, performances are more important to Babel than prettiness. This patch somewhat impacts readability of es2015-presets generated code. But it also has impact on performance.

Although tests are green and I did my best to adhere to what I think are Babel's codebase best practices, please review very carefully. For example, I was not sure whether to include optimiseLoadStatement definition in visitor (as seen in defaults.js) or to define them outside of visitor's scope.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

optimized -> optimised.

@vhf

vhf commented Nov 5, 2015

Copy link
Copy Markdown
Contributor Author

Thanks for your comments. In this first commit I fixed the optimized typo, removed the useless newExpr temp variable and reduced nesting by factoring out a function. This is almost guaranteed to work.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is the || arguments[0] === undefined necessary for the v8 optimisation? It seems odd that it would

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You're right, it's not. In most cases arguments.length <= 0 || (undefined || arguments[0]) (I suppose that's what you suggest?) behaves the same way, but they are not really equivalent. Consider the following:

var _arguments = {length: 0};
console.log(_arguments.length <= 0 || _arguments[0] === undefined ? undefined : _arguments[0]);
console.log(_arguments.length <= 0 || (undefined || _arguments[0]));

First case will return undefined, second case will return true.

@vhf
vhf force-pushed the rest-arguments-use branch 2 times, most recently from 1fb31c9 to 1cd8cb7 Compare November 18, 2015 13:51
@vhf
vhf force-pushed the rest-arguments-use branch from 1cd8cb7 to b82138e Compare November 19, 2015 11:48
@vhf
vhf force-pushed the rest-arguments-use branch from b82138e to 9a97d92 Compare November 19, 2015 12:52
@codecov-io

Copy link
Copy Markdown

Current coverage is 89.20%

Merging #2833 into master will not affect coverage as of 19a0c1d

@@            master   #2833   diff @@
======================================
  Files          214     214       
  Stmts        15466   15461     -5
  Branches         0       0       
  Methods          0       0       
======================================
- Hit          13797   13792     -5
  Partial          0       0       
  Missed        1669    1669       

Review entire Coverage Diff as of 19a0c1d

Powered by Codecov. Updated on successful CI builds.

@vhf

vhf commented Nov 19, 2015

Copy link
Copy Markdown
Contributor Author

I finally fixed my PR. Tests are green, coverage remains unchanged.

@jamiebuilds

Copy link
Copy Markdown
Contributor

Looks good to me, @sebmck ?

@sebmck

sebmck commented Nov 19, 2015

Copy link
Copy Markdown
Contributor

Super disappointing that VMs require this :( I'll hopefully get around to reviewing this early next week. Please feel free to ping this issue if I forget. Really sorry for the delay!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Minor style issue: would INDEX be more appropriate than KEY?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good point. I chose KEY because it was consistent with what I knew from the existing codebase, e.g. 0, 1, 2

If there's a consensus towards INDEX, I'll change it asap.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I would've picked INDEX, but it doesn't really matter either way.

sebmck added a commit that referenced this pull request Dec 11, 2015
Have es2015 rest transform safely use `arguments`
@sebmck
sebmck merged commit e9fa841 into babel:master Dec 11, 2015
@sebmck

sebmck commented Dec 11, 2015

Copy link
Copy Markdown
Contributor

Thanks for doing this @vhf! Extremely sorry for the delay in merging this.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@vhf Why not arguments.length <= 0 ? undefined : arguments[0]?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch! The reason behind this code is this code. But you're absolutely right, it's uselessly complicated here.

@chpio

chpio commented Dec 20, 2015

Copy link
Copy Markdown

my KEY gets transformed to NaN:

function func(...sources) {
    for (let i = 0; i < sources.length; i += 1) {
        const source = sources[i];
        // ...
    }
}
function func() {
    for (var i = 0; i < arguments.length; i += 1) {
        var source = arguments.length <= NaN || arguments[NaN] === undefined ? undefined : arguments[NaN];
        // ...
    }
}

@vhf

vhf commented Dec 20, 2015

Copy link
Copy Markdown
Contributor Author

Should be

function func() {
  for (var _len = arguments.length, sources = Array(_len), _key = 0; _key < _len; _key++) {
    sources[_key] = arguments[_key];
  }

  for (var i = 0; i < sources.length; i++) {
    const source = sources[i];
  }
}

instead.

Fixed by #3165

vhf added a commit to vhf/babel that referenced this pull request Dec 21, 2015
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area: perf outdated A closed issue/PR that is archived due to age. Recommended to make a new issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants