fix(native): share Swift mangling recognition across metadata

Use one upstream Swift mangling prefix owner for demangler status and metadata projection/counts. Keep visible legacy class names and nominal-category decoding semantics distinct.

Validation: metadata prefix, negative symbol and partial decode regressions pass; existing Mach-O controls pass.

Based on #3157.

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
This commit is contained in:
morluto
2026-10-14 13:58:14 +08:00
parent a9af4718ad
commit 17e3cf9802
4 changed files with 61 additions and 18 deletions
@@ -38,4 +38,40 @@ describe("ObjC/Swift metadata", () => {
}),
);
});
it("projects every accepted Swift mangling prefix and counts it", () => {
const names = [
"$s5Store7launchyyF",
"_$s5Store7launchyyF",
"$S5Store7launchyyF",
"_$S5Store7launchyyF",
"_T05Store7launchyyF",
"@__swiftmacro_5Store7launchfMf_",
"$e5Store7launchyyF",
"_$e5Store7launchyyF",
"_TtC5Store11StoreHandle",
"_Tprintf",
];
const result = inspectNativeDispatchMetadata(
names.map((name, index) => ({
address: `ram:0x${(0x1000 + index * 0x100).toString(16)}`,
name,
})),
names.length,
);
expect(result.swift_symbols.map((entry) => entry.mangled_name)).toEqual(
names.slice(0, -1),
);
expect(
result.swift_symbols.every((entry) => entry.decode.status === "partial"),
).toBe(true);
expect(result.coverage).toContainEqual(
expect.objectContaining({
facet: "swift_declarations",
examined: 9,
decoded: 0,
}),
);
});
});
+2 -1
View File
@@ -1,5 +1,6 @@
import { z } from "zod";
import { providerSchema } from "../evidence.js";
import { isSwiftMangledSymbolName } from "../swiftSymbolClassification.js";
import {
compareUnicodeCodePoints,
@@ -368,7 +369,7 @@ export const inspectNativeDispatchMetadata = (
classNames.add(methodMatch[2]);
}
if (/^(?:_?\$s|_Tt)/u.test(entry.name))
if (isSwiftMangledSymbolName(entry.name) || entry.name.startsWith("_Tt"))
swiftSymbols.push({
mangled_name: entry.name,
address: entry.address,
+20
View File
@@ -55,6 +55,26 @@ export const classifySwiftSymbol = (
return { category };
};
/**
* Prefixes accepted by Swift's demangler as manglings. This is deliberately
* narrower than visible Swift runtime class names (`_Tt…`): those names are
* useful metadata symbols but are not themselves demangler manglings.
*/
const SWIFT_MANGLING_PREFIXES = [
"_T0",
"$S",
"_$S",
"$s",
"_$s",
"@__swiftmacro_",
"$e",
"_$e",
] as const;
/** Recognize names the Swift demangler accepts as mangled symbols. */
export const isSwiftMangledSymbolName = (name: string): boolean =>
SWIFT_MANGLING_PREFIXES.some((prefix) => name.startsWith(prefix));
const identifierEnd = (
mangling: string,
offset: number,
+3 -17
View File
@@ -1,5 +1,7 @@
import { z } from "zod";
import { isSwiftMangledSymbolName } from "../../domain/swiftSymbolClassification.js";
const resultSchema = z.object({
input: z.string(),
output: z.string(),
@@ -8,22 +10,6 @@ const resultSchema = z.object({
export type DemangledSymbol = z.infer<typeof resultSchema>;
/**
* Prefixes the Swift demangler accepts as mangled names (Swift 4, 4.x, 5+,
* macro file names and Embedded Swift). Legacy `_T` is omitted because it is
* also an ordinary C symbol spelling such as `_Tprintf`.
*/
const SWIFT_MANGLING_PREFIXES = [
"_T0",
"$S",
"_$S",
"$s",
"_$s",
"@__swiftmacro_",
"$e",
"_$e",
] as const;
/**
* Preserve input ordering while parsing `swift-demangle --compact` lines.
* The demangler ends each record with LF, so a carriage return belongs to the
@@ -49,7 +35,7 @@ export const parseDemangledSymbols = (
output: value,
status:
value === input
? SWIFT_MANGLING_PREFIXES.some((prefix) => input.startsWith(prefix))
? isSwiftMangledSymbolName(input)
? "invalid"
: "unchanged"
: "demangled",