> ## Documentation Index
> Fetch the complete documentation index at: https://bun.zhcndoc.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Bindgen

> Bun 的 Bindgen

<Note>本文档面向 Bun 的维护者和贡献者，描述内部实现细节。</Note>

这个在 2024 年 12 月引入代码库的新绑定生成器，会扫描 `*.bind.ts` 文件以查找函数和类的定义，并生成 JavaScript 与原生代码之间的胶水代码以实现互操作。

目前还有其他代码生成器和系统实现类似功能，以下这些将最终被完全淘汰并由此生成器取代：

* “Classes generator”，转换 `*.classes.ts` 用于自定义类。
* “JS2Native”，允许从 `src/js` 到原生代码的临时调用。

## 在 Zig 中创建 JS 函数

定义一个简单函数实现文件，比如 `add`

```zig src/jsc/math.zig theme={"theme":{"light":"github-light","dark":"dracula"}}
pub fn add(global: *jsc.JSGlobalObject, a: i32, b: i32) !i32 {
    return std.math.add(i32, a, b) catch {
        // 绑定函数可以返回 `error.OutOfMemory` 和 `error.JSError`。
        // 其他如来自 `std.math.add` 的 `error.Overflow` 必须转换。
        // 注意错误提示要明确。
        return global.throwPretty("整数相加时溢出", .{});
    };
}

const gen = bun.gen.math; // "math" 为此文件的基本名

const std = @import("std");
const bun = @import("bun");
const jsc = bun.jsc;
```

然后用 `.bind.ts` 文件描述 API schema。绑定文件位置与 Zig 文件并列。

```ts src/jsc/math.bind.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import { t, fn } from "bindgen";

export const add = fn({
  args: {
    global: t.globalObject,
    a: t.i32,
    b: t.i32.default(1),
  },
  ret: t.i32,
});
```

此函数声明等价于：

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
/**
 * 如果未提供参数则抛出异常。
 * 对超出范围的数字使用模运算进行环绕。
 */
declare function add(a: number, b: number = 1): number;
```

代码生成器会提供 `bun.gen.math.jsAdd`，它是原生函数实现。传递给 JavaScript 时，使用 `bun.gen.math.createAddCallback(global)`。`src/js/` 下的 JS 文件可以用 `$bindgenFn("math.bind.ts", "add")` 来获取该实现句柄。

## 字符串

接收字符串的类型有 [`t.DOMString`](https://webidl.spec.whatwg.org/#idl-DOMString)、[`t.ByteString`](https://webidl.spec.whatwg.org/#idl-ByteString) 和 [`t.USVString`](https://webidl.spec.whatwg.org/#idl-USVString)，它们直接映射到各自的 WebIDL 对应类型，转换逻辑略有不同。Bindgen 会在所有情况下将 BunString 传给原生代码。

不确定时，使用 DOMString。

`t.UTF8String` 可以代替 `t.DOMString`，但是会调用 `bun.String.toUTF8`。原生回调将获得传入的 `[]const u8`（WTF-8 编码数据），函数返回后释放该数据。

来自 WebIDL 规范的简要说明：

* ByteString 仅能包含有效的 latin1 字符。虽然 bun.String 很可能已是 8 位格式，但不能假设一定如此。
* USVString 不会包含无效的代理对，即文本能够正确用 UTF-8 表示。
* DOMString 最宽松，同时也是最推荐的方案。

## 函数变体

`variants` 可以指定多个变体（也叫重载）。

```ts src/jsc/math.bind.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import { t, fn } from "bindgen";

export const action = fn({
  variants: [
    {
      args: {
        a: t.i32,
      },
      ret: t.i32,
    },
    {
      args: {
        a: t.DOMString,
      },
      ret: t.DOMString,
    },
  ],
});
```

在 Zig 中，每个变体会有一个编号，基于 schema 中定义的顺序。

```zig theme={"theme":{"light":"github-light","dark":"dracula"}}
fn action1(a: i32) i32 {
  return a;
}

fn action2(a: bun.String) bun.String {
  return a;
}
```

## `t.dictionary`

`dictionary` 是 JavaScript 对象的定义，通常用于函数的输入参数。对于函数输出，通常声明一个类类型更聪明，可以添加方法和解构功能。

## 枚举

使用 [WebIDL 的枚举类型](https://webidl.spec.whatwg.org/#idl-enums)，可以使用：

* `t.stringEnum`：创建并代码生成一个新的枚举类型。
* `t.zigEnum`：基于已有代码库内的 Zig 枚举派生一个 bindgen 类型。

`fmt.zig` / `bun:internal-for-testing` 中使用 `stringEnum` 的示例：

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
export const Formatter = t.stringEnum("highlight-javascript", "escape-powershell");

export const fmtString = fn({
  args: {
    global: t.globalObject,
    code: t.UTF8String,
    formatter: Formatter,
  },
  ret: t.DOMString,
});
```

WebIDL 强烈建议使用 kebab-case（连接符命名）作为枚举值，以保持与现有 Web API 的一致性。

### 从 Zig 代码派生枚举

TODO: zigEnum

## `t.oneOf`

`oneOf` 是两个或以上类型的联合。在 Zig 中表示为 `union(enum)`。

TODO：

## 属性

有一组属性可链式调用到 `t.*` 类型上。所有类型共有：

* `.required`（仅用于字典参数）
* `.optional`（仅用于函数参数）
* `.default(T)`

当值为可选时，会被转换为 Zig 的可选类型。

根据类型不同，可用的属性更多。查看自动补全中的类型定义以获取更多详情。注意上述三个属性只能选一个，且必须写在末尾。

### 整数属性

整型类型允许用 `clamp` 或 `enforceRange` 自定义溢出行为

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
import { t, fn } from "bindgen";

export const add = fn({
  args: {
    global: t.globalObject,
    // 限制在 i32 范围内
    a: t.i32.enforceRange(),
    // 限制在 u16 范围
    b: t.u16,
    // 限制在任意范围，带默认值
    c: t.i32.enforceRange(0, 1000).default(5),
    // 限制在任意范围，或为 null
    d: t.u16.clamp(0, 10).optional,
  },
  ret: t.i32,
});
```

还有各种 Node.js 验证函数，如 `validateInteger`、`validateNumber` 等。实现 Node.js API 时应使用这些函数，以保证错误信息与 Node.js 完全一致。

与 WebIDL 的 `enforceRange` 不同， Node.js 的 `validate*` 函数对输入更严格。例如，Node 的数字验证会检查 `typeof value === 'number'`，而 WebIDL 使用 `ToNumber` 进行有损转换。

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
import { t, fn } from "bindgen";

export const add = fn({
  args: {
    global: t.globalObject,
    // 非数字则抛出
    a: t.f64.validateNumber(),
    // i32 范围有效
    a: t.i32.validateInt32(),
    // f64，且在安全整数范围内
    b: t.f64.validateInteger(),
    // f64，在指定范围内
    c: t.f64.validateNumber(-10000, 10000),
  },
  ret: t.i32,
});
```

## 回调

TODO

## 类

TODO
