Files
rea/src/domain/native/functionStructuralCandidate.ts
T
gensokyo-dev 1417bf943b feat(native): match retained function inventories across builds (#5390)
Match retained native inventories through shared CLI/MCP workflows with conservative symbol/byte correspondence, unresolved structural candidates, complete Evidence provenance and bounded paged reads.

Closes #5343.
2026-10-20 09:48:52 +00:00

55 lines
2.0 KiB
TypeScript

import type { Evidence } from "../evidence.js";
import type { FunctionDossier } from "../hopperValues.js";
import { digestCanonicalValue } from "../canonicalDigest.js";
import {
isAutoName,
normalizeCfg,
} from "../functionComparisonNormalization.js";
import { assessFunctionCallCoverage } from "../functionCallCoverage.js";
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
/** An observed structural signature proposes candidates; it never proves identity. */
export const functionStructuralCandidate = (
evidence: Evidence,
dossier: FunctionDossier,
): string | null => {
if (
evidence.subject?.architecture == null ||
dossier.facet_availability?.cfg?.available !== true ||
dossier.facet_availability?.callees?.available !== true ||
dossier.facet_availability?.strings_names?.available !== true ||
dossier.unresolved_calls === undefined ||
assessFunctionCallCoverage(evidence.relationship_coverage, dossier).blockers
.length > 0 ||
(dossier.swift_string_literal_omissions?.length ?? 0) > 0 ||
evidence.analysis_profile === null
)
return null;
const cfg = normalizeCfg(dossier.basic_blocks);
if (
cfg === null ||
cfg.length === 0 ||
dossier.callees.some(
(callee) => isAutoName(callee.name) || callee.name.length === 0,
)
)
return null;
const calls = dossier.callees
.map((callee) => callee.name)
.sort(compareUnicodeCodePoints);
const strings = [
...dossier.referenced_strings.map((value) => value.value),
...(dossier.swift_string_literals ?? []).map((value) => value.value),
].sort(compareUnicodeCodePoints);
// Empty leaf functions otherwise produce huge uninformative CFG-only buckets.
if (calls.length === 0 && strings.length === 0) return null;
return digestCanonicalValue({
algorithm: "observed-cfg-calls-strings-v1",
profile: evidence.analysis_profile.digest,
architecture: evidence.subject.architecture,
cfg,
calls,
strings,
});
};