Files
rea/src/domain/javascriptSemanticProvenance.ts
T

67 lines
2.5 KiB
TypeScript

import type {
JavaScriptBindingProvenance,
JavaScriptModuleOrigin,
JavaScriptSemanticPrimitive,
} from "./javascriptSemanticIr.js";
import type { JavaScriptSemanticAnalysisState } from "./javascriptSemanticState.js";
import { compareCodePoints } from "./javascriptStaticAnalysisHelpers.js";
/** Construct one immutable semantic provenance value. */
export const semanticProvenance = (
status: JavaScriptBindingProvenance["status"],
origins: readonly JavaScriptModuleOrigin[],
reason: string | null,
): JavaScriptBindingProvenance => ({ status, origins, reason });
/** Normalize bounded exact module origins into one provenance classification. */
export const semanticOriginsProvenance = (
origins: readonly JavaScriptModuleOrigin[],
state: JavaScriptSemanticAnalysisState,
): JavaScriptBindingProvenance => {
const unique = uniqueSemanticOrigins(origins);
if (unique.length > state.limits.maxUnionValues)
return semanticLimitProvenance(state, "maxUnionValues");
return unique.length === 1
? semanticProvenance("module", unique, null)
: semanticProvenance("ambiguous", unique, "Multiple module origins.");
};
/** Deduplicate and canonically order module origins. */
export const uniqueSemanticOrigins = (
origins: readonly JavaScriptModuleOrigin[],
): JavaScriptModuleOrigin[] =>
[
...new Map(
origins.map((origin) => [semanticOriginKey(origin), origin]),
).values(),
].sort((left, right) =>
compareCodePoints(semanticOriginKey(left), semanticOriginKey(right)),
);
/** Mark and return an explicit provenance-limit value. */
export const semanticLimitProvenance = (
state: JavaScriptSemanticAnalysisState,
limit: "maxValueDepth" | "maxUnionValues",
): JavaScriptBindingProvenance => {
reachSemanticValueLimit(state, limit);
return semanticProvenance("limit-reached", [], `${limit} reached.`);
};
/** Record one value-lattice limit against the shared semantic analysis state. */
export const reachSemanticValueLimit = (
state: JavaScriptSemanticAnalysisState,
limit: keyof JavaScriptSemanticAnalysisState["limits"],
): void => {
state.limitsReached.add(limit);
state.omittedCount += 1;
};
const semanticOriginKey = (origin: JavaScriptModuleOrigin): string =>
`${origin.specifier}\0${origin.importedPath.join("\0")}`;
/** Canonically distinguish primitive values across type boundaries. */
export const semanticPrimitiveKey = (
value: JavaScriptSemanticPrimitive,
): string =>
`${value === null ? "null" : typeof value}:${JSON.stringify(value)}`;