fix(native): accept declared facts in dispatch name inventory (#2906)
This commit is contained in:
1 parent
501c5d45fe
commit
0c9ee56c2f
3 files changed
+145
-15
No files matched your search
@@ -23,6 +23,20 @@ import type { Result } from "../../domain/result.js";
|
||||
|
||||
const target = "a".repeat(64);
|
||||
const provider = { id: "fixture", name: "Fixture", version: "1" };
|
||||
const methodName = "-[BuildViewController buildTapped:]";
|
||||
const methodSymbol = {
|
||||
primary: true,
|
||||
dynamic: false,
|
||||
external: false,
|
||||
type: "function",
|
||||
source: "user_defined",
|
||||
};
|
||||
const methodInventoryEntry = {
|
||||
address: "0x1000",
|
||||
value: methodName,
|
||||
symbol: methodSymbol,
|
||||
};
|
||||
|
||||
const evidence = [
|
||||
{
|
||||
kind: "interface_builder_resource" as const,
|
||||
@@ -258,8 +272,12 @@ describe("native UI action trace", () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
it("builds the evidence path from the active app and native provider", async () => {
|
||||
const tools = new EnhancedTools(testAnalysis());
|
||||
it("builds the evidence path from provider names with declared symbol facts", async () => {
|
||||
const tools = new EnhancedTools(
|
||||
testAnalysis({
|
||||
list_names: () => execution("list_names", [methodInventoryEntry]),
|
||||
}),
|
||||
);
|
||||
const result = await tools.execute("trace_native_ui_action", {
|
||||
action: "buildTapped:",
|
||||
});
|
||||
@@ -392,18 +410,117 @@ describe("bounded native UI tracing", () => {
|
||||
error: { _tag: "AnalysisInputError" },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("binds dispatch metadata to the active target digest", async () => {
|
||||
const result = await new EnhancedTools(testAnalysis()).execute(
|
||||
"inspect_native_dispatch_metadata",
|
||||
{ max_records: 10 },
|
||||
);
|
||||
describe("native dispatch inventory", () => {
|
||||
it.each([
|
||||
{ label: "plain value", entry: { address: "0x1000", value: methodName } },
|
||||
{ label: "value with symbol facts", entry: methodInventoryEntry },
|
||||
{
|
||||
label: "legacy name with symbol facts",
|
||||
entry: { address: "0x1000", name: methodName, symbol: methodSymbol },
|
||||
},
|
||||
])(
|
||||
"binds $label inventory to the target without overstating dispatch coverage",
|
||||
async ({ entry }) => {
|
||||
const result = await new EnhancedTools(
|
||||
testAnalysis({
|
||||
list_names: () => execution("list_names", [entry]),
|
||||
}),
|
||||
).execute("inspect_native_dispatch_metadata", { max_records: 10 });
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: true,
|
||||
value: { target_sha256: target },
|
||||
});
|
||||
});
|
||||
expect(result).toMatchObject({
|
||||
ok: true,
|
||||
value: {
|
||||
target_sha256: target,
|
||||
provider,
|
||||
result: {
|
||||
objc_dispatch_implementations: [
|
||||
{
|
||||
class_name: "BuildViewController",
|
||||
selector: "buildTapped:",
|
||||
implementation_address: "0x1000",
|
||||
decode: { status: "partial", reason: "symbol_name_only" },
|
||||
},
|
||||
],
|
||||
coverage: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
facet: "objc_dispatch_implementations",
|
||||
status: "partial",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
facet: "swift_dispatch_tables",
|
||||
status: "unsupported",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
facet: "relative_pointers",
|
||||
status: "unsupported",
|
||||
}),
|
||||
]),
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it.each([
|
||||
{
|
||||
label: "malformed symbol flag",
|
||||
entry: {
|
||||
...methodInventoryEntry,
|
||||
symbol: { ...methodSymbol, primary: "true" },
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "unknown symbol source",
|
||||
entry: {
|
||||
...methodInventoryEntry,
|
||||
symbol: { ...methodSymbol, source: "unknown" },
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "incomplete symbol",
|
||||
entry: { ...methodInventoryEntry, symbol: { primary: true } },
|
||||
},
|
||||
{
|
||||
label: "malformed legacy symbol",
|
||||
entry: { address: "0x1000", name: methodName, symbol: null },
|
||||
},
|
||||
{
|
||||
label: "malformed address",
|
||||
entry: { ...methodInventoryEntry, address: 4096 },
|
||||
},
|
||||
{
|
||||
label: "malformed value with a legacy name",
|
||||
entry: { ...methodInventoryEntry, value: 42, name: methodName },
|
||||
},
|
||||
{
|
||||
label: "undeclared field",
|
||||
entry: { address: "0x1000", value: methodName, unexpected: true },
|
||||
},
|
||||
])(
|
||||
"rejects $label inventory in both dispatch consumers",
|
||||
async ({ entry }) => {
|
||||
const tools = new EnhancedTools(
|
||||
testAnalysis({
|
||||
list_names: () => execution("list_names", [entry]),
|
||||
}),
|
||||
);
|
||||
for (const [operation, parameters] of [
|
||||
["inspect_native_dispatch_metadata", { max_records: 10 }],
|
||||
["trace_native_ui_action", { action: "buildTapped:" }],
|
||||
] as const) {
|
||||
expect(await tools.execute(operation, parameters)).toMatchObject({
|
||||
ok: false,
|
||||
error: {
|
||||
_tag: "AnalysisOutputError",
|
||||
operation: "list_names",
|
||||
reason: "provider returned an invalid inventory",
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe("native UI query outcomes", () => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { z } from "zod";
|
||||
import { addressedValue } from "../../contracts/toolOutputSchemaPrimitives.js";
|
||||
import {
|
||||
nativeDispatchMetadataResultSchema,
|
||||
inspectNativeDispatchMetadata,
|
||||
@@ -36,8 +37,11 @@ export const inspectNativeDispatch = async (
|
||||
const names = z
|
||||
.array(
|
||||
z.union([
|
||||
z.strictObject({ address: z.string(), value: z.string() }),
|
||||
z.strictObject({ address: z.string(), name: z.string() }),
|
||||
addressedValue.strict(),
|
||||
addressedValue
|
||||
.omit({ value: true })
|
||||
.extend({ name: z.string() })
|
||||
.strict(),
|
||||
]),
|
||||
)
|
||||
.safeParse(execution.value.result);
|
||||
|
||||
@@ -104,7 +104,16 @@ const fixturePort = (): AnalysisOperationPort => ({
|
||||
"0x11": "_OBJC_CLASS_$_Fixture",
|
||||
"0x12": "_OBJC_PROTOCOL_$_FixtureDelegate",
|
||||
"0x13": "entry",
|
||||
}),
|
||||
}).map((entry) => ({
|
||||
...entry,
|
||||
symbol: {
|
||||
primary: true,
|
||||
dynamic: false,
|
||||
external: false,
|
||||
type: "label",
|
||||
source: "analysis",
|
||||
},
|
||||
})),
|
||||
),
|
||||
);
|
||||
case "decode_interface_builder":
|
||||
|
||||
Reference in new issue
Block a user