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

refactor(ast): get rid of unsafe transmutation in VisitMut trait. #2764

Merged
merged 9 commits into from Mar 23, 2024

Conversation

rzvxa
Copy link
Collaborator

@rzvxa rzvxa commented Mar 18, 2024

This will close #2745,

In this PR I attempt to fix this issue using a combination of ideas discussed in the issue mentioned above, I've created this early draft so people can pitch in if there is something I should consider doing.

The first goal of this PR is to resolve the issue with the possible illegal references, As a result of my approach it would also end up with a bunch of walk_* and walk_*_mut functions to help with the abstraction. I want to eliminate enter_node and leave_node functions, but I still haven't started working on it since I first want to familiarize myself with all of its usage throughout the project. I'm hesitating to do it at the moment, When we want to do this it would require quite a bit of refactoring so we should make sure it is probably going to work and end up being a better implementation.

@github-actions github-actions bot added A-ast Area - AST A-transformer Area - Transformer / Transpiler labels Mar 18, 2024
@rzvxa
Copy link
Collaborator Author

rzvxa commented Mar 20, 2024

@Boshen somehow I can't get tasks to run on my system, Is there any possibility for me to have tasks run on GitHub? If there is no lack of resources for running my tasks remotely, it would help me to continue my work on it easier. Please consider approving this PR for running workflows.

It stopped me dead on track after the initial commit of mine, I need to get it to work before I can continue working on the issue on hand since I'm sure I've already broken some stuff with this change.

I suspect that it is because of external resources, I've found out that older releases(which went around 5 to 8 months back) use submodules to add some external repositories, and I can see there are no more submodules on the main for including them, What is the approach now?

@Dunqing
Copy link
Member

Dunqing commented Mar 20, 2024

@Boshen somehow I can't get tasks to run on my system, Is there any possibility for me to have tasks run on GitHub? If there is no lack of resources for running my tasks remotely, it would help me to continue my work on it easier. Please consider approving this PR for running workflows.

Sure, Since this is your first time contributing to this repo we need to manually approve the run ci.

Copy link

codspeed-hq bot commented Mar 20, 2024

CodSpeed Performance Report

Merging #2764 will not alter performance

Comparing rzvxa:main (c478207) with main (d9b77d8)

Summary

✅ 34 untouched benchmarks

@rzvxa
Copy link
Collaborator Author

rzvxa commented Mar 20, 2024

@Boshen somehow I can't get tasks to run on my system, Is there any possibility for me to have tasks run on GitHub? If there is no lack of resources for running my tasks remotely, it would help me to continue my work on it easier. Please consider approving this PR for running workflows.

Sure, Since this is your first time contributing to this repo we need to manually approve the run ci.

Thanks!

@rzvxa rzvxa marked this pull request as ready for review March 20, 2024 17:31
@rzvxa
Copy link
Collaborator Author

rzvxa commented Mar 20, 2024

This PR should be ready to merge, I've managed to get rid of the transmutation in the VisitMut trait altogether. I've also made a few methods in that trait use a walk function for their underlying implementation. If you guys agree in another PR I can refactor all methods to use walk functions for abstraction.

Let me know if there is any issue, ambiguity, lack of documentation, or clarity in this PR.

@rzvxa rzvxa changed the title refactor: better abstraction for visitors refactor(ast, transformer): get rid of unsafe transmutation in VisitMut trait. Mar 20, 2024
@rzvxa rzvxa changed the title refactor(ast, transformer): get rid of unsafe transmutation in VisitMut trait. refactor(ast): get rid of unsafe transmutation in VisitMut trait. Mar 20, 2024
@rzvxa
Copy link
Collaborator Author

rzvxa commented Mar 20, 2024

@Dunqing Hi, Sorry to ping you here. Can you approve workflow actions for this PR once more? I've found a workaround for tests by running them on my fork but I can't get the benchmark result.
If there are no performance drops it should be ready to merge.

@Boshen
Copy link
Member

Boshen commented Mar 21, 2024

I wonder whether we should copy a VisitMut2 and move this to the transformer, so the transformer is all self-contained.

