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

Figure out an approach to NodePath 'hub' object tracking with better cache behavior #6437

Closed
loganfsmyth opened this issue Oct 6, 2017 · 3 comments · Fixed by #15725 or #15759
Closed
Assignees
Labels
i: bug outdated A closed issue/PR that is archived due to age. Recommended to make a new issue pkg: traverse
Milestone

Comments

@loganfsmyth
Copy link
Member

I recently realized we've got bug with our invalidation logic for NodePath that can cause some real weirdness. Here it is on ASTExplorer and here:

var mem = template("property.access")();

traverse(mem, {noScope: true});

var p2 = path.insertAfter(mem)[0];

console.log(p2.get("expression").hub);

will print undefined for hub even through p2.hub is the Hub class instance.

Which is surprising because in

if (!hub && parentPath) {
, the hub value is explicitly supposed to pass down.

It fails in this case because on

if (pathCheck.node === targetNode) {
if a value is found in the cache, we never bother bailing out.

The problem is, what do we do. Two options come to mind:

  1. Overwrite the .hub on the cache result.
  2. Only accept items from the cache if the hub is correct.

Option 1

Pretty gross because we could just as easily be introducing another bug by removing a Hub instance that was supposed to be there

Option 2

Sounds reasonable, until you realize that we actually crucially rely on this gross caching behavior to instantiate NodePath with the correct Hub instance in the first place.

In File init in

we create the root NodePath, but then we never really do anything with it. Traversal begins on
traverse(file.ast, visitor, file.scope);
, which never explicitly mentions this.path at all. That path ends up being used, thus using the root Hub, because it is present in the cache. If we explicitly test for the Hub in the cache, we'll break this weird cache-based pass-through logic.

Solution?

To me, the "feature" we're breaking in option 2 is totally a mistake to rely on since it is essentially relying on sideeffects to the extreme. This means we need a way to do two things:

  1. Initialize the NodePath up front, so it can be stored on the File class instance
  2. Perform a traversal using this NodePath, while ensuring that same traversal logic we have now is preserved.

The exact details on how we do those two things is up in the air, but feels like something we should figure out ASAP.

Incidentally, this same bug is the reason we have to do

traverse.clearNode(node);
to explicitly clear the cache, otherwise every node we get pack from babel-template would trigger this bug, due to the internal traversal, which doesn't have a hub, polluting the cache.

@loganfsmyth loganfsmyth added this to the Babel 7.next milestone Oct 6, 2017
@nicolo-ribaudo
Copy link
Member

Would making hub a getter be too expensive? e.g.

  get hub() {
    return this
_hub || (this.parentPath && this.parentPath.hub);
  }

@xtuc xtuc added the PR: Internal 🏠 A type of pull request used for our changelog categories label Oct 9, 2017
@liuxingbaoyu liuxingbaoyu added pkg: traverse and removed PR: Internal 🏠 A type of pull request used for our changelog categories labels Oct 17, 2022
@liuxingbaoyu liuxingbaoyu modified the milestones: Babel 7.next, Babel 8.0 Oct 17, 2022
robhogan added a commit to robhogan/metro that referenced this issue Dec 17, 2022
…nMap`

Summary:
## Motivation
For performance reasons, we'd like to have the option to re-use an AST (without cloning) after deriving source map information from it. However, due to babel/babel#6437, traversing the AST within `generateFunctionMap` causes the `babel/traverse` path cache to be populated with entries Babel transform plugins can't use - in particular because `hub` is assumed to be set.

## Change
This diffs saves and clears the traverse cache before traversal, and then restores it afterwards. This isn't pretty but it's the cleanest approach I could find - we include tests to verify that this is (and continues to be) necessary.

## Other approaches
I tried some other things before resorting to manipulating `babel/traverse` internals:
 - Actually providing a `hub` so that it's set on the cache means adding a dependency on `babel/core`, and requires a similar level of hackery / assumptions of Babel internals.
 - Clearing the bad cache with `traverse.clearNode(ast)` or `traverse.cache.clearPath()` doesn't work because other tools (eg, Jest) rely on `properties` held in the path cache.

Changelog: [Internal]

Differential Revision: D42068914

fbshipit-source-id: f473f67f17ffe92c9fc378ef54f3dc40bcf25f7a
facebook-github-bot pushed a commit to facebook/metro that referenced this issue Dec 19, 2022
…nMap` (#906)

Summary:
Pull Request resolved: #906

## Motivation
For performance reasons, we'd like to have the option to re-use an AST (without cloning) after deriving source map information from it. However, due to babel/babel#6437, traversing the AST within `generateFunctionMap` causes the `babel/traverse` path cache to be populated with entries Babel transform plugins can't use - in particular because `hub` is assumed to be set.

## Change
This diffs saves and clears the traverse cache before traversal, and then restores it afterwards. This isn't pretty but it's the cleanest approach I could find - we include tests to verify that this is (and continues to be) necessary.

## Other approaches
I tried some other things before resorting to manipulating `babel/traverse` internals:
 - Actually providing a `hub` so that it's set on the cache means adding a dependency on `babel/core`, and requires a similar level of hackery / assumptions of Babel internals.
 - Clearing the bad cache with `traverse.clearNode(ast)` or `traverse.cache.clearPath()` doesn't work because other tools (eg, Jest) rely on `properties` held in the path cache.

Changelog: [Internal]

Reviewed By: jacdebug, motiz88

Differential Revision: D42068914

fbshipit-source-id: e928b7ee1ffc5462f0ead7bcf4077b818a4cd95a
@nicolo-ribaudo
Copy link
Member

I'm investigating this

@liuxingbaoyu
Copy link
Member

#12570
Maybe we can fix it at the same time.

@JLHwung JLHwung reopened this Jul 6, 2023
@github-actions github-actions bot added the outdated A closed issue/PR that is archived due to age. Recommended to make a new issue label Nov 2, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Nov 2, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
i: bug outdated A closed issue/PR that is archived due to age. Recommended to make a new issue pkg: traverse
Projects
None yet
5 participants