#!/usr/bin/env -S deno run -A /*! dump_registry.ts (C) 2022-present SheetJS LLC -- https://sheetjs.com */ /* NOTE: this script requires a SIP-disabled Mac, Numbers, LLDB, and Deno USAGE: deno run -A https://oss.sheetjs.com/notes/iwa/dump_registry.ts */ if(Deno.build.os != "darwin") throw `Must run in macOS!`; const arch_map = { "x86_64": "x86_64", "aarch64": "arm64" }; if(!arch_map[Deno.build.arch]) throw `Unsupported architecture ${Deno.build.arch}`; const cmd = Deno?.args?.[0] || "/Applications/Numbers.app/Contents/MacOS/Numbers"; try { Deno.statSync(cmd); } catch(e) { throw `Could not find ${cmd}!`; } /* test if SIP is disabled */ { const p = Deno.run({cmd:["csrutil","status"],stdin:"piped",stdout:"piped" }); const [status, stdout] = await Promise.all([ p.status(), p.output() ]); await p.close(); const data = new TextDecoder().decode(stdout); if(!data.includes("disabled")) throw `SIP must be disabled!`; } /* start debugger */ const p = Deno.run({ cmd: `lldb ${cmd} -a ${arch_map[Deno.build.arch]}`.split(" "), stdin: "piped", stdout: "piped" }); /* run commands */ const doit = (x: string) => p?.stdin?.write(new TextEncoder().encode(x)) const cmds = [ "b -[NSApplication _sendFinishLaunchingNotification]", "settings set auto-confirm 1", "breakpoint command add 1.1", "po [TSPRegistry sharedRegistry]", "process kill", "exit", "DONE", "run", ]; for(const cmd of cmds) await doit(cmd + "\n"); /* LLDB does not exit normally, setTimeout workaround */ setTimeout(() => p.kill("SIGKILL"), 30000) /* wait for debugger */ const [status, stdout] = await Promise.all([ p.status(), p.output() ]); await p.close(); /* search for _messageTypeToPrototypeMap */ const data = new TextDecoder().decode(stdout); const res = data.match(/_messageTypeToPrototypeMap = {([^]*?)}/m)?.[1]; if(!res) throw `Could not find map!`; /* extract records and sort by ID */ const rows = res.split(/[\r\n]+/).map(r => r.trim().split(/\s+/)).filter(x => x.length > 1); rows.sort((l, r) => +l[0] - +r[0]); /* emit code */ console.log(`export default {`); rows.forEach(r => { /* setDeprecatedMessageType */ if(r[3] == "null") return; console.log(` ${r[0]}: ".${r[3]}",`); }); console.log(`} as {[key: number]: string};`);