diff --git a/.gitignore b/.gitignore index 03feeac..eaa46a0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ generate-wgpu-bindings_cache_*.json generate-bindings_tmp_exec example.ts +lib +bindings_cache_* # Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore diff --git a/.npmignore b/.npmignore index 7e36a09..7ee2d90 100644 --- a/.npmignore +++ b/.npmignore @@ -2,3 +2,4 @@ node_modules generate-wgpu-bindings_cache_*.json generate-bindings_tmp_exec example.ts +src diff --git a/README.md b/README.md index b6894e5..ef70086 100644 --- a/README.md +++ b/README.md @@ -25,11 +25,15 @@ bun install bun-ffi-gen Example of generating bindings for wgpu library. ```ts -import { ClangTypeInfoCache, clangGetAstJson, CodeGen, parseClangAst } from "bun-ffi-gen"; +import { ClangTypeInfoCache, clangGetAstJson, CodeGen, parseClangAst, clangClean, addIncludeDir } from "bun-ffi-gen"; +import path from "path"; const HEADER_PATH = "./wgpu/wgpu.h"; const TYPE_CACHE_PATH = "./generate-wgpu-bindings_cache"; +// add include dirs for clang +addIncludeDir(path.resolve("my_include_dir")); + // get header ast from clang const wgpuAst = clangGetAstJson(HEADER_PATH); diff --git a/package.json b/package.json index ea773a0..dc16f14 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,9 @@ "type": "module", "main": "lib/index.ts", "types": "lib/index.ts", + "scripts": { + "build": "tsc" + }, "author": { "name": "morglod", "url": "https://github.com/Morglod" diff --git a/src/clang.ts b/src/clang.ts index b8ddf59..17b754e 100644 --- a/src/clang.ts +++ b/src/clang.ts @@ -1,20 +1,56 @@ -import { execSync } from "child_process"; +import { execSync as nodeExecSync, exec as nodeExec } from "child_process"; import { existsSync, rmSync, writeFileSync } from "fs"; +import { logInfo, logVerbose } from "./log"; -export function clangGetAstJson(headerPath: string): any[] { - const cmd = "clang -Xclang -ast-dump=json -fsyntax-only " + headerPath; - const result = execSync(cmd).toString(); - return JSON.parse(result).inner; +const includeDirArgs: string[] = []; + +export function addIncludeDir(dir: string) { + includeDirArgs.push(`-I${dir}`); +} + +export const execSync: typeof nodeExecSync = (command: string, ...args: any): any => { + logVerbose("execSync", command); + return nodeExecSync(command, ...args); +}; + +export async function execLargeJSON(command: string): Promise { + logVerbose("execLargeJSON", command); + const tmpFileName = `./exec_tmp_${Date.now()}_${Math.floor(Math.random() * 9999)}`; + + nodeExecSync(command + " > " + tmpFileName); + const json = await Bun.file(tmpFileName).json(); + rmSync(tmpFileName); + + return json; +} + +export async function clangGetAstJson(headerPath: string): Promise { + const cmd = `clang -Xclang -ast-dump=json -fsyntax-only ${includeDirArgs.join(" ")} ` + headerPath; + + logInfo(`clangGetAstJson headerPath="${headerPath}" command="${cmd}"`); + + const result = await execLargeJSON(cmd); + return result.inner; } export function clangCompileAndRunReadOut(code: string) { - execSync("clang -x c - -o ./generate-bindings_tmp_exec", { + const command = `clang ${includeDirArgs.join(" ")} -x c++ - -o ./generate-bindings_tmp_exec`; + if (command === "clang -I/Users/work_vk/Desktop/Dev/personal/bun-ffi-gen/include -x c++ - -o ./generate-bindings_tmp_exec") { + execSync("clang -I/Users/work_vk/Desktop/Dev/personal/bun-ffi-gen/include -x c++ - -E > aaa", { + input: code, + stdio: "pipe", + }); + } + execSync(command, { input: code, + stdio: "pipe", }); return execSync("./generate-bindings_tmp_exec").toString(); } export async function clangClean() { + logInfo("clangClean"); + if (existsSync("./generate-bindings_tmp_exec")) { rmSync("./generate-bindings_tmp_exec"); } @@ -27,11 +63,14 @@ export class ClangTypeInfoCache { offsetOf: Record = {}; async save() { + logInfo("ClangTypeInfoCache.save"); writeFileSync(this.cacheFilePath + "_sizeof.json", JSON.stringify(this.sizeOf)); writeFileSync(this.cacheFilePath + "_offsetof.json", JSON.stringify(this.offsetOf)); } static async create(cacheFilePath: string) { + logInfo(`ClangTypeInfoCache.create cacheFilePath="${cacheFilePath}"`); + let sizeOf = {}; if (await Bun.file(cacheFilePath + "_sizeof.json").exists()) { sizeOf = await Bun.file(cacheFilePath + "_sizeof.json").json(); @@ -84,7 +123,9 @@ export function clangGetOffsetOf(headerPath: string, cTypeName: string, fieldNam #include int main() { - printf("[ %lu ]", offsetof(${cTypeName}, ${fieldName})); + printf("[ %lu ]", + ((size_t)&(reinterpret_cast<${cTypeName}*>(0)->${fieldName})) + ); return 0; } `; diff --git a/src/gen.ts b/src/gen.ts index f6764df..396bafa 100644 --- a/src/gen.ts +++ b/src/gen.ts @@ -1,3 +1,4 @@ +import { logVerbose } from "./log"; import type { ParsedClangAstItem, ParsedClangAstItem_Alias, @@ -155,6 +156,7 @@ export class CodeGen { type Pointer as BunPointer, read as bunRead, suffix as bunSuffix, + toBuffer as bunToBuffer, } from "bun:ffi";` ); } @@ -170,15 +172,16 @@ type _PartialStructArg = { T[P] extends PtrT ? T[P] : T[P] extends ConstPtrT ? T[P] : T[P] extends CString ? T[P] : + T[P] extends Buffer ? T[P] : T[P] extends object ? _PartialStructArg : T[P]; }; const Pointer = BunFFIType.ptr; type Pointer = BunPointer | null; -type ConstPtrT = (Pointer | TypedArray | Buffer) & { __type: T, __const_ptr: true }; -type PtrT = (Pointer | TypedArray | Buffer) & { __type: T, __const_ptr: false }; -type TypedArrayPtr = (TypedArray | Buffer) & { __type: T, __const_ptr: any }; +type ConstPtrT = (Pointer | NodeJS.TypedArray | Buffer) & { __type: T, __const_ptr: true }; +type PtrT = (Pointer | NodeJS.TypedArray | Buffer) & { __type: T, __const_ptr: false }; +type TypedArrayPtr = (NodeJS.TypedArray | Buffer) & { __type: T, __const_ptr: any }; export const NULL = null as any as Pointer & { __type: any, __const_ptr: any }; @@ -194,7 +197,7 @@ export function bunReadArray(from: Pointer | TypedArrayPtr, offset: number } export function alloc_CString(str: string) { - return new BunCString(bunPtr(Buffer.from(str + "\\0"))); + return new BunCString(bunPtr(Buffer.from(str + "\\0")) as any); } export function alloc_opaque_pointer(x: Pointer, buffer?: Buffer): TypedArrayPtr { @@ -237,10 +240,10 @@ export function alloc_opaque_pointer(x: Pointer, buffer?: Buffer): TypedArrayPtr if (this.opts.readers) { this.writeLn(out, `export function read_${name}(from: BunPointer, offset: number): ${tsType} {`); if (d.ffiType === "BunFFIType.cstring") { - this.writeLn(out, `return new BunCString(bunRead.ptr(from, offset));`, 1); + this.writeLn(out, `return new BunCString(bunRead.ptr(from, offset) as any);`, 1); } else { const readFn = this.mapFFITypeToReadFn(d.ffiType); - this.writeLn(out, `return bunRead.${readFn}(from, offset);`, 1); + this.writeLn(out, `return bunRead.${readFn}(from, offset) as any;`, 1); } this.writeLn(out, `}`); } @@ -307,6 +310,9 @@ export function alloc_opaque_pointer(x: Pointer, buffer?: Buffer): TypedArrayPtr this.writeLn(out, `${fieldName} = ${f.value},`, 1); } this.writeLn(out, `}`); + for (const [fieldName, f] of d.fields) { + this.writeLn(out, `export const ${fieldName} = ${name}.${fieldName};`, 0); + } if (this.opts.readers) { this.writeLn(out, `export function read_${name}(from: BunPointer, offset: number): ${name} {`); @@ -321,6 +327,11 @@ export function alloc_opaque_pointer(x: Pointer, buffer?: Buffer): TypedArrayPtr } generateAlias(out: string[], name: string, d: ParsedClangAstItem_Alias) { + if (d.noEmit) { + logVerbose("skip noEmit alias", d); + return; + } + this.writeLn(out, `export type ${name} = ${d.aliasTo.name};`); if (this.opts.readers) { @@ -352,7 +363,7 @@ export function alloc_opaque_pointer(x: Pointer, buffer?: Buffer): TypedArrayPtr if (funcDeclCode instanceof Error) { } else { this.writeLn(out, `export function read_${name}(from: BunPointer, offset: number): ${name} {`); - this.writeLn(out, `const ptr = bunRead.ptr(from, offset);`, 1); + this.writeLn(out, `const ptr = bunRead.ptr(from, offset) as any as BunPointer;`, 1); this.writeLn(out, `return BunCFunction({`, 1); this.writeLn(out, `ptr,`, 2); this.writeLn(out, `...${funcDeclCode},`, 2); @@ -396,7 +407,10 @@ export function alloc_opaque_pointer(x: Pointer, buffer?: Buffer): TypedArrayPtr } inlineTsType(d: ParsedClangAstItem): string { - if (d.name) return d.name; + if (d.type === "static_array" || d.type === "union") { + return `Buffer`; + } + if (!(d as any).noEmit && d.name) return d.name; switch (d.type) { case "alias": return this.inlineTsType(d.aliasTo); @@ -444,6 +458,11 @@ export function alloc_opaque_pointer(x: Pointer, buffer?: Buffer): TypedArrayPtr if (f.valueType.type === "pointer") { this.writeLn(out, `${f.name}: read_opaque_pointer(from, offset + ${f.offset}) as any,`, 2); } else { + if (f.valueType.type === "union" || f.valueType.type === "static_array") { + this.writeLn(out, `${f.name}: bunToBuffer(from, offset + ${f.offset}, ${f.valueType.size}),`, 2); + continue; + } + if (!f.valueType.name) { throw new Error("bad value type"); } @@ -460,6 +479,10 @@ export function alloc_opaque_pointer(x: Pointer, buffer?: Buffer): TypedArrayPtr if (f.valueType.type === "pointer") { this.writeLn(out, `x.${f.name} !== undefined && write_opaque_pointer(x.${f.name}, buffer, offset + ${f.offset});`, 2); } else { + if (f.valueType.type === "union" || f.valueType.type === "static_array") { + this.writeLn(out, `x.${f.name} !== undefined && buffer.copy(new Uint8Array(x.${f.name}), offset + ${f.offset}, 0, ${f.valueType.size});`, 1); + continue; + } if (!f.valueType.name) { throw new Error("bad value type"); } diff --git a/src/log.ts b/src/log.ts new file mode 100644 index 0000000..f9d0e30 --- /dev/null +++ b/src/log.ts @@ -0,0 +1,15 @@ +export enum LogLevel { + none = 0, + info = 1, + verbose = 2, +} + +export let logLevel = LogLevel.info; + +export function logVerbose(...args: any[]) { + if (logLevel === LogLevel.verbose) console.log("[verbose]", ...args); +} + +export function logInfo(...args: any[]) { + if (logLevel >= LogLevel.info) console.log("[info]", ...args); +} diff --git a/src/parser.ts b/src/parser.ts index 4c5f497..782ca0a 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -1,6 +1,7 @@ import { basename } from "path"; import { POINTER_SIZE } from "./constants"; import { ClangTypeInfoCache, clangGetOffsetOf, clangGetSizeOf } from "./clang"; +import { logInfo, logVerbose } from "./log"; export type ParsedClangAstItem = | ParsedClangAstItem_Enum @@ -9,7 +10,9 @@ export type ParsedClangAstItem = | ParsedClangAstItem_Builtin | ParsedClangAstItem_FuncDecl | ParsedClangAstItem_FuncPointer - | ParsedClangAstItem_Struct; + | ParsedClangAstItem_Struct + | ParsedClangAstItem_StaticArray + | ParsedClangAstItem_Union; export type ParsedClangAstItem_Enum_FieldValue = | { @@ -35,10 +38,17 @@ export type ParsedClangAstItem_Enum = { export type ParsedClangAstItem_Alias = { type: "alias"; size: number; + noEmit?: boolean; name?: string; aliasTo: ParsedClangAstItem; }; +export type ParsedClangAstItem_LazyAlias = { + type: "lazy_alias"; + name: string; + aliasTo: string; +}; + export type ParsedClangAstItem_Pointer = { type: "pointer"; size: number; @@ -82,6 +92,14 @@ export type ParsedClangAstItem_Struct = { fields: ParsedClangAstItem_Struct_Field[]; }; +export type ParsedClangAstItem_StaticArray = { + type: "static_array"; + length: number; + size: number; + name: string; + itemType: ParsedClangAstItem; +}; + export type ParsedClangAstItem_FuncPointer = { type: "func_pointer"; size: number; @@ -89,6 +107,13 @@ export type ParsedClangAstItem_FuncPointer = { decl: ParsedClangAstItem_FuncDecl; }; +export type ParsedClangAstItem_Union = { + type: "union"; + size: number; + name?: string; + variants: ParsedClangAstItem_Struct_Field[]; +}; + export type ParsedClangAstResult = { decls: Map; ptrTypeSymbols: Set; @@ -104,16 +129,33 @@ export function parseClangAst(astJson: any[], headerFilePath: string, clangTypeI funcTypeSymbols: new Set(), }; + const lazyAliases = new Map(); + function emitDecl(name: string, item: ParsedClangAstItem) { if (result.decls.has(name)) { debugger; console.warn("overwrite decl name=", name, "item=", item); } result.decls.set(name, item); + + // resolve lazy aliases + if (lazyAliases.has(name)) { + const la = lazyAliases.get(name)!; + lazyAliases.delete(name); + emitDecl(la.name, { + type: "alias", + size: item.size, + name: la.name, + aliasTo: item, + }); + } return item; } function aliasDecl(aliasTo: ParsedClangAstItem): ParsedClangAstItem { + if (aliasTo.type === "alias" && aliasTo.noEmit) { + return aliasTo.aliasTo; + } return { type: "alias", name: aliasTo.name, @@ -125,7 +167,7 @@ export function parseClangAst(astJson: any[], headerFilePath: string, clangTypeI function getDecl(name: string, makeAlias: boolean): ParsedClangAstItem { if (!result.decls.has(name)) { debugger; - throw new Error("decl not found"); + throw new Error(`decl not found "${name}"`); } const t = result.decls.get(name)!; if (makeAlias) return aliasDecl(t); @@ -159,11 +201,50 @@ export function parseClangAst(astJson: any[], headerFilePath: string, clangTypeI const findDeclOrThrow = (...args: Parameters): ParsedClangAstItem => { const found = findDecl(...args); - if (!found) throw new Error("not found"); + if (!found) throw new Error(`not found "${JSON.stringify(args)}"`); return found; }; + function parseUnionDecl(unionDecl: any): Omit { + const variants: ParsedClangAstItem_Union["variants"] = []; + + for (const field of unionDecl.inner) { + const fieldType = extractQualTypeOrPtr(field.type.qualType); + const fieldName = field.name; + variants.push({ + name: fieldName, + size: fieldType.size, + offset: 0, + valueType: fieldType, + }); + } + + return { + type: "union", + variants, + }; + } + function extractQualTypeOrPtr(qualType: string): ParsedClangAstItem { + // static array definition + if (qualType.endsWith("]")) { + const staticArrayMatch = qualType.trim().match(/^([\w\W]+)\[(\w+)\]$/); + if (!staticArrayMatch) { + debugger; + throw new Error(`failed match static array type "${qualType}"`); + } + const baseItemName = staticArrayMatch[1].trim(); + const length = Number(staticArrayMatch[2]); + const itemType = extractQualTypeOrPtr(baseItemName); + return { + type: "static_array", + name: qualType, // TODO probably bad + length, + itemType, + size: itemType.size * length, + }; + } + if (qualType === "const char *") { return findDeclOrThrow({ name: "CString" }, false); } @@ -172,8 +253,8 @@ export function parseClangAst(astJson: any[], headerFilePath: string, clangTypeI let is_const = false; let isPtr = false; - const constPtrMatch = (qualType as string).match(/^const (?:struct\s)?(\w+) \*$/); - const ptrMatch = (qualType as string).match(/^(?:struct\s)?(\w+) \*$/); + const constPtrMatch = qualType.match(/^const (?:struct\s)?(\w+) \*$/); + const ptrMatch = qualType.match(/^(?:struct\s)?([\w\W\s]+)\s?\*(?:const)?$/); if (constPtrMatch) { ptrBaseName = constPtrMatch[1]; @@ -207,12 +288,28 @@ export function parseClangAst(astJson: any[], headerFilePath: string, clangTypeI emitBuiltinTypes(result); - for (const statement of astJson) { + for (let statementIndex = 0; statementIndex < astJson.length; ++statementIndex) { + const statement = astJson[statementIndex]; + // if (statement.name === "WGPUQuerySet") debugger; if (filterStatement(statement, headerFileBaseName)) continue; // enum if (statement.kind === "EnumDecl") { + CDECL_STYLE: if (!statement.name) { + const nextStatement = astJson[statementIndex + 1]; + if (nextStatement?.kind === "TypedefDecl") { + if (nextStatement.range?.begin.line <= statement.loc.line && nextStatement.range?.begin.col <= statement.loc.col) { + statement.name = nextStatement.name; + ++statementIndex; + break CDECL_STYLE; + } + } + debugger; + logVerbose(`unknown unnamed EnumDecl ${JSON.stringify(statement)}`); + continue; + } + const enumItem: ParsedClangAstItem_Enum = { type: "enum", size: 4, @@ -220,10 +317,29 @@ export function parseClangAst(astJson: any[], headerFilePath: string, clangTypeI fields: new Map(), }; + let enumValueCounter: undefined | "no-counter" | number = undefined; for (const item of statement.inner) { const itemName = item.name; - const value = parseEnumValue(item.inner); - enumItem.fields.set(itemName, value); + if (item.kind === "FullComment") continue; + if (item.inner) { + const value = parseEnumValue(item.inner); + if (value.type === "int") { + enumValueCounter = Number(value.value); + } else { + enumValueCounter = "no-counter"; + } + enumItem.fields.set(itemName, value); + } else { + if (enumValueCounter === "no-counter") { + throw new Error(`enum with mixed implicit & explicit declarations ${JSON.stringify(statement)}`); + } + if (enumValueCounter === undefined) enumValueCounter = 0; + enumItem.fields.set(itemName, { + type: "int", + value: `${enumValueCounter}`, + }); + enumValueCounter++; + } } emitDecl(statement.name, enumItem); @@ -260,9 +376,44 @@ export function parseClangAst(astJson: any[], headerFilePath: string, clangTypeI const structSize = clangGetSizeOf(headerFilePath, name, clangTypeInfoCache); const fields: ParsedClangAstItem_Struct_Field[] = []; - for (const item of statement.inner) { + for (let itemIndex = 0; itemIndex < statement.inner.length; ++itemIndex) { + const item = statement.inner[itemIndex]; + + if (item.kind === "FullComment") { + continue; + } + + if (item.tagUsed === "union" && item.kind === "RecordDecl") { + const unionFieldType = parseUnionDecl(item); + const nextItem = statement.inner[itemIndex + 1]; + + if (!item.name && nextItem?.kind === "FieldDecl" && nextItem.range?.begin.line <= item.loc.line && nextItem.range?.begin.col <= item.loc.col) { + item.name = nextItem.name; + ++itemIndex; + } + + const fieldOffset = clangGetOffsetOf(headerFilePath, name, item.name, clangTypeInfoCache); + const fieldSize = clangGetSizeOf(headerFilePath, `(sizeof ((${name}*)0)->${item.name})`, clangTypeInfoCache); + + fields.push({ + name: item.name, + size: fieldSize, + offset: fieldOffset, + valueType: { + ...unionFieldType, + size: fieldSize, + }, + }); + continue; + } + if (item.kind !== "FieldDecl") { debugger; + throw new Error(`unknown field kind in struct ${JSON.stringify(item)}`); + } + + if (!item.name) { + throw new Error(`unknown field in struct ${JSON.stringify(item)}`); } const fieldName = item.name; @@ -289,23 +440,6 @@ export function parseClangAst(astJson: any[], headerFilePath: string, clangTypeI continue; } - // opque pointer type - if (statement.kind === "TypedefDecl" && statement.type.qualType.startsWith("struct ") && statement.type.qualType.endsWith("Impl *")) { - // TODO: base type - const pointerItem: ParsedClangAstItem_Pointer = { - type: "pointer", - is_const: false, - size: POINTER_SIZE, - name: statement.name, - }; - - // debugger; - - emitDecl(statement.name, pointerItem); - - continue; - } - // func callback type if ( statement.kind === "TypedefDecl" && @@ -386,6 +520,23 @@ export function parseClangAst(astJson: any[], headerFilePath: string, clangTypeI continue; } + // opque pointer type + if (statement.kind === "TypedefDecl" && statement.type.qualType.endsWith(" *")) { + // TODO: base type + const pointerItem: ParsedClangAstItem_Pointer = { + type: "pointer", + is_const: false, + size: POINTER_SIZE, + name: statement.name, + }; + + // debugger; + + emitDecl(statement.name, pointerItem); + + continue; + } + // func decl if (statement.kind === "FunctionDecl") { const declName = statement.name; @@ -418,6 +569,49 @@ export function parseClangAst(astJson: any[], headerFilePath: string, clangTypeI continue; } + + if (statement.kind === "TypedefDecl" && statement.type.qualType.endsWith(" *")) { + // TODO: base type + const pointerItem: ParsedClangAstItem_Pointer = { + type: "pointer", + is_const: false, + size: POINTER_SIZE, + name: statement.name, + }; + + // debugger; + + emitDecl(statement.name, pointerItem); + + continue; + } + + if (statement.kind === "TypedefDecl") { + if (statement.type?.qualType) { + try { + const aliasType = extractQualTypeOrPtr(statement.type?.qualType); + emitDecl(statement.name, { + name: statement.name, + type: "alias", + size: aliasType.size, + aliasTo: aliasType, + }); + continue; + } catch {} + } + if (statement.type?.qualType.startsWith("struct ")) { + const structAliasOrDecl = statement.type.qualType.substr("struct ".length); + lazyAliases.set(statement.name, { + type: "lazy_alias", + aliasTo: structAliasOrDecl, + name: statement.name, + }); + continue; + } + } + + logInfo(`unknown statement, skipping`, statement); + debugger; } return result; @@ -467,26 +661,27 @@ function parseEnumValue(ast: any): ParsedClangAstItem_Enum_FieldValue { } if (ast.kind === "BinaryOperator") { - if (ast.opcode === "<<") { - const a = parseEnumValue(ast.inner[0]); - const b = parseEnumValue(ast.inner[1]); - - return { type: "expr", value: `(${a.value} << ${b.value})` }; - } + const a = parseEnumValue(ast.inner[0]); + const b = parseEnumValue(ast.inner[1]); - if (ast.opcode === "|") { - const a = parseEnumValue(ast.inner[0]); - const b = parseEnumValue(ast.inner[1]); + return { type: "expr", value: `((${a.value}) ${ast.opcode} (${b.value}))` }; + } - return { type: "expr", value: `(${a.value} | ${b.value})` }; - } + if (ast.kind === "UnaryOperator") { + const a = parseEnumValue(ast.inner[0]); + return { type: "expr", value: `(${ast.opcode} ${a.value})` }; } if (ast.kind === "DeclRefExpr") { return { type: "alias", value: ast.referencedDecl.name }; } - throw new Error("unknown enum value"); + if (ast.kind === "ParenExpr") { + const a = parseEnumValue(ast.inner[0]); + return { type: "expr", value: `(${a.value})` }; + } + + throw new Error(`unknown enum value "${JSON.stringify(ast)}"`); } function emitBuiltinTypes(out: ParsedClangAstResult) { @@ -499,6 +694,17 @@ function emitBuiltinTypes(out: ParsedClangAstResult) { }); } + function emitAlias(name: string, aliasTo: string, noEmit?: "no-emit") { + const foundAlias = out.decls.get(aliasTo)!; + out.decls.set(name, { + type: "alias", + aliasTo: foundAlias, + size: foundAlias.size, + noEmit: !!noEmit, + name, + }); + } + emitSimple("int8_t", 1, "BunFFIType.int8_t"); emitSimple("int16_t", 2, "BunFFIType.int16_t"); emitSimple("int32_t", 4, "BunFFIType.int32_t"); @@ -520,4 +726,25 @@ function emitBuiltinTypes(out: ParsedClangAstResult) { emitSimple("double", 8, "BunFFIType.double"); emitSimple("opaque_pointer", POINTER_SIZE, "BunFFIType.pointer"); + + // alias + emitAlias("char", "int8_t", "no-emit"); + emitAlias("unsigned char", "uint8_t", "no-emit"); + emitAlias("int", "int32_t", "no-emit"); + emitAlias("short", "int16_t", "no-emit"); + emitAlias("unsigned short", "uint16_t", "no-emit"); + emitAlias("unsigned int", "uint32_t", "no-emit"); + emitAlias("long long", "int64_t", "no-emit"); + emitAlias("unsigned long long", "uint64_t", "no-emit"); + emitAlias("unsigned long", "uint64_t", "no-emit"); + + emitAlias("u_int8_t", "uint8_t"); + emitAlias("u_int16_t", "uint16_t"); + emitAlias("u_int32_t", "uint32_t"); + emitAlias("u_int64_t", "uint64_t"); + + emitAlias("__int8_t", "int8_t"); + emitAlias("__int16_t", "int16_t"); + emitAlias("__int32_t", "int32_t"); + emitAlias("__int64_t", "int64_t"); } diff --git a/tsconfig.json b/tsconfig.json index bbcb559..79c022e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,16 +5,17 @@ "target": "ESNext", "module": "ESNext", "moduleDetection": "force", - "jsx": "react-jsx", "allowJs": true, "outDir": "lib", // Bundler mode "moduleResolution": "bundler", - "allowImportingTsExtensions": true, + // "allowImportingTsExtensions": true, "verbatimModuleSyntax": true, - "noEmit": true, + // "noEmit": true, + "declaration": true, + "sourceMap": true, // Best practices "strict": true, @@ -29,5 +30,6 @@ "types": [ "bun-types" // add Bun global ] - } + }, + "include": ["src"] }