mirror of
https://github.com/morluto/rea.git
synced 2026-11-15 11:13:21 +00:00
fix(javascript): honor effective symbolic descriptor values (#7222)
Resolve ordered descriptor values at the shared JavaScript fact owner and preserve uncertain intrinsic writes instead of stale exports. Normalize ordinary method values and reuse canonical primitive, prototype, ambient identity and ordering facts across consumers. 384 selected tests, format/static/docs checks, all final CI jobs and reviews pass. Actual Node controls cover 35 bundles; one possibly repeated factory explicitly stays unknown. Shipped Obsidian selected-source complete Evidence matches base/fixed CLI and MCP, with coverage and resource measurements in the PR. Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
This commit is contained in:
1 parent
d54f5b07b4
commit
d4c511ae6e
12 files changed
+1117
-102
No files matched your search
@@ -16,12 +16,19 @@ import {
|
||||
type JavaScriptSemanticAnalysisState,
|
||||
} from "./javascriptSemanticState.js";
|
||||
import { isImportMetaUrl } from "./javascriptStaticAnalysisHelpers.js";
|
||||
import { semanticBindingAtRead } from "./javascriptSemanticMutationInitializers.js";
|
||||
import {
|
||||
semanticBindingAtRead,
|
||||
semanticEffectTiming,
|
||||
semanticDefiniteEffectPrecedesReference,
|
||||
} from "./javascriptSemanticMutationInitializers.js";
|
||||
import {
|
||||
semanticIterationSources,
|
||||
type SemanticIterationSource,
|
||||
} from "./javascriptSemanticIterationSources.js";
|
||||
import { joinSemanticPropertyPaths } from "./javascriptSemanticPropertyPaths.js";
|
||||
import {
|
||||
joinSemanticPropertyPaths,
|
||||
semanticPropertyPathKeyMatches,
|
||||
} from "./javascriptSemanticPropertyPaths.js";
|
||||
import { createSemanticReturnedReferences } from "./javascriptSemanticReturnedReferences.js";
|
||||
import {
|
||||
semanticCopiedReferencePath,
|
||||
@@ -789,6 +796,22 @@ export function* indexJavaScriptAmbientWritesSteps(
|
||||
state.ambientEscapes.retain(["global", null]);
|
||||
}
|
||||
|
||||
/** Match lexical global identity; null denotes a direct global root. */
|
||||
const globalReferenceNamespace = (
|
||||
callee: t.Node,
|
||||
state: JavaScriptSemanticAnalysisState,
|
||||
path: readonly [string, ...string[]],
|
||||
): string | null | undefined => {
|
||||
const { root, members } = electronMemberPath(callee);
|
||||
const matches = (expected: readonly string[]) =>
|
||||
members.length === expected.length &&
|
||||
members.every((key, index) => key === expected[index]);
|
||||
if (isUnshadowedGlobal(root, state, path[0]) && matches(path.slice(1)))
|
||||
return null;
|
||||
const namespace = globalObjectName(root, state);
|
||||
return namespace !== undefined && matches(path) ? namespace : undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Recognize an ambient property path, optionally through a global object, without a visible
|
||||
* source-local overwrite. This is a syntax-local identity fact, not proof that
|
||||
@@ -802,20 +825,118 @@ export const isUnmodifiedGlobalReference = (
|
||||
const path: readonly [string, ...string[]] =
|
||||
typeof name === "string" ? [name] : name;
|
||||
if (state.ambientWrites.covers(["global", ...path])) return false;
|
||||
const { root, members } = electronMemberPath(callee);
|
||||
const matches = (expected: readonly string[]) =>
|
||||
members.length === expected.length &&
|
||||
members.every((key, index) => key === expected[index]);
|
||||
if (isUnshadowedGlobal(root, state, path[0]) && matches(path.slice(1)))
|
||||
return true;
|
||||
const namespace = globalObjectName(root, state);
|
||||
const namespace = globalReferenceNamespace(callee, state, path);
|
||||
return (
|
||||
namespace !== undefined &&
|
||||
matches(path) &&
|
||||
!state.ambientWrites.covers(["global", namespace])
|
||||
(namespace === null || !state.ambientWrites.covers(["global", namespace]))
|
||||
);
|
||||
};
|
||||
|
||||
export type GlobalReferenceRead =
|
||||
| { readonly status: "unmodified" | "unrelated" | "unknown" }
|
||||
| { readonly status: "replacement"; readonly value: t.Expression };
|
||||
|
||||
/** Reuse retained write provenance and activation ordering at a selected read.
|
||||
* The reader retains only matching effects, refreshing when their owner grows.
|
||||
* Missing timing, possibly repeated activations, conditional replacements and
|
||||
* parent writes stay unknown. */
|
||||
export const createGlobalReferenceReader = (
|
||||
state: JavaScriptSemanticAnalysisState,
|
||||
path: readonly [string, ...string[]],
|
||||
): ((callee: t.Node) => GlobalReferenceRead) => {
|
||||
const selected = ["global", ...path];
|
||||
let generation = -1;
|
||||
const byNamespace = new Map<
|
||||
string | null,
|
||||
typeof state.ambientWriteEffects
|
||||
>();
|
||||
const matches = (
|
||||
effect: (typeof state.ambientWriteEffects)[number],
|
||||
target: readonly string[],
|
||||
) =>
|
||||
effect.path.length <= target.length &&
|
||||
effect.path.every((key, index) =>
|
||||
semanticPropertyPathKeyMatches(key, target[index]!),
|
||||
);
|
||||
return (callee) => {
|
||||
const namespace = globalReferenceNamespace(callee, state, path);
|
||||
if (namespace === undefined) return { status: "unrelated" };
|
||||
if (
|
||||
!state.ambientWrites.covers(selected) &&
|
||||
(namespace === null || !state.ambientWrites.covers(["global", namespace]))
|
||||
)
|
||||
return { status: "unmodified" };
|
||||
if (generation !== state.ambientWriteEffects.length) {
|
||||
generation = state.ambientWriteEffects.length;
|
||||
byNamespace.clear();
|
||||
}
|
||||
let effects = byNamespace.get(namespace);
|
||||
if (effects === undefined) {
|
||||
effects = state.ambientWriteEffects.filter(
|
||||
(effect) =>
|
||||
matches(effect, selected) ||
|
||||
(namespace !== null && matches(effect, ["global", namespace])),
|
||||
);
|
||||
byNamespace.set(namespace, effects);
|
||||
}
|
||||
if (effects.length === 0) return { status: "unknown" };
|
||||
const active = effects.filter(
|
||||
(effect) => semanticEffectTiming(callee, effect.node, state) !== "after",
|
||||
);
|
||||
if (active.length === 0) return { status: "unmodified" };
|
||||
if (
|
||||
active.some(
|
||||
(effect) =>
|
||||
effect.path.length !== selected.length ||
|
||||
!effect.path.every((key, index) => key === selected[index]),
|
||||
)
|
||||
)
|
||||
return { status: "unknown" };
|
||||
let replacement: t.AssignmentExpression | undefined;
|
||||
for (const effect of active) {
|
||||
const write = effect.node;
|
||||
const writeNamespace = t.isAssignmentExpression(write)
|
||||
? globalReferenceNamespace(write.left, state, path)
|
||||
: undefined;
|
||||
if (
|
||||
t.isAssignmentExpression(write, { operator: "=" }) &&
|
||||
writeNamespace !== undefined &&
|
||||
(writeNamespace === null ||
|
||||
!state.ambientWrites.covers(["global", writeNamespace])) &&
|
||||
semanticDefiniteEffectPrecedesReference(
|
||||
write,
|
||||
callee,
|
||||
state.parentsByNode,
|
||||
) &&
|
||||
(replacement === undefined ||
|
||||
semanticDefiniteEffectPrecedesReference(
|
||||
replacement,
|
||||
write,
|
||||
state.parentsByNode,
|
||||
))
|
||||
)
|
||||
replacement = write;
|
||||
}
|
||||
if (replacement === undefined) return { status: "unknown" };
|
||||
const confirmed = replacement;
|
||||
if (
|
||||
active.some(
|
||||
(effect) =>
|
||||
effect.node !== confirmed &&
|
||||
semanticEffectTiming(confirmed, effect.node, state) !== "before" &&
|
||||
(effect.node === undefined ||
|
||||
!semanticDefiniteEffectPrecedesReference(
|
||||
effect.node,
|
||||
confirmed,
|
||||
state.parentsByNode,
|
||||
)),
|
||||
)
|
||||
)
|
||||
return { status: "unknown" };
|
||||
return { status: "replacement", value: confirmed.right };
|
||||
};
|
||||
};
|
||||
|
||||
/** A module URL base is known only while its property has no visible write. */
|
||||
export const isUnmodifiedImportMetaUrl = (
|
||||
node: t.Node | undefined,
|
||||
|
||||
Reference in new issue
Block a user