Files
reverse-engineering-anything/src/domain/symbolAnalysis.test.ts
T
Kaoru0822andClaude Opus 5.5 ffe4441522 fix(swift): match legacy runtime names by format, not a _Tt substring
analyze_swift_types treated any symbol containing `_Tt` as a Swift
observation, and get_objc_protocols treated any symbol containing `_TtP` as a
protocol, so Objective-C method labels and message-send shims for selectors
such as `_TtReset` or `_TtPing` were reported as Swift names or protocols.
Recognize an embedded legacy runtime name only when `_Tt` is followed by
nominal kind markers and an identifier length or the `s` module, owned by
the Swift classifier and reused by protocol discovery.

Fixes #5576
Fixes #5580

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 15:14:02 +00:00

206 lines
5.9 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
categorizeSwiftTypes,
discoverObjcProtocols,
} from "./symbolAnalysis.js";
describe("modern Swift category evidence", () => {
it.each([
["$s4main7ServiceCMa", "classes"],
["$s4main5StateOMa", "enums"],
["$s4main7ScoringPMp", "protocols"],
["$s4main5OuterV5InnerCMa", "classes"],
["$s4main4PairV5otherE7doubledSiyF", "extensions"],
["$S4main4PairVMa", "structs"],
["_T04main4PairVMa", "structs"],
["$s4_TtX4PairVMa", "structs"],
] as const)("decodes nominal contexts for %s", (name, category) => {
expect(
categorizeSwiftTypes([{ address: "0x1", name }], { category }),
).toMatchObject({
total: 1,
categories: { [category]: { count: 1 } },
unclassified: [],
});
});
it("retains unresolved Swift observations under category filters", () => {
const names = [
"$s4main13swiftIndirectySiAA7Scoring_p_SitF",
"$sSi4mainE7doubledSiyF",
"$s9999999999999999999999BadVMa",
"$s4main04PairVMa",
];
const result = categorizeSwiftTypes(
names.map((name, index) => ({ address: String(index), name })),
{ category: "structs" },
);
expect(result).toMatchObject({
total: 0,
unclassified: names.map((name) => ({
name,
mangled_names: [name],
reason: "category_not_decoded",
})),
limitations: expect.arrayContaining([
expect.stringContaining("could not be established"),
]),
});
expect(
categorizeSwiftTypes([{ address: "0x1", name: names[0] ?? "" }], {
category: "structs",
pattern: "missing",
}),
).toMatchObject({ total: 0, unclassified: [] });
});
it("deduplicates display names after selecting a category", () => {
expect(
categorizeSwiftTypes(
[
{ address: "0x1", name: "shared" },
{ address: "0x2", name: "shared" },
],
{ category: "structs" },
[
{ address: "0x1", name: "$s4main7ServiceCMa" },
{ address: "0x2", name: "$s4main4PairVMa" },
],
),
).toMatchObject({
total: 1,
categories: { structs: { items: [{ address: "0x2", name: "shared" }] } },
});
});
});
describe("Swift alias ambiguity", () => {
it("reports conflicting aliases rather than choosing a category", () => {
expect(
categorizeSwiftTypes(
[{ address: "0x1", name: "shared" }],
{ category: "structs" },
[
{ address: "0x1", name: "$s4main4PairVMa" },
{ address: "0x1", name: "$s4main7ServiceCMa" },
],
),
).toMatchObject({
total: 0,
unclassified: [
{
name: "shared",
reason: "conflicting_categories",
mangled_names: ["$s4main4PairVMa", "$s4main7ServiceCMa"],
},
],
});
});
it("retains manglings whose prefix the demangler accepts", () => {
const names = [
"$e4main7ServiceCMa",
"_$e4main7ServiceCMa",
"@__swiftmacro_4main7ServiceCMa",
];
const result = categorizeSwiftTypes(
names.map((name, index) => ({ address: `0x${String(index)}`, name })),
);
expect(result.categories.other?.items.map((item) => item.name)).toEqual(
names,
);
expect(result.unclassified.map((item) => item.name)).toEqual(names);
});
it("keeps legacy names with an optional leading underscore", () => {
expect(
categorizeSwiftTypes([{ address: "0x1", name: "__T04main4PairVMa" }], {
category: "structs",
}),
).toMatchObject({
total: 1,
categories: { structs: { count: 1 } },
unclassified: [],
});
});
it("does not let a demangler-only alias erase a decoded category", () => {
const result = categorizeSwiftTypes(
[{ address: "0x1", name: "$s4main7ServiceCMa" }],
{},
[{ address: "0x1", name: "$e4main7ServiceCMa" }],
);
expect(result.categories.classes?.items).toMatchObject([
{
name: "$s4main7ServiceCMa",
mangled_names: ["$s4main7ServiceCMa", "$e4main7ServiceCMa"],
},
]);
expect(result.categories.other?.items).toEqual([]);
});
it.each([undefined, "classes"] as const)(
"keeps a decoded alias category beside an undecoded Swift alias (filter %s)",
(category) => {
const result = categorizeSwiftTypes(
[{ address: "0x1", name: "main::Service::init" }],
category === undefined ? {} : { category },
[
{ address: "0x1", name: "_$s4main7ServiceCMa" },
{ address: "0x1", name: "_$s4main04PairVMa" },
],
);
expect(result).toMatchObject({
total: 1,
categories: {
classes: {
count: 1,
items: [
{
name: "main::Service::init",
mangled_names: ["_$s4main7ServiceCMa", "_$s4main04PairVMa"],
},
],
},
other: { count: 0 },
},
unclassified: [],
});
},
);
});
describe("Objective-C labels that merely contain _Tt", () => {
const labels = (selector: string) => [
{ address: "0x1", name: `-[Widget ${selector}]` },
{ address: "0x2", name: `_objc_msgSend$${selector}` },
];
it.each(["_TtReset", "_TtPing"])(
"are not Swift observations or protocols: %s",
(selector) => {
expect(categorizeSwiftTypes(labels(selector))).toMatchObject({
total: 0,
unclassified: [],
});
expect(discoverObjcProtocols(labels(selector))).toEqual({
count: 0,
protocols: [],
});
},
);
it("keeps embedded legacy Swift runtime names", () => {
expect(
discoverObjcProtocols([
{ address: "0x1", name: "_OBJC_PROTOCOL_$__TtP5Hello5Proto_" },
{ address: "0x2", name: "_TtPs5Error_" },
]),
).toMatchObject({ count: 2 });
expect(
categorizeSwiftTypes([{ address: "0x1", name: "-[_TtC5Hello3Foo bar]" }]),
).toMatchObject({ total: 1 });
});
});