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

Rely on the call stack to clean up cache in _guessExecutionStatusRelativeTo #14649

Merged
merged 1 commit into from Jun 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions packages/babel-traverse/src/path/index.ts
Expand Up @@ -230,6 +230,15 @@ Object.assign(
NodePath_comments,
);

if (!process.env.BABEL_8_BREAKING) {
// The original _guessExecutionStatusRelativeToDifferentFunctions only worked for paths in
// different functions, but _guessExecutionStatusRelativeTo works as a replacement in those cases.

// @ts-ignore
NodePath.prototype._guessExecutionStatusRelativeToDifferentFunctions =
NodePath_introspection._guessExecutionStatusRelativeTo;
}

// we can not use `import { TYPES } from "@babel/types"` here
// because the transformNamedBabelTypesImportToDestructuring plugin in babel.config.js
// does not offer live bindings for `TYPES`
Expand Down
70 changes: 34 additions & 36 deletions packages/babel-traverse/src/path/introspection.ts
Expand Up @@ -322,6 +322,8 @@ function isExecutionUncertainInList(paths, maxIndex) {
// is both before and unknown/after like if it were before.
type RelativeExecutionStatus = "before" | "after" | "unknown";

type ExecutionStatusCache = Map<t.Node, Map<t.Node, RelativeExecutionStatus>>;

/**
* Given a `target` check the execution status of it relative to the current path.
*
Expand All @@ -332,29 +334,39 @@ type RelativeExecutionStatus = "before" | "after" | "unknown";
export function _guessExecutionStatusRelativeTo(
this: NodePath,
target: NodePath,
): RelativeExecutionStatus {
return _guessExecutionStatusRelativeToCached(this, target, new Map());
}

function _guessExecutionStatusRelativeToCached(
base: NodePath,
target: NodePath,
cache: ExecutionStatusCache,
): RelativeExecutionStatus {
// check if the two paths are in different functions, we can't track execution of these
const funcParent = {
this: getOuterFunction(this),
this: getOuterFunction(base),
target: getOuterFunction(target),
};

// here we check the `node` equality as sometimes we may have different paths for the
// same node due to path thrashing
if (funcParent.target.node !== funcParent.this.node) {
return this._guessExecutionStatusRelativeToDifferentFunctions(
return _guessExecutionStatusRelativeToDifferentFunctionsCached(
base,
funcParent.target,
cache,
);
}

const paths = {
target: target.getAncestry(),
this: this.getAncestry(),
this: base.getAncestry(),
};

// If this is an ancestor of the target path,
// e.g. f(g); where this is f and target is g.
if (paths.target.indexOf(this) >= 0) return "after";
if (paths.target.indexOf(base) >= 0) return "after";
if (paths.this.indexOf(target) >= 0) return "before";

// get ancestor where the branches intersect
Expand Down Expand Up @@ -416,8 +428,9 @@ export function _guessExecutionStatusRelativeTo(
const executionOrderCheckedNodes = new Set();

function _guessExecutionStatusRelativeToDifferentFunctionsInternal(
this: NodePath,
base: NodePath,
target: NodePath,
cache: ExecutionStatusCache,
): RelativeExecutionStatus {
if (
!target.isFunctionDeclaration() ||
Expand Down Expand Up @@ -456,7 +469,7 @@ function _guessExecutionStatusRelativeToDifferentFunctionsInternal(
if (executionOrderCheckedNodes.has(path.node)) continue;
executionOrderCheckedNodes.add(path.node);
try {
const status = this._guessExecutionStatusRelativeTo(path);
const status = _guessExecutionStatusRelativeToCached(base, path, cache);

if (allStatus && allStatus !== status) {
return "unknown";
Expand All @@ -471,41 +484,26 @@ function _guessExecutionStatusRelativeToDifferentFunctionsInternal(
return allStatus;
}

let executionStatusCache: Map<
NodePath["node"],
Map<NodePath["node"], RelativeExecutionStatus>
>;

export function _guessExecutionStatusRelativeToDifferentFunctions(
this: NodePath,
function _guessExecutionStatusRelativeToDifferentFunctionsCached(
base: NodePath,
target: NodePath,
cache: ExecutionStatusCache,
): RelativeExecutionStatus {
const inited = !!executionStatusCache;
if (!inited) {
executionStatusCache = new Map();
let nodeMap = cache.get(base.node);
if (!nodeMap) {
cache.set(base.node, (nodeMap = new Map()));
} else if (nodeMap.has(target.node)) {
return nodeMap.get(target.node);
}

try {
let nodeMap = executionStatusCache.get(this.node);
if (!nodeMap) {
executionStatusCache.set(this.node, (nodeMap = new Map()));
} else if (nodeMap.has(target.node)) {
return nodeMap.get(target.node);
}

const result =
_guessExecutionStatusRelativeToDifferentFunctionsInternal.call(
this,
target,
);
const result = _guessExecutionStatusRelativeToDifferentFunctionsInternal(
base,
target,
cache,
);

nodeMap.set(target.node, result);
return result;
} finally {
if (!inited) {
executionStatusCache = undefined;
}
}
nodeMap.set(target.node, result);
return result;
}

/**
Expand Down