mirror of
https://github.com/morluto/rea.git
synced 2026-11-09 16:26:36 +00:00
Remove the test-only selection/projection facade and its unused synthesized binding scan. Keep lexical and identity controls at their active owners, with combined behavior covered through EnhancedTools.
197 lines
5.9 KiB
TypeScript
197 lines
5.9 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
selectObjcClassCandidates,
|
|
projectObjcClassCandidates,
|
|
} from "./symbolAnalysis.js";
|
|
|
|
const completeCoverage = { status: "complete" as const, reason: null };
|
|
const classImport = {
|
|
requested_address: "0x1000",
|
|
symbol: "_OBJC_CLASS_$_Document",
|
|
binding_source: "bind" as const,
|
|
library_ordinal: 1,
|
|
library: "/System/Library/Frameworks/AppKit.framework/AppKit",
|
|
weak: false,
|
|
addend: "0",
|
|
};
|
|
|
|
describe("Objective-C class label discovery", () => {
|
|
it("does not reinterpret a provider-specific label without an owner tag", () => {
|
|
expect(
|
|
selectObjcClassCandidates(
|
|
[{ address: "0x1", name: "__objc_class_HuntRoot_class" }],
|
|
"Hunt",
|
|
),
|
|
).toEqual([]);
|
|
});
|
|
|
|
it("uses provider-tagged stripped class labels and filters their exact text", () => {
|
|
const labels = [
|
|
"__objc_class_HuntRoot_class",
|
|
"__objc_class_HuntSub_class",
|
|
"__objc_class_HuntWorker_class",
|
|
"__objc_class_HuntModel_class",
|
|
"__objc_class_HuntLegacy_class",
|
|
"__objc_class_Hunt_class_class",
|
|
"prefix__objc_class_HuntForeign_class",
|
|
"__objc_class__class",
|
|
"__objc_class_HuntMissingSuffix",
|
|
"__objc_class_HuntExtra_class_suffix",
|
|
];
|
|
const names = labels.map((name, index) => ({
|
|
address: `0x${(index + 1).toString(16)}`,
|
|
name,
|
|
...(index < 6
|
|
? { objc_class_label: "hopper_legacy_class_object" as const }
|
|
: {}),
|
|
}));
|
|
|
|
expect(selectObjcClassCandidates(names, "Hunt")).toEqual(names.slice(0, 6));
|
|
expect(
|
|
selectObjcClassCandidates(names, "__objc_class_HuntRoot_class"),
|
|
).toEqual([names[0]]);
|
|
});
|
|
|
|
it("coalesces only selected rows whose own addresses prove the same import", () => {
|
|
const alias = { address: "0x1000", name: "OBJC_CLASS_$_Document$GOT" };
|
|
const canonical = { address: "0x1000", name: "OBJC_CLASS_$_Document" };
|
|
const unrelated = { address: "0x2000", name: "OBJC_CLASS_$_Document" };
|
|
const scan = {
|
|
bindings: [classImport],
|
|
coverage: completeCoverage,
|
|
};
|
|
|
|
expect(
|
|
projectObjcClassCandidates([alias, canonical, unrelated], scan),
|
|
).toEqual({
|
|
count: 2,
|
|
classes: [
|
|
{
|
|
...canonical,
|
|
binding_identity: {
|
|
symbol: classImport.symbol,
|
|
binding_source: classImport.binding_source,
|
|
library_ordinal: classImport.library_ordinal,
|
|
library: classImport.library,
|
|
weak: false,
|
|
addend: "0",
|
|
slot_addresses: ["0x1000"],
|
|
},
|
|
},
|
|
unrelated,
|
|
],
|
|
identity_coverage: completeCoverage,
|
|
});
|
|
});
|
|
|
|
it("does not join same-name rows without exact slot evidence", () => {
|
|
const alias = { address: "0x1000", name: "OBJC_CLASS_$_Document$GOT" };
|
|
const canonical = { address: "0x2000", name: "OBJC_CLASS_$_Document" };
|
|
expect(
|
|
projectObjcClassCandidates([alias, canonical], {
|
|
bindings: [classImport],
|
|
coverage: completeCoverage,
|
|
}),
|
|
).toMatchObject({ count: 2, classes: [alias, canonical] });
|
|
});
|
|
|
|
it("keeps rows separate when equal symbols have different bind identities", () => {
|
|
const rows = [
|
|
{ address: "0x1000", name: "OBJC_CLASS_$_Document$GOT" },
|
|
{ address: "0x2000", name: "OBJC_CLASS_$_Document$GOT" },
|
|
];
|
|
expect(
|
|
projectObjcClassCandidates(rows, {
|
|
bindings: [
|
|
classImport,
|
|
{
|
|
...classImport,
|
|
requested_address: "0x2000",
|
|
library: "/other/framework.dylib",
|
|
},
|
|
],
|
|
coverage: completeCoverage,
|
|
}),
|
|
).toMatchObject({ count: 2, classes: rows });
|
|
});
|
|
|
|
it("deduplicates exact class labels while keeping distinct addresses", () => {
|
|
const rows = [
|
|
{ address: "0x1000", name: "OBJC_CLASS_$_Document" },
|
|
{ address: "0x1000", name: "OBJC_CLASS_$_Document" },
|
|
{ address: "0x2000", name: "OBJC_CLASS_$_Document" },
|
|
];
|
|
expect(selectObjcClassCandidates(rows, "")).toEqual([rows[0], rows[2]]);
|
|
});
|
|
});
|
|
|
|
it("keeps equal symbols separate when only their lookup ordinals differ", () => {
|
|
const rows = [
|
|
{ address: "0x1000", name: "OBJC_CLASS_$_Document$GOT" },
|
|
{ address: "0x2000", name: "OBJC_CLASS_$_Document$GOT" },
|
|
];
|
|
expect(
|
|
projectObjcClassCandidates(rows, {
|
|
bindings: [
|
|
{ ...classImport, library_ordinal: -1, library: null },
|
|
{
|
|
...classImport,
|
|
requested_address: "0x2000",
|
|
library_ordinal: -2,
|
|
library: null,
|
|
},
|
|
],
|
|
coverage: completeCoverage,
|
|
}),
|
|
).toMatchObject({
|
|
count: 2,
|
|
classes: [
|
|
{ ...rows[0], binding_identity: { library_ordinal: -1 } },
|
|
{ ...rows[1], binding_identity: { library_ordinal: -2 } },
|
|
],
|
|
});
|
|
});
|
|
|
|
it("keeps regular self-scope and weak rebinding records separate", () => {
|
|
const rows = [
|
|
{ address: "0x1000", name: "OBJC_CLASS_$_Document$GOT" },
|
|
{ address: "0x2000", name: "OBJC_CLASS_$_Document$GOT" },
|
|
];
|
|
expect(
|
|
projectObjcClassCandidates(rows, {
|
|
bindings: [
|
|
{ ...classImport, library_ordinal: 0, library: null },
|
|
{
|
|
...classImport,
|
|
requested_address: "0x2000",
|
|
binding_source: "weak",
|
|
library_ordinal: 0,
|
|
library: null,
|
|
},
|
|
],
|
|
coverage: completeCoverage,
|
|
}),
|
|
).toMatchObject({
|
|
count: 2,
|
|
classes: [
|
|
{ ...rows[0], binding_identity: { binding_source: "bind" } },
|
|
{ ...rows[1], binding_identity: { binding_source: "weak" } },
|
|
],
|
|
});
|
|
});
|
|
|
|
it("excludes pointer and string labels that only contain a class marker", () => {
|
|
const result = selectObjcClassCandidates(
|
|
[
|
|
{ address: "0x1", name: "PTR__OBJC_CLASS_$_NSAlert_100028668" },
|
|
{ address: "0x2", name: "s__OBJC_CLASS_$_NSAlert_10002ed0d" },
|
|
{ address: "0x3", name: "AppKit::_OBJC_CLASS_$_NSAlert" },
|
|
],
|
|
"",
|
|
);
|
|
expect(result).toEqual([
|
|
{ address: "0x3", name: "AppKit::_OBJC_CLASS_$_NSAlert" },
|
|
]);
|
|
});
|