mirror of
https://github.com/morluto/rea.git
synced 2026-11-14 02:32:54 +00:00
* fix(hopper): normalize string-backed addresses and update verification * feat(native): add inspection primitives, dispatch traces and approved UI observation * test: prioritize real artifact E2E and captured golden regressions * fix(process): queue observed output before reactive rendering actions * test: prune redundant cases and strengthen boundary assertions * fix(cli): route native value tracing through shared workflows * test: replace synthetic success with real workflows and captured data * fix(catalog): separate portable metadata from native host availability
120 lines
3.5 KiB
TypeScript
120 lines
3.5 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { decodeAppleDispatchMetadata } from "./AppleDispatchMetadata.js";
|
|
|
|
const base = 0x100000000n;
|
|
const fixture = (relative = false) => {
|
|
const bytes = Buffer.alloc(2048);
|
|
const u32 = (offset: number, value: number) =>
|
|
bytes.writeUInt32LE(value, offset);
|
|
const ptr = (offset: number, target: number) =>
|
|
bytes.writeBigUInt64LE(base + BigInt(target), offset);
|
|
u32(0, 0xfeedfacf);
|
|
u32(4, 0x0100000c);
|
|
u32(16, 1);
|
|
u32(20, 152);
|
|
u32(32, 0x19);
|
|
u32(36, 152);
|
|
bytes.write("__DATA", 40);
|
|
ptr(56, 0);
|
|
bytes.writeBigUInt64LE(2048n, 64);
|
|
bytes.writeBigUInt64LE(2048n, 80);
|
|
u32(92, 5);
|
|
u32(96, 1);
|
|
bytes.write("__objc_classlist", 104);
|
|
bytes.write("__DATA", 120);
|
|
ptr(136, 0x180);
|
|
bytes.writeBigUInt64LE(8n, 144);
|
|
u32(152, 0x180);
|
|
ptr(0x180, 0x200);
|
|
ptr(0x200, 0x280);
|
|
ptr(0x220, 0x300);
|
|
ptr(0x2a0, 0x380);
|
|
u32(0x308, 16);
|
|
ptr(0x318, 0x450);
|
|
ptr(0x320, 0x480);
|
|
ptr(0x330, 0x500);
|
|
u32(0x380, 1);
|
|
ptr(0x398, 0x450);
|
|
bytes.write("Fixture\0", 0x450);
|
|
bytes.write("performAction:\0", 0x460);
|
|
u32(0x480, relative ? 0xc000000c : 24);
|
|
u32(0x484, 1);
|
|
if (relative) {
|
|
bytes.writeInt32LE(0x460 - 0x488, 0x488);
|
|
bytes.writeInt32LE(0x5c0 - 0x48c, 0x48c);
|
|
bytes.writeInt32LE(0x700 - 0x490, 0x490);
|
|
} else {
|
|
ptr(0x488, 0x460);
|
|
ptr(0x490, 0x5c0);
|
|
ptr(0x498, 0x700);
|
|
}
|
|
u32(0x500, 32);
|
|
u32(0x504, 1);
|
|
ptr(0x508, 0x580);
|
|
ptr(0x510, 0x5a0);
|
|
ptr(0x518, 0x5c0);
|
|
u32(0x580, 8);
|
|
bytes.write("state\0", 0x5a0);
|
|
bytes.write("i\0", 0x5c0);
|
|
return bytes;
|
|
};
|
|
const provenance = { path: "/fixture/app", sha256: "a".repeat(64) };
|
|
describe("Apple dispatch binary metadata", () => {
|
|
it.each([false, true])(
|
|
"decodes stripped absolute/relative method lists (relative=%s) with exact offsets",
|
|
(relative) => {
|
|
const result = decodeAppleDispatchMetadata(
|
|
fixture(relative),
|
|
100,
|
|
provenance,
|
|
);
|
|
expect(result.objc_classes).toMatchObject([
|
|
{
|
|
name: "Fixture",
|
|
instance_size: 16,
|
|
location: { address: "0x100000200", file_offset: 512 },
|
|
},
|
|
{ name: "Fixture", is_meta_class: true },
|
|
]);
|
|
expect(result.objc_dispatch_implementations).toMatchObject([
|
|
{
|
|
selector: "performAction:",
|
|
implementation_address: "0x100000700",
|
|
location: { file_offset: 0x488 },
|
|
decode: { status: "decoded" },
|
|
},
|
|
]);
|
|
expect(result.objc_ivars).toMatchObject([
|
|
{
|
|
name: "state",
|
|
type_encoding: "i",
|
|
offset: 8,
|
|
location: { file_offset: 0x508 },
|
|
},
|
|
]);
|
|
},
|
|
);
|
|
it("keeps unsupported pointers and record truncation explicit", () => {
|
|
const bytes = fixture();
|
|
bytes.writeBigUInt64LE(0xffffffffffffffffn, 0x498);
|
|
const result = decodeAppleDispatchMetadata(bytes, 100, provenance);
|
|
expect(result.objc_dispatch_implementations[0]).toMatchObject({
|
|
implementation_address: null,
|
|
decode: { status: "partial" },
|
|
});
|
|
expect(
|
|
decodeAppleDispatchMetadata(fixture(), 1, provenance).coverage[0]?.reason,
|
|
).toContain("max_records_reached");
|
|
});
|
|
it("rejects malformed command and section boundaries", () => {
|
|
const bytes = fixture();
|
|
bytes.writeUInt32LE(7, 36);
|
|
expect(() => decodeAppleDispatchMetadata(bytes, 100, provenance)).toThrow(
|
|
"command size",
|
|
);
|
|
expect(() =>
|
|
decodeAppleDispatchMetadata(Buffer.alloc(10), 100, provenance),
|
|
).toThrow("Truncated");
|
|
});
|
|
});
|