mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-26 06:34:20 +08:00
102 lines
3.5 KiB
JavaScript
102 lines
3.5 KiB
JavaScript
#!/usr/bin/env node
|
|
/* Writes packages/runtime/src/scr_dyn_methods.h: the method-name ids the
|
|
* checked-dynamic dispatch in scr_dyn_invoke.c compares (DYN_M_<name>), the
|
|
* names, and an open-addressed hash table mapping a name to its id. The ids
|
|
* are the DYN_M_ names scr_dyn_invoke.c uses, so the header follows the
|
|
* dispatch code.
|
|
*
|
|
* node scripts/gen-dyn-methods.mjs write the header
|
|
* node scripts/gen-dyn-methods.mjs --check fail when it is out of date */
|
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
|
|
const source = new URL("../packages/runtime/src/scr_dyn_invoke.c", import.meta.url);
|
|
const target = new URL("../packages/runtime/src/scr_dyn_methods.h", import.meta.url);
|
|
const SLOTS = 256;
|
|
|
|
const used = new Set();
|
|
for (const match of readFileSync(source, "utf8").matchAll(/\bDYN_M_([A-Za-z][A-Za-z0-9]*)\b/g))
|
|
if (match[1] !== "NONE" && match[1] !== "COUNT") used.add(match[1]);
|
|
const names = [...used].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
if (names.length >= SLOTS / 2) throw new Error("too many method names for the hash table");
|
|
|
|
// The slot dyn_method_of in the header computes.
|
|
const slotOf = (name) =>
|
|
(name.length * 7 + name.charCodeAt(0) * 31 + name.charCodeAt(name.length - 1)) & (SLOTS - 1);
|
|
const slots = new Array(SLOTS).fill(0);
|
|
names.forEach((name, i) => {
|
|
let slot = slotOf(name);
|
|
while (slots[slot] !== 0) slot = (slot + 1) & (SLOTS - 1);
|
|
slots[slot] = i + 1;
|
|
});
|
|
|
|
const rows = (values, width) => {
|
|
const out = [];
|
|
for (let i = 0; i < values.length; i += width)
|
|
out.push(` ${values.slice(i, i + width).join(", ")},`);
|
|
return out;
|
|
};
|
|
const text = [
|
|
"/* Generated by scripts/gen-dyn-methods.mjs from the DYN_M_ ids scr_dyn_invoke.c",
|
|
" * uses. Do not edit by hand. */",
|
|
"#ifndef SCR_DYN_METHODS_H",
|
|
"#define SCR_DYN_METHODS_H",
|
|
"",
|
|
"#include <stddef.h>",
|
|
"#include <string.h>",
|
|
"",
|
|
"enum {",
|
|
" DYN_M_NONE,",
|
|
...names.map((name) => ` DYN_M_${name},`),
|
|
" DYN_M_COUNT",
|
|
"};",
|
|
"",
|
|
"static const char *const dyn_method_names[DYN_M_COUNT] = {",
|
|
" NULL,",
|
|
...names.map((name) => ` ${JSON.stringify(name)},`),
|
|
"};",
|
|
"",
|
|
"static const unsigned char dyn_method_lengths[DYN_M_COUNT] = {",
|
|
...rows([0, ...names.map((name) => name.length)], 16),
|
|
"};",
|
|
"",
|
|
`/* Each name's id at its slot (or the next free one); 0 ends a probe. */`,
|
|
`static const unsigned char dyn_method_slots[${SLOTS}] = {`,
|
|
...rows(slots, 16),
|
|
"};",
|
|
"",
|
|
`#define DYN_METHOD_MAX_LENGTH ${Math.max(...names.map((name) => name.length))}`,
|
|
"",
|
|
"/* The id of a method name, compared by content; DYN_M_NONE for any other",
|
|
" * name. */",
|
|
"static inline int dyn_method_of(const char *m) {",
|
|
" size_t length = strlen(m);",
|
|
" if (length == 0 || length > DYN_METHOD_MAX_LENGTH) return DYN_M_NONE;",
|
|
" unsigned slot = (unsigned)(length * 7 + (unsigned char)m[0] * 31 +",
|
|
` (unsigned char)m[length - 1]) & ${SLOTS - 1};`,
|
|
" for (;;) {",
|
|
" unsigned id = dyn_method_slots[slot];",
|
|
" if (id == 0) return DYN_M_NONE;",
|
|
" if (dyn_method_lengths[id] == length && memcmp(m, dyn_method_names[id], length) == 0) return (int)id;",
|
|
` slot = (slot + 1) & ${SLOTS - 1};`,
|
|
" }",
|
|
"}",
|
|
"",
|
|
"#endif",
|
|
"",
|
|
].join("\n");
|
|
|
|
if (process.argv.includes("--check")) {
|
|
let current = "";
|
|
try {
|
|
current = readFileSync(target, "utf8");
|
|
} catch {}
|
|
if (current !== text) {
|
|
console.error(
|
|
"packages/runtime/src/scr_dyn_methods.h is out of date: run node scripts/gen-dyn-methods.mjs",
|
|
);
|
|
process.exit(1);
|
|
}
|
|
} else {
|
|
writeFileSync(target, text);
|
|
}
|