-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Emit more efficient/concise "empty" ES6 ctor #10189
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
Emit more efficient/concise "empty" ES6 ctor #10189
Conversation
Hi @chancancode, I'm your friendly neighborhood Microsoft Pull Request Bot (You can call me MSBOT). Thanks for your contribution! TTYL, MSBOT; |
A few data points:
|
Interesting - I know that the spec is very explicit about how the constructor body that gets generated. Step 10 on https://tc39.github.io/ecma262/#sec-runtime-semantics-classdefinitionevaluation right now says that it's constructor(...args){ super (...args);} But I can't imagine what differences there could be. They literally even use the same iterator by using %ArrayProto_values%. From Step 23 on https://tc39.github.io/ecma262/#sec-createmappedargumentsobject right now:
Pinging @bterlson for any input on this. |
When there are property assignments in a the class body of an inheriting class, tsc current emit the following compilation: ```ts class Foo extends Bar { public foo = 1; } ``` ```js class Foo extends Bar { constructor(…args) { super(…args); this.foo = 1; } } ``` This introduces an unneeded local variable and might force a reification of the `arguments` object (or otherwise reify the arguments into an array). This is particularly bad when that output is fed into another transpiler like Babel. In Babel, you get something like this today: ```js var Foo = (function (_Bar) { _inherits(Foo, _Bar); function Foo() { _classCallCheck(this, Foo); for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _Bar.call.apply(_Bar, [this].concat(args)); this.foo = 1; } return Foo; })(Bar); ``` This causes a lot of needless work/allocations and some very strange code (`.call.apply` o_0). Admittedly, this is not strictly tsc’s problem; it could have done a deeper analysis of the code and optimized out the extra dance. However, tsc could also have emitted this simpler, more concise and semantically equivalent code in the first place: ```js class Foo extends Bar { constructor() { super(…arguments); this.foo = 1; } } ``` Which compiles into the following in Babel: ```js var Foo = (function (_Bar) { _inherits(Foo, _Bar); function Foo() { _classCallCheck(this, Foo); _Bar.apply(this, arguments); this.foo = 1; } return Foo; })(Bar); ``` Which is well-optimized (today) in most engines and much less confusing to read. As far as I can tell, the proposed compilation has exactly the same semantics as before. Fixes microsoft#10175
09e72bf
to
cc2dc3a
Compare
Yup, see #7596. |
I don't know about claims of efficiency as arguments object comes with its own amount of overhead but @chancancode's suggestion is semantically equivalent (with the exception of the constructor's toString() output). |
Any updates on this? 😄 We are getting close to shipping so we need a solution for this. We can use my branch, write an AST transform etc, but if an official fix is on the horizon (even just in nightly), then we won't bother coming up with our own solution. |
👍, @mhegazy? |
👍 |
@rbuckton any comments? |
@yuit we need to port this to transforms branch |
This looks like an acceptable change to me. |
@bterlson seems good? |
@RyanCavanaugh seems fine to me, although a comment documenting the seeming deviation from the letter of ES6 would be appreciated I think. |
Good idea. @chancancode can you leave the original comment and document the deviation? |
Merged, we'll add the comment for you 😄 |
Thank you @DanielRosenwasser! |
Fixes #10175
Our setup is to have tsc compile into ES6 and run the output through our existing build pipeline (babel plugins, etc). I understand this is not strictly speaking a bug in tsc, but it is definitely a problem for us. This is a case where both sides, independently, are doing an acceptable thing but the combination of the two is not, so I figured I'll give this a shot and see if we can get this addressed in tsc.
When there are property assignments in a the class body of an inheriting class, tsc current emit the following compilation:
This introduces an unneeded local variable and might force a reification of the
arguments
object (or otherwise reify the arguments into an array).This is particularly bad when that output is fed into another transpiler like Babel. In Babel, you get something like this today:
This causes a lot of needless work/allocations and some very strange code (
.call.apply
o_0).Admittedly, this is not strictly tsc’s problem; it could have done a deeper analysis of the code and optimized out the extra dance. However, tsc could also have emitted this simpler, more concise and semantically equivalent code in the first place:
Which compiles into the following in Babel:
Which is well-optimized (today) in most engines and much less confusing
to read.
As far as I can tell, the proposed compilation has exactly the same
semantics as before.