使用 UniFFI 生成 Javascript 绑定

Firefox 支持使用 UniFFI 自动生成 Rust 组件的 JS 绑定。

工作原理

Rust crate 包含一个 UniFFI 定义语言 (UDL) 文件,用于描述要为其生成绑定的接口。

UniFFI 核心生成脚手架:从 UDL 文件生成充当 FFI 层的 Rust 代码。此层的所有函数都使用 C 调用约定,所有结构体都使用 C 布局,这是 FFI 互操作的事实标准。

位于 Firefox 源码树中的 uniffi-bindgen-gecko-js 工具生成两样东西

  • 脚手架代码的 JS 接口,使用 WebIDL

  • 一个使用脚手架提供绑定 API 的模块。

目前,这些生成的代码已签入到源代码控制中。我们正在开发一个系统,以避免这种情况,而是在构建时自动生成这些代码(请参阅 bugzilla 1756214)。

在使用 UniFFI 创建新绑定之前

在创建新绑定集之前,请牢记以下几点

  • UniFFI 的编写目的并非为了最大化性能。其代码效率足以处理许多用例,但在目前阶段,对于性能关键型组件,最好避免使用。

  • uniffi-bindgen-gecko-js 绑定以 chrome 权限运行。请确保这对于您的项目来说是可以接受的。

  • 只能通过 FFI 公开 Rust 类型的一个子集。请查看 UniFFI 手册 以了解哪些类型与 UniFFI 兼容。

如果以上任何一项对您的工作造成了阻碍,请考虑与 UniFFI 开发人员进一步讨论,看看我们是否可以支持您的项目。

  • 在 Matrix/Element 上的 #uniffi 频道与我们聊天

  • mozilla/uniffi 上提交问题

使用 UniFFI 创建新绑定

您可以查看此功能的使用示例:当 application-services 使用 Rust 替换 tabs js 同步引擎时

以下是如何使用 UniFFI 创建新绑定集的方法

  1. 使用 UniFFI 处理您的 crate(如果尚未处理)

    • 按照 UniFFI 用户指南 中的步骤向您的 crate 添加支持。

    • UDL 和 proc-macros 都受支持。

  2. 将您的 crate 添加为 Firefox 依赖项(如果尚未添加)

    • 如果代码存在于 mozilla-central 存储库中

      • 为 Rust crate 创建一个新目录

      • 编辑 toolkit/components/uniffi-bindgen-gecko-js/components/Cargo.toml 并添加对库路径的依赖项

    • 如果代码存在于外部存储库中

      • 编辑 toolkit/components/uniffi-bindgen-gecko-js/components/Cargo.toml 并添加对库 URL 的依赖项

      • 运行 mach vendor rust 以引入您的 Rust 代码

  3. 配置您的 crate(可选)

    • 编辑 toolkit/components/uniffi-bindgen-gecko-js/config.toml 并添加您的 crate 的条目。

  4. 添加您的 crate 的脚手架

    • 编辑 toolkit/components/uniffi-bindgen-gecko-js/components/lib.rs 并添加您的 crate 的 uniffi 脚手架的条目。

  5. 为您的 crate 生成绑定代码

    • 运行 ./mach uniffi generate

      • 将新生成的 Rust{udl-name}.sys.mjs 文件添加到 toolkit/components/uniffi-bindgen-gecko-js/components/moz.build

    • 然后只需将您的模块导入到您想要使用它的文件中,然后开始使用您的 API!

      来自 tabs 模块的示例

      ChromeUtils.defineESModuleGetters(lazy, {
        ...
        TabsStore: "resource://gre/modules/RustTabs.sys.mjs",
      });
      ...
      this._rustStore = await lazy.TabsStore.init(path);