-
-
Notifications
You must be signed in to change notification settings - Fork 475
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
fix(isolated_declarations): Emit computed properties when they are well known symbols #4099
fix(isolated_declarations): Emit computed properties when they are well known symbols #4099
Conversation
Your org has enabled the Graphite merge queue for merging into mainAdd the label “merge” to the PR and Graphite will automatically add it to the merge queue when it’s ready to merge. Or use the label “hotfix” to add to the merge queue as a hot fix. You must have a Graphite account and log in to Graphite in order to use the merge queue. Sign up using this link. |
197ad19
to
194ef76
Compare
CodSpeed Performance ReportMerging #4099 will not alter performanceComparing Summary
|
79a5d02
to
224c490
Compare
Have you looked at scope.rs? |
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
Can you explain more what the difference between this approach and typescript's behavior? |
With an alternative approach this code would have an isolated declarations error: export function foo(Symbol: any): void {
const x = {[Symbol.iterator]: 1};
} because |
Let's try this alternative approach. |
Ok, I might not get to this until next weekend |
cff5ab1
to
8e97f1d
Compare
@Dunqing actually I thought of an approach that matches the same behavior as TypeScript, but I'm not sure if the performance/memory usage would be considered acceptable by the project. Please take a look. |
4ccf44d
to
14de332
Compare
// Collect information about global Symbol usage within computed | ||
// properties before performing any transformations. | ||
self.global_symbol_binding_tracker.visit_program(program); |
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 could probably avoid allocations for this most of the time by first checking if there is both an occurrence of overriding the global Symbol and references to Symbol anywhere in the program, not necessarily in the same scope, but I'm not sure what the cost of that extra traversal would be.
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 it possible to check on demand? For example, we initially only check for the root scope. When we transform the function we will check the function. This will only check for declarations that will be exported
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.
Possibly, I will think about it some more. I think one of the main challenges comes just from organizing the code and performing state management. It feels like similar to with ScopeTree
we would need to tell GlobalSymbolBindingTracker
when it is entering or leaving a scope, but the logic will diverge from ScopeTree
(otherwise we would have just used ScopeTree
for this purpose), which may result in the code looking more confusing.
(Just voicing my thoughts out loud) I think what feels redundant with my current approach is that the short-circuit optimizations I take are making assumptions about how the tree will be traversed for emitting declarations, so that logic kind of feels like it's being reproduced in two places. The code could be potentially cleaner if that logic could be pulled out. What I imagine is something like alternative Visitor
traits that only traverse the parts of the AST relevant to emitted declarations. Such traits would already have the "optimizations" I wrote naturally built-in. What's harder for me to imagine right now is how easy it would be to re-implement declaration transforms on top of such traits.
Please let me know your thoughts.
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.
Possibly, I will think about it some more. I think one of the main challenges comes just from organizing the code and performing state management. It feels like similar to with
ScopeTree
we would need to tellGlobalSymbolBindingTracker
when it is entering or leaving a scope, but the logic will diverge fromScopeTree
(otherwise we would have just usedScopeTree
for this purpose), which may result in the code looking more confusing.
Only Function
and TSModuleDeclaration
will have nested scopes, which I don't think should be too much trouble. We could try.
(Just voicing my thoughts out loud) I think what feels redundant with my current approach is that the short-circuit optimizations I take are making assumptions about how the tree will be traversed for emitting declarations, so that logic kind of feels like it's being reproduced in two places. The code could be potentially cleaner if that logic could be pulled out. What I imagine is something like alternative
Visitor
traits that only traverse the parts of the AST relevant to emitted declarations. Such traits would already have the "optimizations" I wrote naturally built-in. What's harder for me to imagine right now is how easy it would be to re-implement declaration transforms on top of such traits.
Adding another trait instead of Visitor
may significantly increase maintenance costs. Currently Visitor
is automatically generated with ast-codegen
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.
Adding another trait instead of Visitor may significantly increase maintenance costs
That's a good point. I suppose it would be necessary for such a trait to extend Visit
and only override a small subset of the method implementations.
Only
Function
andTSModuleDeclaration
will have nested scopes, which I don't think should be too much trouble. We could try.
I may not be able to get to this soon, but as you said, we have time!
21cb1e4
to
0d7ebf5
Compare
No problem, no rush :)
I think it's ok to wait, though I pinged the TypeScript repo to see if they might include the change in a new patch release of 5.5. |
c4ef594
to
5d16684
Compare
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 we can skip visit_statement
in GlobalSymbolBindingTracker
. We just need to collect bindings.
I believe we need to traverse into the bodies of |
Oh yes. You are right. Can we skip some statements that are not binding, traversing the whole AST has a performance impact. |
51b5361
to
a7e1cbc
Compare
@Dunqing I made some optimizations to bring the benchmark back to baseline performance, but each one needs careful attention to make sure there isn't a scenario that I didn't think of. |
Well done! |
Can you add a test(like this) that uses non-global |
I saw that v5.5.4 doesn't include this change. We have more time to improve this! |
Sure, just added them |
// Collect information about global Symbol usage within computed | ||
// properties before performing any transformations. | ||
self.global_symbol_binding_tracker.visit_program(program); |
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 it possible to check on demand? For example, we initially only check for the root scope. When we transform the function we will check the function. This will only check for declarations that will be exported
de8c843
to
6b634ce
Compare
@Dunqing what do we need to do to get this over the finish line? |
Feel free to convert it to open and ping me if it ready to review |
I'm about to go on vacation so will probably not be able to touch this for at least another couple weeks. If anyone wants to iterate on this in the meantime, please feel free :) |
Thank you for your hard work on Isolated Declaration! Have a nice holiday!! |
25a0fb9
to
ab309ee
Compare
dfd19b6
to
2045470
Compare
Close as stale. Feel free to comeback when ts is updated. |
Fixes #4016.
To fully comply with the reference behavior of the TypeScript compiler, we need to be able to detect the case when
Symbol
orglobalThis.Symbol
do not refer to the globalSymbol
. I achieve this by creating a similar structure toScopeTree
calledGlobalSymbolBindingTracker
(will take suggestions for a better name) and have it visit the entire program before we begin transformation. We can keep track of which computed properties reference non-globalSymbol
by storing a set of their spans, which should each be uniquely identifying since we're in the context of a single file.Later, if and when we transform the computed property into a type declaration, we can refer back to this set to see if it should be allowed.