-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
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
Kill the "shadow-functions.js" internal plugin in favor of an explicit helper #5677
Conversation
@loganfsmyth, thanks for your PR! By analyzing the history of the files in this pull request, we identified @hzoo, @existentialism and @motiz88 to be potential reviewers. |
(function () { | ||
return _this; | ||
}); | ||
() => this; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These changes are from the removed call on https://github.com/babel/babel/pull/5677/files#diff-3953cdd7a44c58e0431a2e202efb64d4L31 that wasn't needed.
@@ -0,0 +1,11 @@ | |||
class Foo extends class {} { | |||
method() { | |||
var _superprop_callMethod = (..._args) => super.method(..._args); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not pretty, but it's the only way we can deal with this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks okay to me. Nice job, you use the same reference to the super.method
call (instead of two separate) 👍
@@ -416,56 +416,79 @@ class BlockScoping { | |||
// build the closure that we're going to wrap the block with, possible wrapping switch(){} | |||
const fn = t.functionExpression(null, params, | |||
t.blockStatement(isSwitch ? [block] : block.body)); | |||
fn.shadow = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This whole set of changes is gross and probably the most likely to be missing edge cases, but it works as far as I can tell.
These changes were all needed so that we can get access to the final NodePath for the function, since it wasn't available anywhere before.
@@ -1,5 +1,5 @@ | |||
(function () { | |||
var _loop = function (i) { | |||
var _loop2 = function (i) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A change in ordering in block scoping means these variable names are generated in opposite order now, thus breaking the test.
@@ -46,6 +46,10 @@ export default function ({ types: t }) { | |||
if (state.opts.loose) Constructor = LooseTransformer; | |||
|
|||
path.replaceWith(new Constructor(path, state.file).run()); | |||
|
|||
if (path.isCallExpression() && path.get("callee").isArrowFunctionExpression()) { | |||
path.get("callee").arrowFunctionToExpression(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't really have a firm policy for when it's cool to insert actual arrows, so I added a few new arrows in place of .shadow
but explicitly converted them to keep the tests passing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would hope that the dependency system would allow us to add/remove, preferring to instead arrows/newer syntax
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, we can always drop this now if we want, since the plugins are already in a fine order for it AFAIK, I just figured it would be better to leave that for a separate PR.
|
||
ObjectExpression: { | ||
exit(path, file) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This implementation had to change because the usage of the exit
handler meant that the arrow transform would fail for
var obj = {
method(){
var fn = () => super.method();
}
};
since it can't handle super
, meaning that for object-super to work, it needs to run before the arrow transform.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So there's no need for the CONTAINS_SUPER
check anymore?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there's a good reason to have it if we've not using the exit
handler, since the main benefit of it was avoiding traversing the methods more than once. and there's no way to avoid that without the exit
, but the exit
would run it all in the wrong order.
@@ -239,6 +238,8 @@ export function replaceExpressionWithStatements(nodes: Array<Object>) { | |||
} | |||
} | |||
|
|||
this.get("callee").arrowFunctionToExpression(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again just to make the tests pass, we could also leave this out and insert real arrows if we want to.
@@ -245,10 +245,10 @@ defineType("MetaProperty", { | |||
fields: { | |||
// todo: limit to new.target | |||
meta: { | |||
validate: assertValueType("string"), | |||
validate: assertNodeType("Identifier"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These were just wrong :P
Codecov Report
@@ Coverage Diff @@
## 7.0 #5677 +/- ##
==========================================
- Coverage 84.61% 84.57% -0.04%
==========================================
Files 283 282 -1
Lines 9744 9857 +113
Branches 2736 2766 +30
==========================================
+ Hits 8245 8337 +92
- Misses 986 999 +13
- Partials 513 521 +8
Continue to review full report at Codecov.
|
@@ -14,7 +14,7 @@ function foo() { | |||
|
|||
return function () { | |||
babelHelpers.newArrowCheck(this, _this2); | |||
return this; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 looks like this used to be a bug too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since it is .bind
both work, this way is just a little more consistent with the non-spec arrow transform, since they share more code now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer if we could keep this
as this
, but it's not like it's incorrect code here. Minifies better too!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Up for expanding on your reasoning? My motivation was mostly that it's less separation between spec and non-spec mode, but it could be made to work either way I guess.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, I added support for spec arrow in subclass constructors, and made this
stay this
in spec arrows except for the subclass constructor case.
@@ -0,0 +1,11 @@ | |||
class Foo extends class {} { | |||
method() { | |||
var _superprop_callMethod = (..._args) => super.method(..._args); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks okay to me. Nice job, you use the same reference to the super.method
call (instead of two separate) 👍
* Please Note, these flags are for private internal use only and should be avoided. | ||
* Only "shadow" is a public property that other transforms may manipulate. | ||
*/ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we keep this comment. It helped me understand what shadow
'ing meant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy to chat if you have suggestions. This PR mostly removes the whole concept of "shadowing" that we had before, in favor of fixing everything up right away, so it's not obvious where we'd put this, or if it would be useful to people at this point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I you did great by removing unnecessary complexity. The term variable shadowing as explained in the wiki there is confusing to me. It seems that shadowing wasn't the correct concept for passing this
since it's just a transpilation technique. We could maybe chat about that on Slack if you have time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like the shadow transform was actually used for _un_shadowing 😆
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeaaah talk about confusing naming.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I talked with @loganfsmyth and now I'm fine with that.
@@ -95,6 +90,9 @@ function classOrObjectMethod(path: NodePath, callId: Object) { | |||
// Regardless of whether or not the wrapped function is a an async method | |||
// or generator the outer function should not be | |||
node.generator = false; | |||
|
|||
// Unwrap the wrapper IIFE's environment so super and this and such still work. | |||
path.get("body.body.0.argument.callee.arguments.0").unwrapFunctionEnvironment(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wth is body.body.0.argument.callee.arguments.0
😆
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's gross garbage :P It's reaching in to the path where we put the IIFE, since we only get access to the path after it is inserted, so we have to insert it then do this gross stuff to get the reference.
for (var _len2 = arguments.length, innerArgs = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) { | ||
innerArgs[_key2 - 2] = arguments[_key2]; | ||
} | ||
|
||
yield z; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did this yield
move up?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I forget, but this change seems more like what it's supposed to do, so I didn't worry about it. It's possible I looked into the reason, but I don't remember anymore.
} | ||
path.arrowFunctionToExpression({ | ||
// While other utils may be fine inserting other arrows to make more transforms possible, | ||
// the arrow transform itself absolutely cannot insert new arrow functions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because it would leave arrows in the output defeating the purpose, or because it could lead to infinite recursion? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not both? :P
t.variableDeclarator(ref, fn), | ||
])); | ||
} | ||
const has = this.has; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this.has
was just written to a few lines ago in the same function; can it just write directly to const has
, or are there things that look into this.has
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this was leftover from copy-pasting from https://github.com/babel/babel/pull/5677/files/c8ff06f90661f5e67a93b7e2f2d387a029edd98a#diff-775ae6452de339480d54604c7d217b5dL463
Looks like this.has
is used one other place, but otherwise isn't updated, so you're right I could move this up farther.
} | ||
|
||
// handlers async functions | ||
const hasAsync = traverse.hasType(fn.body, this.scope, "AwaitExpression", t.FUNCTION_TYPES); | ||
if (hasAsync) { | ||
fn.async = true; | ||
call = t.awaitExpression(call); | ||
basePath = ".argument" + basePath; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does this break with async iterators?
Though anyone not transforming async iterators are certainly running on an engine where they can skip transforming block scoping... right Safari? <_<
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No idea, I didn't realize this logic even did this until inspecting this code.
* Please Note, these flags are for private internal use only and should be avoided. | ||
* Only "shadow" is a public property that other transforms may manipulate. | ||
*/ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like the shadow transform was actually used for _un_shadowing 😆
t.thisExpression(), | ||
t.identifier(thisBinding), | ||
])) | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like I got you a merge conflict by merging #5620
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No worries.
@@ -14,7 +14,7 @@ function foo() { | |||
|
|||
return function () { | |||
babelHelpers.newArrowCheck(this, _this2); | |||
return this; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer if we could keep this
as this
, but it's not like it's incorrect code here. Minifies better too!
ThisExpression(child) { | ||
thisPaths.push(child); | ||
}, | ||
JSXIdentifier(child) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this for <this.Foo/>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is, I guess I should make this JSXMemberExpression
now that you mention it though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added some checks to explicitly verify the location of the identifier.
() => super(); | ||
() => this; | ||
`, ` | ||
var _supercall = (..._args) => _this = super(..._args), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was wondering if something would catch multiple super()
invocations, but I guess that's the job of the JS engine / class transform.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That I think we should leave up to the class transform, it could certainly add logic for that.
c8ff06f
to
bb7a85e
Compare
Okay, I rebased to fix the merge conflict with spec function naming, and addressed the few issues people have raised except the one question of |
…class constructors.
What if I just need to fix the #3930 issue? |
_forceShadow
,.shadow
or._shadowedFunctionLiteral
properties. Hopefully minimal though.This removes the private "shadow"-related properties from the AST and relies on explicit traversal to handle the conversion of arrow functions into non-arrow functions.
I've also added support for handling
super
andnew.target
into the generic utility to handle cases that we could not handle properly with the old implementation.This also has the added benefit of not performing unexpected transformations on the AST when no plugins were enabled by the user, since we've had users reporting this as an annoyance on several occasions. This is also a bit of a blocker for any work trying to make Babel a more general codemod-like tool.