Skip to main content
本文档面向 Bun 的维护者和贡献者,描述内部实现细节。
绑定生成器扫描 *.bind.ts 文件以查找函数和类定义,并生成胶水代码以实现 JavaScript 与原生代码之间的互操作。 还有其他代码生成器和系统可以实现类似目的;以下系统最终都将被此系统取代:
  • “Classes 生成器”,转换 *.classes.ts 以支持自定义类。
  • “JS2Native”,允许从 src/js 到原生代码的临时调用。

在 Rust 中创建 JS 函数

给定一个实现函数的文件,例如 add
src/jsc/bindgen_test.rs
然后使用 .bind.ts 文件描述 API 模式。绑定文件应放在 Rust 文件旁边。
src/jsc/bindgen_test.bind.ts
该函数声明等价于:
代码生成器会发出一个 C++ thunk,用于验证和强制转换 JS 参数,然后调用 Rust 实现。在 Rust 端,生成的模块可通过 crate::r#gen::<basename> 访问(对于 bindgen_test.bind.ts,即 crate::r#gen::bindgen_test)。要构造包装原生实现的 JSFunction,使用 generated::create_add_callback(global)
src/js/ 中的 JS 文件中,$bindgenFn("bindgen_test.bind.ts", "add") 返回实现句柄。 导出的 bindgen 函数在 Rust 端使用蛇形命名法(requiredAndOptionalArgrequired_and_optional_arg),生成的回调构造函数遵循相同的约定(create_required_and_optional_arg_callback)。

字符串

要接收字符串,使用 t.DOMStringt.ByteStringt.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 键声明函数的多个变体(也称为重载)。
每个变体对应一个带编号的 Rust 函数:

t.dictionary

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

枚举

t.stringEnum 创建一个 WebIDL 枚举并为其生成一个新的枚举类型。 fmt_jsc.bind.ts / bun:internal-for-testingstringEnum 的示例:
在 Rust 端,该枚举镜像为一个 #[repr(u8)] 枚举。Bindgen 在发出 C++ enum class 之前按字母顺序对 t.stringEnum 值排序,因此判别值必须与生成的头文件顺序一致,而不是 .bind.ts 声明顺序:
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>
根据类型不同,还有更多属性可用。详情请参见自动补全中的类型定义。这三个属性中只能应用一个,且必须最后应用。

整数属性

整数类型可以使用 clampenforceRange 自定义溢出行为:
Node.js 的验证函数如 validateIntegervalidateNumber 也可使用。在实现 Node.js API 时使用这些函数,以使错误信息与 Node 完全一致。 与源于 WebIDL 的 enforceRange 不同,validate* 函数对接受的输入更为严格。例如,Node 的数字验证器检查 typeof value === 'number',而 WebIDL 使用 ToNumber 进行有损转换。

回调

TODO

TODO