refactor(objc): retire obsolete class discovery paths

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.
This commit is contained in:
morluto committed 2026-10-24 09:14:21 +08:00
1 parent 3ae7daa101
commit 8defd220a9
3 files changed
+67 -83

No files matched your search

@@ -183,6 +183,29 @@ it.each([
},
);
it("reports no applicable identity scan when names contain no owned class labels", async () => {
const execute = vi.fn(
createPort({
names: [
{ address: "0x1000", value: "__objc_class_HuntRoot_class" },
{ address: "0x2000", value: "PTR__OBJC_CLASS_$_NSAlert_100028668" },
{ address: "0x3000", value: "s__OBJC_CLASS_$_NSAlert_10002ed0d" },
],
}).execute,
);
const tools = new EnhancedTools({ execute });
await expect(tools.execute("get_objc_classes", {})).resolves.toEqual({
ok: true,
value: {
count: 0,
classes: [],
identity_coverage: { status: "not_applicable", reason: null },
},
});
expect(execute).toHaveBeenCalledTimes(1);
expect(execute.mock.calls[0]?.[0]).toBe("list_names");
});
describe("Objective-C class import identity composition", () => {
it("joins only exact same-target bind slots and preserves literal filter selection", async () => {
const tools = new EnhancedTools(createPort());
@@ -209,7 +232,22 @@ describe("Objective-C class import identity composition", () => {
ok: true,
value: {
count: 1,
classes: [{ address: "0x1000", name: "OBJC_CLASS_$_Document$GOT" }],
classes: [
{
address: "0x1000",
name: "OBJC_CLASS_$_Document$GOT",
binding_identity: {
symbol: "_OBJC_CLASS_$_Document",
binding_source: "chained",
library_ordinal: 1,
library: "/System/Library/Frameworks/AppKit.framework/AppKit",
weak: false,
addend: "0",
slot_addresses: ["0x1000"],
},
},
],
identity_coverage: { status: "complete", reason: null },
},
});
});
@@ -274,6 +312,12 @@ describe("Objective-C class import identity composition", () => {
it("keeps names with unknown identity when binding inspection is unavailable", async () => {
const tools = new EnhancedTools(
createPort({
names: [
{ address: "0x1000", value: "OBJC_CLASS_$_Document$GOT" },
{ address: "0x1000", value: "OBJC_CLASS_$_Document" },
{ address: "0x2000", value: "OBJC_CLASS_$_Document" },
{ address: "0x2000", value: "OBJC_CLASS_$_Document" },
],
metadataFailure: new AnalysisCapabilityUnavailableError(
provider.id,
"inspect_native_dispatch_metadata",
@@ -284,7 +328,12 @@ describe("Objective-C class import identity composition", () => {
await expect(tools.execute("get_objc_classes", {})).resolves.toMatchObject({
ok: true,
value: {
count: 2,
count: 3,
classes: [
{ address: "0x1000", name: "OBJC_CLASS_$_Document$GOT" },
{ address: "0x1000", name: "OBJC_CLASS_$_Document" },
{ address: "0x2000", name: "OBJC_CLASS_$_Document" },
],
identity_coverage: {
status: "unknown",
reason: "No static Mach-O reader",
+15 -60
View File
@@ -1,7 +1,7 @@
import { describe, expect, it } from "vitest";
import {
discoverObjcClasses,
selectObjcClassCandidates,
projectObjcClassCandidates,
} from "./symbolAnalysis.js";
@@ -19,15 +19,11 @@ const classImport = {
describe("Objective-C class label discovery", () => {
it("does not reinterpret a provider-specific label without an owner tag", () => {
expect(
discoverObjcClasses(
selectObjcClassCandidates(
[{ address: "0x1", name: "__objc_class_HuntRoot_class" }],
"Hunt",
),
).toEqual({
count: 0,
classes: [],
identity_coverage: { status: "not_applicable", reason: null },
});
).toEqual([]);
});
it("uses provider-tagged stripped class labels and filters their exact text", () => {
@@ -51,25 +47,10 @@ describe("Objective-C class label discovery", () => {
: {}),
}));
expect(discoverObjcClasses(names, "Hunt")).toEqual({
count: 6,
classes: names.slice(0, 6).map(({ address, name }) => ({
address,
name,
})),
identity_coverage: {
status: "unknown",
reason: "Exact class-import binding identity is unavailable.",
},
});
expect(discoverObjcClasses(names, "__objc_class_HuntRoot_class")).toEqual({
count: 1,
classes: [{ address: "0x1", name: "__objc_class_HuntRoot_class" }],
identity_coverage: {
status: "unknown",
reason: "Exact class-import binding identity is unavailable.",
},
});
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", () => {
@@ -102,24 +83,6 @@ describe("Objective-C class label discovery", () => {
],
identity_coverage: completeCoverage,
});
expect(discoverObjcClasses([alias, canonical], "$GOT", scan)).toEqual({
count: 1,
classes: [
{
...alias,
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"],
},
},
],
identity_coverage: completeCoverage,
});
});
it("does not join same-name rows without exact slot evidence", () => {
@@ -153,20 +116,13 @@ describe("Objective-C class label discovery", () => {
).toMatchObject({ count: 2, classes: rows });
});
it("keeps same-named candidates at distinct addresses until bind identity is known", () => {
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(discoverObjcClasses(rows, "")).toEqual({
count: 2,
classes: [rows[0], rows[2]],
identity_coverage: {
status: "unknown",
reason: "Exact class-import binding identity is unavailable.",
},
});
expect(selectObjcClassCandidates(rows, "")).toEqual([rows[0], rows[2]]);
});
});
@@ -176,7 +132,7 @@ it("keeps equal symbols separate when only their lookup ordinals differ", () =>
{ address: "0x2000", name: "OBJC_CLASS_$_Document$GOT" },
];
expect(
discoverObjcClasses(rows, "", {
projectObjcClassCandidates(rows, {
bindings: [
{ ...classImport, library_ordinal: -1, library: null },
{
@@ -203,7 +159,7 @@ it("keeps regular self-scope and weak rebinding records separate", () => {
{ address: "0x2000", name: "OBJC_CLASS_$_Document$GOT" },
];
expect(
discoverObjcClasses(rows, "", {
projectObjcClassCandidates(rows, {
bindings: [
{ ...classImport, library_ordinal: 0, library: null },
{
@@ -226,7 +182,7 @@ it("keeps regular self-scope and weak rebinding records separate", () => {
});
it("excludes pointer and string labels that only contain a class marker", () => {
const result = discoverObjcClasses(
const result = selectObjcClassCandidates(
[
{ address: "0x1", name: "PTR__OBJC_CLASS_$_NSAlert_100028668" },
{ address: "0x2", name: "s__OBJC_CLASS_$_NSAlert_10002ed0d" },
@@ -234,8 +190,7 @@ it("excludes pointer and string labels that only contain a class marker", () =>
],
"",
);
expect(result).toMatchObject({
count: 1,
classes: [{ address: "0x3", name: "AppKit::_OBJC_CLASS_$_NSAlert" }],
});
expect(result).toEqual([
{ address: "0x3", name: "AppKit::_OBJC_CLASS_$_NSAlert" },
]);
});
+1 -21
View File
@@ -54,16 +54,7 @@ export const selectObjcClassCandidates = (
/** Project selected class labels, coalescing only exact proven import identities. */
export const projectObjcClassCandidates = (
candidates: readonly AddressedName[],
bindingScan: ObjcClassImportBindingScan = {
bindings: [],
coverage: {
status: candidates.length === 0 ? "not_applicable" : "unknown",
reason:
candidates.length === 0
? null
: "Exact class-import binding identity is unavailable.",
},
},
bindingScan: ObjcClassImportBindingScan,
): JsonValue => {
const classes = coalesceBoundClassAliases(candidates, bindingScan.bindings);
return {
@@ -76,17 +67,6 @@ export const projectObjcClassCandidates = (
};
};
/** Select, then project Objective-C class labels for provider-neutral callers. */
export const discoverObjcClasses = (
names: readonly AddressedName[],
pattern: string,
bindingScan?: ObjcClassImportBindingScan,
): JsonValue =>
projectObjcClassCandidates(
selectObjcClassCandidates(names, pattern),
bindingScan,
);
/** Select and deduplicate Objective-C and Swift protocol labels. */
export const discoverObjcProtocols = (
names: readonly AddressedName[],