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

# 绑定生成器

> Bun 的绑定生成器

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

绑定生成器扫描 `*.bind.ts` 文件以查找函数和类定义，并生成胶水代码以实现 JavaScript 与原生代码之间的互操作。

还有其他代码生成器和系统可以实现类似目的；以下系统最终都将被此系统取代：

* "Classes 生成器"，转换 `*.classes.ts` 以支持自定义类。
* "JS2Native"，允许从 `src/js` 到原生代码的临时调用。

## 在 Rust 中创建 JS 函数

给定一个实现函数的文件，例如 `add`：

```rust src/jsc/bindgen_test.rs theme={null}
use crate::{JSGlobalObject, JsResult};
use crate::r#gen::bindgen_test as generated;

pub fn add(global: &JSGlobalObject, a: i32, b: i32) -> JsResult<i32> {
    match a.checked_add(b) {
        Some(v) => Ok(v),
        None => {
            // 绑定函数可以直接传播内存不足和 JS 异常；
            // 其他失败（如整数溢出）必须转换为抛出的错误。
            // 记得要描述清楚。
            Err(global.throw_pretty(format_args!("Integer overflow while adding")))
        }
    }
}
```

然后使用 `.bind.ts` 文件描述 API 模式。绑定文件应放在 Rust 文件旁边。

```ts src/jsc/bindgen_test.bind.ts icon="https://mintcdn.com/span-inc/82N53aP7NFbaCVSl/icons/typescript.svg?fit=max&auto=format&n=82N53aP7NFbaCVSl&q=85&s=787d39d6a7d96d7f9540dc74344eba23" theme={null}
import { fn, t } from "bindgen";

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

该函数声明等价于：

```ts theme={null}
/**
 * 如果未提供参数则抛出异常。
 * 使用取模处理超出范围的数字。
 */
declare function add(a: number, b: number = -1): number;
```

代码生成器会发出一个 C++ thunk，用于验证和强制转换 JS 参数，然后调用 Rust 实现。在 Rust 端，生成的模块可通过 `crate::r#gen::<basename>` 访问（对于 `bindgen_test.bind.ts`，即 `crate::r#gen::bindgen_test`）。要构造包装原生实现的 `JSFunction`，使用 `generated::create_add_callback(global)`：

```rust theme={null}
use crate::r#gen::bindgen_test as generated;

let js_fn: JSValue = generated::create_add_callback(global);
```

在 `src/js/` 中的 JS 文件中，`$bindgenFn("bindgen_test.bind.ts", "add")` 返回实现句柄。

导出的 bindgen 函数在 Rust 端使用蛇形命名法（`requiredAndOptionalArg` → `required_and_optional_arg`），生成的回调构造函数遵循相同的约定（`create_required_and_optional_arg_callback`）。

## 字符串

要接收字符串，使用 [`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 在所有情况下都会将 `bun_core::String` 传递给原生代码。

如果拿不准，请使用 DOMString。

`t.UTF8String` 可替代 `t.DOMString`，但会主动转换为 UTF-8。原生回调接收一个 `&[u8]` 切片（WTF-8 数据），该切片在函数返回后释放。

来自 WebIDL 规范的简要说明：

* ByteString 只能包含有效的 latin1 字符。虽然不能假设 `bun_core::String` 已为 8 位格式，但极大概率如此。
* USVString 不包含无效的代理对，因此其文本可以正确用 UTF-8 表示。
* DOMString 是最宽松的，也是最推荐的策略。

## 函数变体

`variants` 键声明函数的多个变体（也称为重载）。

```ts theme={null}
import { fn, t } from "bindgen";

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

每个变体对应一个带编号的 Rust 函数：

```rust theme={null}
pub fn action1(a: i32) -> i32 {
    a
}

pub fn action2(a: bun_core::String) -> bun_core::String {
    a
}
```

## `t.dictionary`

`dictionary` 描述一个 JavaScript 对象，通常是函数输入。对于函数输出，推荐使用类类型，以便添加方法并支持解构。

## 枚举

`t.stringEnum` 创建一个 [WebIDL 枚举](https://webidl.spec.whatwg.org/#idl-enums)并为其生成一个新的枚举类型。

`fmt_jsc.bind.ts` / `bun:internal-for-testing` 中 `stringEnum` 的示例：

```ts theme={null}
export const Formatter = t.stringEnum("highlight-javascript", "highlight-javascript-redacted", "escape-powershell");

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

在 Rust 端，该枚举镜像为一个 `#[repr(u8)]` 枚举。Bindgen 在发出 C++ `enum class` 之前**按字母顺序对 `t.stringEnum` 值排序**，因此判别值必须与生成的头文件顺序一致，而不是 `.bind.ts` 声明顺序：

```rust theme={null}
pub mod js_bindings {
    #[repr(u8)]
    #[derive(Copy, Clone, Eq, PartialEq)]
    pub enum Formatter {
        EscapePowershell = 0,
        HighlightJavascript = 1,
        HighlightJavascriptRedacted = 2,
    }

    pub fn fmt_string(
        global: &JSGlobalObject,
        code: &[u8],
        formatter_id: Formatter,
    ) -> JsResult<bun_core::String> {
        // ...
    }
}
```

WebIDL 强烈建议枚举值使用 kebab-case，以便与现有 Web API 保持一致。

## `implNamespace`

在 `fn({...})` 上设置 `implNamespace: "foo"` 会将生成的调用路由到 `crate::<basename>::foo::fn_name`，而不是 `crate::<basename>::fn_name`。用于将相关绑定实现分组在子模块下。

## `t.oneOf`

`oneOf` 是两种或多种类型的联合。它表示为 Rust `enum`，每个成员类型对应一个变体。

## 属性

属性可以链式添加到 `t.*` 类型上。所有类型通用的属性：

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

当值为 `.optional` 时，降级为 Rust `Option<T>`：

```ts theme={null}
export const requiredAndOptionalArg = fn({
  args: {
    a: t.boolean,
    b: t.usize.optional,
    c: t.i32.enforceRange(0, 100).default(42),
    d: t.u8.optional,
  },
  ret: t.i32,
});
```

```rust theme={null}
pub fn required_and_optional_arg(a: bool, b: Option<usize>, c: i32, d: Option<u8>) -> i32 {
    // ...
}
```

根据类型不同，还有更多属性可用。详情请参见自动补全中的类型定义。这三个属性中只能应用一个，且必须最后应用。

### 整数属性

整数类型可以使用 `clamp` 或 `enforceRange` 自定义溢出行为：

```ts theme={null}
import { fn, t } 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),
    // 钳位到任意范围，或 None
    d: t.u16.clamp(0, 10).optional,
  },
  ret: t.i32,
});
```

Node.js 的验证函数如 `validateInteger` 和 `validateNumber` 也可使用。在实现 Node.js API 时使用这些函数，以使错误信息与 Node 完全一致。

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

```ts theme={null}
import { fn, t } from "bindgen";

export const add = fn({
  args: {
    global: t.globalObject,
    // 如果不是数字则抛出异常
    a: t.f64.validateNumber(),
    // 在 i32 范围内有效
    b: t.i32.validateInt32(),
    // 在安全整数范围内的 f64
    c: t.f64.validateInteger(),
    // 在给定范围内的 f64
    d: t.f64.validateNumber(-10000, 10000),
  },
  ret: t.i32,
});
```

## 回调

TODO

## 类

TODO
