fix(domain): preserve derived byte counts and uncertain identity

This commit is contained in:
morluto committed 2026-10-13 13:12:06 +08:00
1 parent 8b181716ac
commit 7a2cd1ea0a
4 files changed
+59 -13

No files matched your search

@@ -22,6 +22,26 @@ const exampleMethod = () => {
};
describe("managed decompiler reconstruction import", () => {
it("reports UTF-8 byte totals while preserving UTF-16 text lengths", () => {
const text = 'return "é"; // 😀';
const method = exampleMethod();
const result = importManagedReconstructionEvidence({
...MANAGED_RECONSTRUCTION_IMPORT_EXAMPLE,
methods: [
{
...method,
reconstruction: { ...method.reconstruction, text },
},
],
});
if (!result.ok) throw result.error;
expect(result.value.normalized_result).toMatchObject({
summary: { total_text_bytes: 20 },
methods: [{ reconstruction: { text, text_length: 17 } }],
});
});
it("rejects reconstruction text hash drift", () => {
expect(() =>
importManagedReconstruction({
+28
View File
@@ -119,6 +119,34 @@ const observe = (
);
describe("function comparison normalized identity", () => {
it("keeps differing generated names ambiguous and stable names mismatched", () => {
const named = (base: "0x1000" | "0x2000", name: string) =>
functionDossierSchema.parse({
...dossier("return 0;", base),
procedure: {
...procedure(base, name),
signature: "int helper(void)",
locals: [],
},
});
const generated = compareFunctions(
observe("1", named("0x1000", "sub_1000")),
observe("2", named("0x2000", "sub_2000")),
);
expect(generated.function_match.status).toBe("ambiguous");
expect(generated.status).toBe("unknown");
expect(
generated.dimensions.find(({ dimension }) => dimension === "identity"),
).toMatchObject({ status: "unknown" });
const stable = compareFunctions(
observe("3", named("0x1000", "firstFunction")),
observe("4", named("0x2000", "secondFunction")),
);
expect(stable.function_match.status).toBe("mismatched");
expect(stable.status).toBe("changed");
});
it.each(["sub_deallocate", "sub_deadbeef_handler", "fcn.dispatch"])(
"recognizes the explicit symbol %s without a generated-name prefix false positive",
(name) => {
+9 -12
View File
@@ -16,24 +16,21 @@ export const functionMatch = (
left: FunctionSnapshot,
right: FunctionSnapshot,
): FunctionComparisonResult["function_match"] => {
if (
!isAutoName(left.dossier.procedure.name) &&
left.dossier.procedure.name === right.dossier.procedure.name
)
const leftName = left.dossier.procedure.name;
const rightName = right.dossier.procedure.name;
const stableNames = !isAutoName(leftName) && !isAutoName(rightName);
if (stableNames && leftName === rightName)
return {
status: "matched",
method: "symbol",
left_name: left.dossier.procedure.name,
right_name: right.dossier.procedure.name,
left_name: leftName,
right_name: rightName,
};
return {
status:
left.dossier.procedure.name === right.dossier.procedure.name
? "ambiguous"
: "mismatched",
status: stableNames ? "mismatched" : "ambiguous",
method: "explicit",
left_name: left.dossier.procedure.name,
right_name: right.dossier.procedure.name,
left_name: leftName,
right_name: rightName,
};
};
+2 -1
View File
@@ -204,7 +204,8 @@ const buildReconstructionSummary = (
({ reconstruction }) => reconstruction.kind === "semantic-pseudocode",
).length,
total_text_bytes: methods.reduce(
(total, { reconstruction }) => total + reconstruction.text_length,
(total, { reconstruction }) =>
total + Buffer.byteLength(reconstruction.text, "utf8"),
0,
),
});