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.
55 lines
2.0 KiB
TypeScript
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,
|
|
});
|
|
};
|