fix(native)!: preserve unknown provider symbol addresses
Normalize empty addresses once at the symbol-inventory interpretation boundary. Keep Swift and Objective-C records with null locations, preserve the original empty value in evidence.reported_address, and retain partial coverage instead of failing the entire metadata result. Nonempty provider address syntax remains unchanged. Validation: domain and production-registration MCP regressions failed before the fix. Ten focused files passed 181 tests with 2 skipped, including SDK converted JSON Schema/catalog validation and graph consumers. npm run check and docs:check passed (5 documentation tests). Fixture provider only; no native-provider malformed-symbol or Windows runtime reproduction claimed. BREAKING CHANGE: Swift symbol address can now be null when the provider has no location. Clients consuming swift_symbols must handle an unknown address. The original provider value is retained in optional evidence.reported_address. Refs #4542
This commit is contained in:
1 parent
060587b456
commit
8116dc0651
4 files changed
+100
-8
No files matched your search
@@ -398,6 +398,10 @@ complete observation.
|
||||
coroutine tables, and inherited overrides, have explicit unsupported or
|
||||
partial coverage. Other providers retain the
|
||||
existing symbol-based inventory with its narrower coverage.
|
||||
Symbols without a reported location remain in that inventory: Swift symbol
|
||||
`address` and Objective-C `implementation_address` are null. An empty provider
|
||||
address is retained as `reported_address` in the symbol's evidence, whose
|
||||
location also remains unknown. Nonempty provider address syntax is preserved.
|
||||
|
||||
## Native desktop observation
|
||||
|
||||
|
||||
@@ -74,4 +74,43 @@ describe("ObjC/Swift metadata", () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("retains unlocated Swift and Objective-C symbols with explicit unknown addresses and source evidence", () => {
|
||||
const result = inspectNativeDispatchMetadata([
|
||||
{ name: "$s4Test7MyClassC", address: "" },
|
||||
{ name: "-[StoreController buildTapped:]", address: "" },
|
||||
{ name: "$s5Store7launchyyF", address: "ram:0x3000" },
|
||||
]);
|
||||
expect(result.swift_symbols).toMatchObject([
|
||||
{
|
||||
mangled_name: "$s4Test7MyClassC",
|
||||
address: null,
|
||||
evidence: [{ location: { address: null }, reported_address: "" }],
|
||||
},
|
||||
{ mangled_name: "$s5Store7launchyyF", address: "ram:0x3000" },
|
||||
]);
|
||||
expect(result.swift_symbols[1]?.evidence[0]).not.toHaveProperty(
|
||||
"reported_address",
|
||||
);
|
||||
expect(result.objc_dispatch_implementations).toMatchObject([
|
||||
{
|
||||
class_name: "StoreController",
|
||||
implementation_address: null,
|
||||
location: { address: null },
|
||||
evidence: [{ location: { address: null }, reported_address: "" }],
|
||||
},
|
||||
]);
|
||||
expect(result.objc_classes[0]?.methods[0]).toMatchObject({
|
||||
selector: "buildTapped:",
|
||||
address: null,
|
||||
});
|
||||
expect(result.coverage).toContainEqual(
|
||||
expect.objectContaining({
|
||||
facet: "swift_declarations",
|
||||
status: "partial",
|
||||
examined: 2,
|
||||
decoded: 0,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -124,6 +124,8 @@ export const nativeMetadataEvidenceSchema = z.strictObject({
|
||||
description: z.string().min(1),
|
||||
provider: providerSchema.optional(),
|
||||
location: nativeMetadataLocationSchema,
|
||||
/** Preserve a provider's source value when it does not establish a location. */
|
||||
reported_address: z.string().optional(),
|
||||
artifact_path: z.string().min(1).nullable().default(null),
|
||||
artifact_sha256: z
|
||||
.string()
|
||||
@@ -229,7 +231,7 @@ export const relativePointerSchema = z.strictObject({
|
||||
/** A raw Swift-mangled symbol when a provider has not decoded its metadata. */
|
||||
export const swiftSymbolSchema = z.strictObject({
|
||||
mangled_name: z.string().min(1),
|
||||
address: z.string().min(1),
|
||||
address: z.string().min(1).nullable(),
|
||||
decode: nativeMetadataDecodeSchema,
|
||||
evidence: z.array(nativeMetadataEvidenceSchema).min(1),
|
||||
});
|
||||
@@ -338,11 +340,15 @@ export const inspectNativeDispatchMetadata = (
|
||||
names: readonly { readonly address: string; readonly name: string }[],
|
||||
maxRecords = 5_000,
|
||||
): ObjcSwiftMetadata => {
|
||||
const symbolEvidence = (name: string, address: string) => [
|
||||
const symbolEvidence = (
|
||||
entry: (typeof names)[number],
|
||||
address: string | null,
|
||||
) => [
|
||||
{
|
||||
kind: "symbol" as const,
|
||||
description: `Provider symbol ${name}`,
|
||||
description: `Provider symbol ${entry.name}`,
|
||||
location: { address, file_offset: null },
|
||||
...(address === null ? { reported_address: entry.address } : {}),
|
||||
artifact_path: null,
|
||||
artifact_sha256: null,
|
||||
},
|
||||
@@ -353,6 +359,7 @@ export const inspectNativeDispatchMetadata = (
|
||||
|
||||
const examinedNames = names.slice(0, maxRecords);
|
||||
for (const entry of examinedNames) {
|
||||
const address = entry.address === "" ? null : entry.address;
|
||||
const classMatch = /(?:OBJC_CLASS_|OBJC_METACLASS_)\$_([^\s]+)/u.exec(
|
||||
entry.name,
|
||||
);
|
||||
@@ -366,10 +373,10 @@ export const inspectNativeDispatchMetadata = (
|
||||
class_name: methodMatch[2],
|
||||
selector,
|
||||
method_type: methodType,
|
||||
implementation_address: entry.address,
|
||||
location: { address: entry.address, file_offset: null },
|
||||
implementation_address: address,
|
||||
location: { address, file_offset: null },
|
||||
decode: { status: "partial", reason: "symbol_name_only" },
|
||||
evidence: symbolEvidence(entry.name, entry.address),
|
||||
evidence: symbolEvidence(entry, address),
|
||||
});
|
||||
classNames.add(methodMatch[2]);
|
||||
}
|
||||
@@ -377,9 +384,9 @@ export const inspectNativeDispatchMetadata = (
|
||||
if (isSwiftMangledSymbolName(entry.name) || entry.name.startsWith("_Tt"))
|
||||
swiftSymbols.push({
|
||||
mangled_name: entry.name,
|
||||
address: entry.address,
|
||||
address,
|
||||
decode: { status: "partial", reason: "mangled_symbol_not_demangled" },
|
||||
evidence: symbolEvidence(entry.name, entry.address),
|
||||
evidence: symbolEvidence(entry, address),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -425,3 +425,45 @@ describe("enhanced MCP tools", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("retains unlocated provider symbols through the advertised native dispatch MCP contract", async () => {
|
||||
const ordinary = fixturePort();
|
||||
const client = await connect({
|
||||
execute: (operation, parameters, options) =>
|
||||
operation === "list_names"
|
||||
? Promise.resolve(
|
||||
targetObservation([
|
||||
{ address: "", value: "$s4Test7MyClassC" },
|
||||
{ address: "", value: "-[Fixture buildTapped:]" },
|
||||
]),
|
||||
)
|
||||
: ordinary.execute(operation, parameters, options),
|
||||
});
|
||||
const response = await client.callTool({
|
||||
name: "inspect_native_dispatch_metadata",
|
||||
arguments: { max_records: 100 },
|
||||
});
|
||||
expect(response.isError).not.toBe(true);
|
||||
expect(jsonResult(response)).toMatchObject({
|
||||
provider: { id: "fixture" },
|
||||
target_sha256: "a".repeat(64),
|
||||
result: {
|
||||
swift_symbols: [
|
||||
{
|
||||
mangled_name: "$s4Test7MyClassC",
|
||||
address: null,
|
||||
evidence: [{ reported_address: "", location: { address: null } }],
|
||||
},
|
||||
],
|
||||
objc_dispatch_implementations: [
|
||||
{ implementation_address: null, evidence: [{ reported_address: "" }] },
|
||||
],
|
||||
objc_classes: [
|
||||
{
|
||||
name: "Fixture",
|
||||
methods: [{ selector: "buildTapped:", address: null }],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user