UNPKG

@thi.ng/wasm-api-bindgen

Version:

Polyglot bindings code generators (TS/JS, Zig, C11) for hybrid WebAssembly projects

219 lines (218 loc) 6.26 kB
import { SIZEOF } from "@thi.ng/api/typedarray"; import { compareByKey } from "@thi.ng/compare/keys"; import { compareNumDesc } from "@thi.ng/compare/numeric"; import { DEFAULT, defmulti } from "@thi.ng/defmulti/defmulti"; import { illegalArgs } from "@thi.ng/errors/illegal-arguments"; import { PKG_NAME, WASM32 } from "./api.js"; import { selectAlignment } from "./internal/align.js"; import { ensureStringArray, isBigNumeric, isNumeric, isOpaque, isPointer, isPointerLike, isSizeT, isSlice, isStringSlice, isWasmString } from "./internal/utils.js"; const DEFAULT_CODEGEN_OPTS = { debug: false, header: true, lineWidth: 80, stringType: "ptr", target: WASM32, uppercaseEnums: true }; const __sizeOf = defmulti( (x) => x.type, {}, { [DEFAULT]: (field, coll, align, opts) => { if (field.__size) return field.__size; if (field.pad != null) { field.pad < 1 && illegalArgs(`pad size must be > 0`); return field.__size = field.pad; } let size = 0; if (isPointer(field.tag)) { size = opts.target.sizeBytes; } else if (isSlice(field.tag)) { size = opts.target.sizeBytes * 2; } else { size = isNumeric(field.type) || isBigNumeric(field.type) ? SIZEOF[field.type] : isWasmString(field.type) ? opts.target.sizeBytes * (isStringSlice(opts.stringType) ? 2 : 1) : isOpaque(field.type) ? opts.target.sizeBytes : __sizeOf(coll[field.type], coll, align, opts); if (field.tag == "array" || field.tag === "vec") { size *= field.len; if (field.sentinel !== void 0 && field.tag === "array") { size += SIZEOF[field.type]; } } } return field.__size = align.size(size, field.__align); }, ext: (type) => type.__size = type.size, enum: (type) => { if (type.__size) return type.__size; return type.__size = SIZEOF[type.tag]; }, struct: (type, coll, align, opts) => { if (type.__size) return type.__size; let offset = 0; for (let f of type.fields) { offset = align.offset(offset, f.__align); f.__offset = offset; offset += __sizeOf(f, coll, align, opts); } return type.__size = align.size(offset, type.__align); }, union: (type, coll, align, opts) => { if (type.__size) return type.__size; let maxSize = 0; for (let f of type.fields) { f.__offset = 0; maxSize = Math.max(maxSize, __sizeOf(f, coll, align, opts)); } return type.__size = align.size(maxSize, type.__align); }, funcptr: (type, _, __, opts) => { return type.__size || (type.__size = opts.target.sizeBytes); } } ); const __alignOf = defmulti( (x) => x.type, {}, { [DEFAULT]: (field, coll, align, opts) => { if (field.__align) return field.__align; if (field.pad) return field.__align = 1; if (isSizeT(field.type)) { field.type = opts.target[field.type]; } return field.__align = isPointerLike(field, coll) ? align.align({ type: opts.target.usize }) : isNumeric(field.type) || isBigNumeric(field.type) ? align.align(field) : __alignOf( coll[field.type], coll, selectAlignment(coll[field.type]), opts ); }, ext: (type) => { const e = type; return e.__align = e.align; }, enum: (type, _, align) => { const e = type; if (!e.tag) e.tag = "i32"; return e.__align = align.align({ type: e.tag }); }, struct: (type, coll, align, opts) => { let maxAlign = 1; for (let f of type.fields) { maxAlign = Math.max(maxAlign, __alignOf(f, coll, align, opts)); } return type.__align = maxAlign; }, union: (type, coll, align, opts) => { let maxAlign = 1; for (let f of type.fields) { maxAlign = Math.max(maxAlign, __alignOf(f, coll, align, opts)); } return type.__align = maxAlign; }, funcptr: (type, coll, align, opts) => { if (type.__align) return type.__align; const ptr = type; if (ptr.rtype !== "void") { __sizeOf(ptr.rtype, coll, align, opts); } for (let a of ptr.args) { __alignOf(a, coll, align, opts); } return type.__align = align.align({ type: opts.target.usize }); } } ); const __prepareType = defmulti( (x) => x.type, {}, { [DEFAULT]: (x, coll, alignImpl, opts) => { if (x.__align) return; __alignOf(x, coll, alignImpl, opts); __sizeOf(x, coll, alignImpl, opts); }, struct: (x, coll, align, opts) => { if (x.__align) return; const struct = x; __alignOf(struct, coll, align, opts); if (struct.auto) { struct.fields.sort( compareByKey("__align", compareNumDesc) ); } for (let f of struct.fields) { const type = coll[f.type]; if (type) { __prepareType(type, coll, selectAlignment(type), opts); } } __sizeOf(struct, coll, align, opts); } } ); const prepareTypes = (coll, opts) => { for (let id in coll) { __prepareType(coll[id], coll, selectAlignment(coll[id]), opts); } return coll; }; const generateTypes = (coll, codegen, opts = {}) => { const $opts = { ...DEFAULT_CODEGEN_OPTS, ...opts }; prepareTypes(coll, $opts); const res = []; if ($opts.header) { codegen.doc( [ `Generated by ${PKG_NAME} at ${(/* @__PURE__ */ new Date()).toISOString()}`, "DO NOT EDIT!" ], res, $opts, true ); res.push(""); } if (codegen.pre) { const pre = codegen.pre(coll, $opts); pre && res.push(pre, ""); } $opts.pre && res.push(...ensureStringArray($opts.pre), ""); for (let id in coll) { const type = coll[id]; if (type.skip?.includes(codegen.id)) continue; type.doc && codegen.doc(type.doc, res, $opts); codegen[type.type](type, coll, res, $opts); } $opts.post && res.push("", ...ensureStringArray($opts.post)); if (codegen.post) { const post = codegen.post(coll, $opts); post && res.push("", post); } return res.join("\n"); }; export { DEFAULT_CODEGEN_OPTS, generateTypes, prepareTypes };