mirror of
https://github.com/morluto/rea.git
synced 2026-11-12 01:41:21 +00:00
perf(javascript): keep one member-effect record per stored fact (#8103)
Member-mutation indexing appended a binding write or escape record for every traversal state that reached it. States that differ only in coverage fields the record never stores (origin, owner initializer, getter widening) produced identical records, and each later receiver query rescanned all of them. Record each stored fact once, alongside the existing required-module effect deduplication. Propagation is unchanged. Effect ordering also re-walked the same top-level statement ancestry for every candidate record. Cache it with the other initializer positions, and test the selected path before the costlier receiver comparison. On the #7774 reproducer the semantic IR and the complete analyze-javascript-application Evidence are byte-identical, while the CLI run drops from 146 s to 40 s. Refs #7774 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6b68fb642a
commit
a47dcca010
4 files changed
+184
-38
No files matched your search
@@ -51,6 +51,7 @@ interface ExpressionAncestry {
|
||||
*/
|
||||
interface InitializerPositionCache {
|
||||
readonly direct: WeakMap<t.Node, StatementPosition | null>;
|
||||
readonly topLevel: WeakMap<t.Node, StatementPosition | null>;
|
||||
readonly mutation: WeakMap<t.Node, StatementPosition | null>;
|
||||
readonly reference: WeakMap<t.Node, StatementPosition | null>;
|
||||
readonly read: WeakMap<t.Node, BindingReadPosition>;
|
||||
@@ -75,6 +76,7 @@ const initializerPositionCache = (
|
||||
if (cached !== undefined) return cached;
|
||||
const created: InitializerPositionCache = {
|
||||
direct: new WeakMap(),
|
||||
topLevel: new WeakMap(),
|
||||
mutation: new WeakMap(),
|
||||
reference: new WeakMap(),
|
||||
read: new WeakMap(),
|
||||
@@ -122,37 +124,57 @@ const positionPrecedes = (
|
||||
left.declaratorIndex >= 0 &&
|
||||
left.declaratorIndex < right.declaratorIndex));
|
||||
|
||||
/** Statement position of a node evaluated once by the program, outside loops and functions. */
|
||||
const computeTopLevelStatementPosition = (
|
||||
node: t.Node | undefined,
|
||||
parents: WeakMap<t.Node, t.Node>,
|
||||
): StatementPosition | null => {
|
||||
let selected: StatementPosition | null = null;
|
||||
let declarator: t.VariableDeclarator | undefined;
|
||||
let child: t.Node | undefined;
|
||||
while (node !== undefined) {
|
||||
if (
|
||||
(t.isFunction(node) &&
|
||||
(child === undefined || !semanticComputedMethodKey(node, child))) ||
|
||||
t.isLoop(node)
|
||||
)
|
||||
return null;
|
||||
if (delayedInstanceFieldValue(node, child)) return null;
|
||||
if (t.isVariableDeclarator(node)) declarator = node;
|
||||
if (t.isStatement(node)) {
|
||||
const enclosing = statementPosition(node, parents, declarator);
|
||||
if (enclosing !== null) selected = enclosing;
|
||||
}
|
||||
if (t.isProgram(node)) return selected;
|
||||
child = node;
|
||||
node = parents.get(node);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// Effect ordering asks for the same capture and effect positions once per
|
||||
// candidate record, so a growing record set would repeat each ancestry walk.
|
||||
const topLevelStatementPosition = (
|
||||
node: t.Node | undefined,
|
||||
parents: WeakMap<t.Node, t.Node>,
|
||||
): StatementPosition | null => {
|
||||
if (node === undefined) return null;
|
||||
const cache = initializerPositionCache(parents);
|
||||
const cached = cache.topLevel.get(node);
|
||||
if (cached !== undefined) return cached;
|
||||
const computed = computeTopLevelStatementPosition(node, parents);
|
||||
cache.topLevel.set(node, computed);
|
||||
return computed;
|
||||
};
|
||||
|
||||
/** Order effects only in a non-repeating top-level activation. */
|
||||
export const semanticTopLevelEffectFollowsCapture = (
|
||||
capture: t.Node,
|
||||
effect: t.Node | undefined,
|
||||
parents: WeakMap<t.Node, t.Node>,
|
||||
): boolean => {
|
||||
const position = (node: t.Node | undefined): StatementPosition | null => {
|
||||
let selected: StatementPosition | null = null;
|
||||
let declarator: t.VariableDeclarator | undefined;
|
||||
let child: t.Node | undefined;
|
||||
while (node !== undefined) {
|
||||
if (
|
||||
(t.isFunction(node) &&
|
||||
(child === undefined || !semanticComputedMethodKey(node, child))) ||
|
||||
t.isLoop(node)
|
||||
)
|
||||
return null;
|
||||
if (delayedInstanceFieldValue(node, child)) return null;
|
||||
if (t.isVariableDeclarator(node)) declarator = node;
|
||||
if (t.isStatement(node)) {
|
||||
const enclosing = statementPosition(node, parents, declarator);
|
||||
if (enclosing !== null) selected = enclosing;
|
||||
}
|
||||
if (t.isProgram(node)) return selected;
|
||||
child = node;
|
||||
node = parents.get(node);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const before = position(capture);
|
||||
const after = position(effect);
|
||||
const before = topLevelStatementPosition(capture, parents);
|
||||
const after = topLevelStatementPosition(effect, parents);
|
||||
if (before === null || after === null) return false;
|
||||
if (positionPrecedes(before, after)) return true;
|
||||
if (
|
||||
|
||||
Reference in new issue
Block a user