For two reasons:

  • I think there are downstream tools which depend on this, this'll be a huge breaking change.
  • The transformer will end-up with a few more iterations of overhaul. There are several people who are looking at the transformer from different angles. All self-contained code will make transitions easier.

I'm on vacation right now, please excuse me for the late response and the trouble.

@rzvxa
Copy link
Collaborator Author

rzvxa commented Mar 21, 2024

Hello @Boshen, First off I wanna thank you for responding while on vacation. It means a lot!

I think there are downstream tools which depend on this, this'll be a huge breaking change.

I believe if this change goes through despite it being a breaking change it will be a net positive for the third-party tools since we don't enforce unsafe code without letting them know about our assumptions of the code. Plus we are in the minor versions so breaking changes should be expected as it is not a stable release. It is also fairly easy to achieve the same thing without the old design, And if you need the transmutation in a specific transformer or third-party tool? well, then it is on the implementor to handle it, not the core trait itself.
If we don't change how visitors work right now, It going to never change or at least it is going to be left untouched for quite a long time since we will be too scared to even touch it.

The transformer will end-up with a few more iterations of overhaul. There are several people who are looking at the transformer from different angles. All self-contained code will make transitions easier.

I didn't quite understand this statement, Can you let me know why is there going to be additional iteration as a result of this approach?

If we add the walk functions most of the time when changing the logic we are going to do it in walk_functions and those can be used for most use cases unless something really needs deep changes like how SemanticBuilder works. So most people won't need to worry about upcoming breaking changes if they only rely on visit functions and defer walking to the walk function instead of doing it themselves.

I honestly believe we can even remove enter_node leave_node, enter_scope, and leave_scope, All of these methods can be implemented by the user instead of being called by walk_functions from within the trait, Localizing the Visitor, in this case, means that people have a basic visitor that can visit any node if they want to handle scope or node events they can use these methods instead of having specific methods for those events. It would make specialized traits local to their own transformer/crate.


TL;DR I think since implementing a custom VisitMut with walk functions introduced in #2776 isn't going to be such a chore, People can implement their own custom implementations if there is a need for a specific visitor in a specific case.

@rzvxa
Copy link
Collaborator Author

rzvxa commented Mar 21, 2024

Localizing the Visitor, in this case, means that people have a basic visitor that can visit any node if they want to handle scope or node events they can use these methods instead of having specific methods for those events. It would make specialized traits local to their own transformer/crate.

I just want to add this, We can have a local trait called ScopeVisit which adds the scope events and is a super trait of the Visit.

@rzvxa
Copy link
Collaborator Author

rzvxa commented Mar 21, 2024

In future, we can also experiment with the use of reference counting for AstNode's reference to the node itself. This way we shouldn't need the transmutation to begin with at the cost of some runtime performance.

@rzvxa
Copy link
Collaborator Author

rzvxa commented Mar 21, 2024

Sorry for speaking in such short comments, I'm just thinking about this PR out loud so you know my thought process and can give me some insight if there is something that I'm not aware of.

Take this for example

impl<'a> VisitMut<'a> for NewTarget<'a> {
    fn enter_node(&mut self, kind: AstKind<'a>) {
        if let Some(kind) = self.get_kind(kind) {
            self.kinds.push(kind);
        }
    }

    fn leave_node(&mut self, kind: AstKind<'a>) {
        if self.get_kind(kind).is_some() {
            self.kinds.pop();
        }
    }
}

While it does implement VisitMut it doesn't need it to work. If it was going to actually walk the nodes itself it would've been a lot slower.
I've just taken the idea which was already there and rewrote it as such:

impl<'a> NewTarget<'a> {
    pub(crate) fn enter_method_definition(&mut self, def: &MethodDefinition<'a>) {
        let kind = match def.kind {
            MethodDefinitionKind::Get
            | MethodDefinitionKind::Set
            | MethodDefinitionKind::Method => NewTargetKind::Method,
            MethodDefinitionKind::Constructor => NewTargetKind::Constructor,
        };
        self.push(kind);
    }

