mirror of
https://github.com/morluto/rea.git
synced 2026-11-14 18:52:48 +00:00
fix(decompile): keep the provider's null-pseudocode reason on inline failures
decompile_all inline failure rows (failures and last_failures) carried only address, code, message and name, so the provider's typed pseudocode_absence reason stayed in the index only. Rows now include pseudocode_absence. When the inline budget is short the name is dropped first, then the reason, before the row; the index always keeps both. Fixes #8731 The report and an earlier manifest-note approach are from #8735 by wunianze666-netizen. Co-Authored-By: wunianze666-netizen <wunianze666@gmail.com> Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
This commit is contained in:
1 parent
9d2a57fd8d
commit
c9f33aaa82
3 files changed
+82
-4
No files matched your search
@@ -599,6 +599,10 @@ describe("decompile_all external functions", () => {
|
||||
expect(resumed.error.userMessage).toContain("not a compatible export");
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe("decompile_all pseudocode absence", () => {
|
||||
const directory = useOutputDirectory();
|
||||
|
||||
it("reports a null pseudocode result as no pseudocode, not as text of the wrong type", async () => {
|
||||
const { tools } = harness({ fail: { "0x2000": "null" } });
|
||||
@@ -611,6 +615,61 @@ describe("decompile_all external functions", () => {
|
||||
failures: [{ address: "0x2000", code: "no_pseudocode" }],
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
["fits", "Flow exceeded maximum instructions"],
|
||||
["exceeds the inline budget", "x".repeat(4_096)],
|
||||
] as const)(
|
||||
"keeps the provider's null-pseudocode reason on the inline row while it %s",
|
||||
async (_case, reason) => {
|
||||
const { port } = harness();
|
||||
const absence = { kind: "provider-failure", reason } as const;
|
||||
const reporting: AnalysisOperationPort = {
|
||||
execute: (operation, parameters, options) =>
|
||||
operation === "procedure_pseudo_code" &&
|
||||
parameters.procedure === "0x2000"
|
||||
? Promise.resolve(
|
||||
ok(
|
||||
createAnalysisExecution(null, provider, {
|
||||
analysisProfile: profile,
|
||||
pseudocodeAbsence: absence,
|
||||
}),
|
||||
),
|
||||
)
|
||||
: port.execute(operation, parameters, options),
|
||||
};
|
||||
const result = await run(new EnhancedTools(reporting), directory());
|
||||
if (!result.ok) throw result.error;
|
||||
const [failure] = (
|
||||
result.value as { failures: Record<string, unknown>[] }
|
||||
).failures;
|
||||
expect(failure).toMatchObject({
|
||||
address: "0x2000",
|
||||
code: "no_pseudocode",
|
||||
});
|
||||
// Name goes first, then the reason; the index always keeps both.
|
||||
if (reason.length < 100)
|
||||
expect(failure).toMatchObject({
|
||||
name: "Foo::helper",
|
||||
pseudocode_absence: absence,
|
||||
});
|
||||
else {
|
||||
expect(failure).not.toHaveProperty("name");
|
||||
expect(failure).not.toHaveProperty("pseudocode_absence");
|
||||
}
|
||||
const index = decompileDirectoryIndexSchema.parse(
|
||||
JSON.parse(
|
||||
await readFile(
|
||||
join(directory(), DECOMPILE_DIRECTORY_INDEX_NAME),
|
||||
"utf8",
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(
|
||||
index.functions.find(({ address }) => address === "0x2000"),
|
||||
).toMatchObject({ pseudocode_absence: absence });
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe("decompile_all resume", () => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ExportPublishedCleanupFailure } from "./exportResources.js";
|
||||
import { join } from "node:path";
|
||||
import { z } from "zod";
|
||||
import type { PseudocodeAbsence } from "../../domain/native/pseudocodeAbsence.js";
|
||||
|
||||
import { ArtifactReaderFailure } from "../../artifacts/ArtifactReader.js";
|
||||
|
||||
@@ -647,7 +648,12 @@ const publishOne = async (input: {
|
||||
"The provider reported a function decompilation failure. If it supports independent decompilation, resume this export with retry_failed: independent_fresh (CLI: --resume --retry-failed independent_fresh). The retry may use different analysis context.",
|
||||
}
|
||||
: pseudocodeFailure(pseudocode);
|
||||
recordFailure(input.state, input.procedure, reason);
|
||||
recordFailure(
|
||||
input.state,
|
||||
input.procedure,
|
||||
reason,
|
||||
decompiled.value.pseudocodeAbsence,
|
||||
);
|
||||
return {
|
||||
result: await input.session.append(
|
||||
failureRecord(input.procedure, body.fact, reason, {
|
||||
@@ -951,14 +957,18 @@ const recordFailure = (
|
||||
state: PublishState,
|
||||
procedure: SelectedProcedure,
|
||||
reason: FailureReason,
|
||||
pseudocodeAbsence?: PseudocodeAbsence,
|
||||
): void => {
|
||||
state.failed += 1;
|
||||
state.lastFailureCode = reason.code;
|
||||
const failure = {
|
||||
const failure: NamedFailure = {
|
||||
address: procedure.address,
|
||||
name: procedure.name,
|
||||
code: reason.code,
|
||||
message: reason.message,
|
||||
...(pseudocodeAbsence === undefined
|
||||
? {}
|
||||
: { pseudocode_absence: pseudocodeAbsence }),
|
||||
};
|
||||
if (state.failures.length < DECOMPILE_DIRECTORY_INLINE_FAILURE_LIMIT) {
|
||||
const comma = state.failures.length === 0 ? 0 : 1;
|
||||
@@ -999,8 +1009,12 @@ const fitInlineFailure = (
|
||||
// Name is display context; address remains the complete lookup identity.
|
||||
const { name: _name, ...withoutName } = failure;
|
||||
const compactBytes = jsonValueByteLength(withoutName, budget);
|
||||
return compactBytes <= budget
|
||||
? { row: withoutName, bytes: compactBytes }
|
||||
if (compactBytes <= budget) return { row: withoutName, bytes: compactBytes };
|
||||
// The provider's reason is kept in the index record, so it goes next.
|
||||
const { pseudocode_absence: _absence, ...minimal } = withoutName;
|
||||
const minimalBytes = jsonValueByteLength(minimal, budget);
|
||||
return minimalBytes <= budget
|
||||
? { row: minimal, bytes: minimalBytes }
|
||||
: undefined;
|
||||
};
|
||||
|
||||
|
||||
@@ -62,6 +62,11 @@ const failureFields = {
|
||||
address: z.string().min(1),
|
||||
code: z.string().min(1),
|
||||
message: z.string().min(1),
|
||||
pseudocode_absence: pseudocodeAbsenceSchema
|
||||
.exactOptional()
|
||||
.describe(
|
||||
"The provider's reported reason for null pseudocode; omitted after the name to fit the inline text budget, and always kept in the index.",
|
||||
),
|
||||
};
|
||||
|
||||
const failureSchema = z.union([
|
||||
|
||||
Reference in new issue
Block a user