mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
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>
206 lines
5.9 KiB
TypeScript
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 });
|
|
});
|
|
});
|