    pub(crate) fn leave_method_definition(&mut self, _: &MethodDefinition) {
        self.pop();
    }

    pub(crate) fn enter_object_property(&mut self, prop: &ObjectProperty<'a>) {
        if prop.method {
            self.push(NewTargetKind::Method);
        }
    }

    pub(crate) fn leave_object_property(&mut self, prop: &ObjectProperty<'a>) {
        if prop.method {
            self.pop();
        }
    }

    pub(crate) fn enter_function(&mut self, func: &Function<'a>) {
        if let Some(kind) = self.function_new_target_kind(func) {
            self.push(kind);
        }
    }

    pub(crate) fn leave_function(&mut self, func: &Function<'a>) {
        if self.function_new_target_kind(func).is_some() {
            self.pop();
        }
    }

    fn function_new_target_kind(&self, func: &Function<'a>) -> Option<NewTargetKind<'a>> {
        // oxc visitor `MethodDefinitionKind` will enter `Function` node, here need to exclude it
        if let Some(kind) = self.kinds.last() {
            if !matches!(kind, NewTargetKind::Function(_)) {
                return None;
            }
        }
        func.id.as_ref().map(|id| NewTargetKind::Function(Some(id.name.clone())))
    }
}

Now with walk functions, we can have the main transformer(transformer/lib.rs) implement something like this:

