Files
reverse-engineering-anything/src/application/native/NativeValueTraceBindings.ts
T
N0zoM1z0andgensokyo-dev 3dced80300 fix(native): locate recovered call inputs without parameter bindings
Group exact input indices with caller Evidence after complete parameter examination; preserve budget uncertainty and advance the trace workflow profile.

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-11 18:24:58 +08:00

128 lines
4.3 KiB
TypeScript

import type { z } from "zod";
import type { nativeValueTraceSchema } from "../../domain/native/nativeValueTrace.js";
import type { createValueTraceBudget } from "./NativeValueTraceBudget.js";
type Trace = z.output<typeof nativeValueTraceSchema>;
/** Retained caller facts; callee remains the exact selector reported by resolution. */
export interface NativeValueTraceCall {
readonly caller: string;
readonly callee: string;
readonly call: string;
readonly address: string;
readonly argument_count: number;
readonly output: boolean;
readonly evidence_id: string;
}
/** Bind retained graph rows through procedure identities established by analyzed dossiers. */
export const bindNativeValueTraceCalls = ({
budget,
calls,
procedureAddresses,
completeParameterProcedures,
unknowns,
}: {
readonly budget: ReturnType<typeof createValueTraceBudget>;
readonly calls: readonly NativeValueTraceCall[];
readonly procedureAddresses: ReadonlyMap<string, string>;
readonly completeParameterProcedures: ReadonlySet<string>;
readonly unknowns: Trace["unknowns"];
}): void => {
const parametersByProcedure = new Map<string, Trace["nodes"]>();
const returnsByProcedure = new Map<string, Trace["nodes"]>();
for (const node of budget.nodes) {
if (node.kind === "parameter") {
const parameters = parametersByProcedure.get(node.procedure) ?? [];
parameters.push(node);
parametersByProcedure.set(node.procedure, parameters);
} else if (
node.operation?.opcode === "RETURN" &&
node.operation.inputs.length > 1
) {
const returns = returnsByProcedure.get(node.procedure) ?? [];
returns.push(node);
returnsByProcedure.set(node.procedure, returns);
}
}
for (const binding of calls) {
const callee = procedureAddresses.get(binding.callee);
const parameters =
callee === undefined ? [] : (parametersByProcedure.get(callee) ?? []);
const complete =
callee !== undefined && completeParameterProcedures.has(callee);
if (parameters.length === 0 && !complete)
unknowns.push({
procedure: binding.caller,
address: null,
reason: `Parameter bindings for ${binding.callee} are unavailable or omitted`,
});
const boundInputs = new Set<number>();
for (const node of parameters) {
const ordinal = node.parameter?.ordinal;
if (ordinal === undefined || ordinal >= binding.argument_count) {
unknowns.push({
procedure: binding.caller,
address: null,
reason: `Argument ${ordinal ?? "unknown"} for ${binding.callee} was not recovered`,
});
continue;
}
if (
!budget.retainEdge(
{
source: binding.call,
target: node.id,
input_index: ordinal + 1,
kind: "argument-binding",
status: "derived",
evidence_id: binding.evidence_id,
},
binding.caller,
)
)
break;
boundInputs.add(ordinal + 1);
}
if (complete && !budget.edgesExhausted()) {
// One group of indices per call preserves positions without copying the
// already retained operands or repeating their provenance for each input.
const inputIndices: number[] = [];
for (let index = 1; index <= binding.argument_count; index++)
if (!boundInputs.has(index)) inputIndices.push(index);
if (inputIndices.length > 0)
unknowns.push({
procedure: binding.caller,
address: binding.address,
reason:
"Recovered call inputs have no recovered parameter binding; formal and variadic signature details remain unknown.",
call_inputs: {
call_node_id: binding.call,
destination: binding.callee,
evidence_id: binding.evidence_id,
input_indices: inputIndices,
},
});
}
if (binding.output)
for (const node of callee === undefined
? []
: (returnsByProcedure.get(callee) ?? [])) {
if (
!budget.retainEdge(
{
source: node.id,
target: binding.call,
input_index: 1,
kind: "return-binding",
status: "derived",
evidence_id: node.evidence_id,
},
binding.caller,
)
)
break;
}
}
};