    // from Transformer implementation of VisitMut
    fn visit_function(&mut self, func: &mut Function<'a>, flags: Option<ScopeFlags>) {
        self.es2015_new_target.as_mut().map(|t| t.enter_function(func));
        walk_function_mut(self, func, flags);
        self.es2015_new_target.as_mut().map(|t| t.leave_function(func));
    }

It is a bit more verbose as you can see, But it is more readable, does less work, compiles easier(including compiler optimizations), and is more future-proof. For example, it won't break if we add an additional AstKind and don't have a default case, or even worse we handle the default case which makes a place that needs change go unnoticed.

@rzvxa
Copy link
Collaborator Author

rzvxa commented Mar 21, 2024

I would like to elaborate on this statement.

does less work, compiles easier(including compiler optimizations)

In the example I've mentioned above it wouldn't be noticeable, But when walking huge arrays if we don't call enter and leave functions on every node it is going to let the compiler and CPU better use cache lines which in theory should minimize the cache misses while iterating the child nodes.

I believe if this early benchmark improvement isn't a false positive, It is probably because of the reason I've talked about here.

Copy link
Member

@Boshen Boshen left a comment

Choose a reason for hiding this comment

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

Let's get the boat sailing.

@Boshen Boshen enabled auto-merge (squash) March 23, 2024 13:44
@Boshen Boshen merged commit 813226b into oxc-project:main Mar 23, 2024
27 checks passed
charnog pushed a commit to charnog/oxc that referenced this pull request Mar 26, 2024
…c-project#2764)

This will close oxc-project#2745,

In this PR I attempt to fix this issue using a combination of ideas
discussed in the issue mentioned above, I've created this early draft so
people can pitch in if there is something I should consider doing.

The first goal of this PR is to resolve the issue with the possible
illegal references, As a result of my approach it would also end up with
a bunch of walk_* and walk_*_mut functions to help with the abstraction.
I want to eliminate enter_node and leave_node functions, but I still
haven't started working on it since I first want to familiarize myself
with all of its usage throughout the project. I'm hesitating to do it at
the moment, When we want to do this it would require quite a bit of
refactoring so we should make sure it is probably going to work and end
up being a better implementation.
Brooooooklyn pushed a commit to toeverything/AFFiNE that referenced this pull request Apr 11, 2024
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [oxlint](https://oxc-project.github.io) ([source](https://togithub.com/oxc-project/oxc/tree/HEAD/npm/oxlint)) | [`0.2.14` -> `0.2.17`](https://renovatebot.com/diffs/npm/oxlint/0.2.14/0.2.17) | [![age](https://developer.mend.io/api/mc/badges/age/npm/oxlint/0.2.17?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/oxlint/0.2.17?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/oxlint/0.2.14/0.2.17?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/oxlint/0.2.14/0.2.17?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>oxc-project/oxc (oxlint)</summary>

### [`v0.2.17`](https://togithub.com/oxc-project/oxc/releases/tag/oxlint_v0.2.17): oxlint v0.2.17

[Compare Source](https://togithub.com/oxc-project/oxc/compare/7066d55153ad70f95ae975adc3958c1010f9c5ff...df11d10a2220e9aa7a33d9ab39ed662c2ba6fdb5)

##### What's Changed

-   feat(linter): eslint-plugin-jest/prefer-lowercase-title by [@&#8203;eryue0220](https://togithub.com/eryue0220) in [oxc-project/oxc#2911
-   feat(linter): typescript-eslint/consistent-type-definitions by [@&#8203;todor-a](https://togithub.com/todor-a) in [oxc-project/oxc#2885
-   fix(cli): fix `oxlint --format json` yields 0 files to lint by [@&#8203;Boshen](https://togithub.com/Boshen) in [oxc-project/oxc#2940
-   fix(cli): if format is json do not print summary information ([#&#8203;2899](https://togithub.com/oxc-project/oxc/issues/2899)) by [@&#8203;kalvenschraut](https://togithub.com/kalvenschraut) in [oxc-project/oxc#2925
-   fix(linter): import/no-cycle ignore type-only imports by [@&#8203;JohnDaly](https://togithub.com/JohnDaly) in [oxc-project/oxc#2924
-   refactor(semantic/jsdoc): Rework JSDoc struct for better Span handling by [@&#8203;leaysgur](https://togithub.com/leaysgur) in [oxc-project/oxc#2917

##### New Contributors

-   [@&#8203;bradzacher](https://togithub.com/bradzacher) made their first contribution in [oxc-project/oxc#2938

**Full Changelog**: oxc-project/oxc@oxlint_v0.2.16...oxlint_v0.2.17

### [`v0.2.16`](https://togithub.com/oxc-project/oxc/releases/tag/oxlint_v0.2.16): oxlint v0.2.16

[Compare Source](https://togithub.com/oxc-project/oxc/compare/e7307ed23ca9b0707586b6bf4220cafb221ae86e...7066d55153ad70f95ae975adc3958c1010f9c5ff)

#### What's Changed

-   feat(linter): [@&#8203;typescript-eslint/prefer-for-of](https://togithub.com/typescript-eslint/prefer-for-of) by [@&#8203;charnog](https://togithub.com/charnog) in [oxc-project/oxc#2789
-   feat(linter): Implement jsdoc/check-access by [@&#8203;leaysgur](https://togithub.com/leaysgur) in [oxc-project/oxc#2642
-   feat(linter): Implement jsdoc/empty-tags by [@&#8203;leaysgur](https://togithub.com/leaysgur) in [oxc-project/oxc#2893
-   feat(linter): eslint-plugin-jest/prefer-mock-promise-sorthand by [@&#8203;eryue0220](https://togithub.com/eryue0220) in [oxc-project/oxc#2864
-   feat(linter/import): Add `ignoreTypes` option for the `import/no-cycle` rule by [@&#8203;JohnDaly](https://togithub.com/JohnDaly) in [oxc-project/oxc#2905
-   fix(ast): `FinallyClause` won't get visited as `BlockStatement` anymore. by [@&#8203;rzvxa](https://togithub.com/rzvxa) in [oxc-project/oxc#2881
-   fix(linter): handle self closing script tags in astro partial loader ([#&#8203;2017](https://togithub.com/oxc-project/oxc/issues/2017)) by [@&#8203;kalvenschraut](https://togithub.com/kalvenschraut) in [oxc-project/oxc#2907
-   fix(linter): svelte partial loader handle generics ([#&#8203;2875](https://togithub.com/oxc-project/oxc/issues/2875)) by [@&#8203;kalvenschraut](https://togithub.com/kalvenschraut) in [oxc-project/oxc#2906

#### New Contributors

-   [@&#8203;charnog](https://togithub.com/charnog) made their first contribution in [oxc-project/oxc#2789
-   [@&#8203;kalvenschraut](https://togithub.com/kalvenschraut) made their first contribution in [oxc-project/oxc#2906
-   [@&#8203;JohnDaly](https://togithub.com/JohnDaly) made their first contribution in [oxc-project/oxc#2905

**Full Changelog**: oxc-project/oxc@oxlint_v0.2.15...oxlint_v0.2.16

### [`v0.2.15`](https://togithub.com/oxc-project/oxc/releases/tag/oxlint_v0.2.15): oxlint v0.2.15

[Compare Source](https://togithub.com/oxc-project/oxc/compare/b1343d7bcbd490105583b561946f057ac91e40cf...e7307ed23ca9b0707586b6bf4220cafb221ae86e)

#### What's Changed

-   feat(linter): default_param_last by [@&#8203;JoSeBu1](https://togithub.com/JoSeBu1) in [oxc-project/oxc#2756
-   feat(linter): eslint-plugin-jest/no-untyped-mock-factory by [@&#8203;eryue0220](https://togithub.com/eryue0220) in [oxc-project/oxc#2807
-   feat(linter): eslint-plugin-jest/prefer-comparison-matcher by [@&#8203;eryue0220](https://togithub.com/eryue0220) in [oxc-project/oxc#2806
-   feat(linter): eslint-plugin-react checked-requires-onchange-or-readonly by [@&#8203;keita-hino](https://togithub.com/keita-hino) in [oxc-project/oxc#2754
-   feat(linter): eslint/no-iterator by [@&#8203;JoSeBu1](https://togithub.com/JoSeBu1) in [oxc-project/oxc#2758
-   feat(linter): fallback to the default tsconfig path by [@&#8203;Dunqing](https://togithub.com/Dunqing) in [oxc-project/oxc#2842
-   feat(linter): no_script_url by [@&#8203;JoSeBu1](https://togithub.com/JoSeBu1) in [oxc-project/oxc#2761
-   feat(linter/import) check deep namespace in namespace rule by [@&#8203;Dunqing](https://togithub.com/Dunqing) in [oxc-project/oxc#2805
-   feat(linter/import) check module import in no_duplicates by [@&#8203;Dunqing](https://togithub.com/Dunqing) in [oxc-project/oxc#2771
-   feat(linter/import) check type import in no_duplicates by [@&#8203;Dunqing](https://togithub.com/Dunqing) in [oxc-project/oxc#2777
-   feat(linter/import) support allow_computed option in namespace by [@&#8203;Dunqing](https://togithub.com/Dunqing) in [oxc-project/oxc#2840
-   feat(linter/import) support check re-export in named by [@&#8203;Dunqing](https://togithub.com/Dunqing) in [oxc-project/oxc#2769
-   feat(linter/import): ignore type-only imports and exports in no_unresolved by [@&#8203;Dunqing](https://togithub.com/Dunqing) in [oxc-project/oxc#2849
-   fix(linter/import): false positive for indirect export in namespace by [@&#8203;Dunqing](https://togithub.com/Dunqing) in [oxc-project/oxc#2862
-   fix(linter/import): ignore export declaration in no-duplicates by [@&#8203;Dunqing](https://togithub.com/Dunqing) in [oxc-project/oxc#2863
-   fix(linter/max-lines): only report codes that exceed the line limit by [@&#8203;mysteryven](https://togithub.com/mysteryven) in [oxc-project/oxc#2778
-   fix(parser): add support for empty module declaration by [@&#8203;rzvxa](https://togithub.com/rzvxa) in [oxc-project/oxc#2834

#### New Contributors

-   [@&#8203;rzvxa](https://togithub.com/rzvxa) made their first contribution in [oxc-project/oxc#2764

**Full Changelog**: oxc-project/oxc@oxlint_v0.2.14...oxlint_v0.2.15

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/toeverything/AFFiNE).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4xNTMuMiIsInVwZGF0ZWRJblZlciI6IjM3LjI2OS4yIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ast Area - AST A-transformer Area - Transformer / Transpiler
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Borrow checker rule violation in VisitMut trait
3 participants