将 Rust 程序编译为 WebAssembly 的知识与实践

举报
jcLee95 发表于 2023/06/15 00:04:36 2023/06/15
【摘要】 本文记叙如何将一个 Rust 项目编译成可执行的 WebAssembly 文件。
Rust 笔记、WebAssembly
将 Rust 程序编译为 WebAssembly 的知识与实践


【介绍】:本文记叙如何将一个 Rust 语言编译成可执行的 WebAssembly 文件。




WebAssembly 是一种低级的类汇编语言,它是一种可以在现代的网络浏览器中运行的新的编码方式,并且可以接近原生的性能运行。依据官网的介绍,WebAssembly(缩写为 Wasm)是 基于堆栈的虚拟机  二进制指令格式。Wasm被设计为编程语言的可移植编译目标,支持 客户端和服务器应用程序 在 Web 上的部署

通过 WebAssembly 技术,我们可以为 RustC / C ++Go 等多种语言提供一个编译目标,以便它们可以在 Web 上运行,并且是可以与 JavaScript 一起工作的。


WebAssembly 官方对于该技术的优势归纳为以下几个方面:高效、安全、开放。


Wasm堆栈机被设计为 以 大小 和 加载时间 有效的 二进制格式编码。WebAssembly 旨在利用各种平台上可用的通用硬件功能,以本机速度执行。


WebAssembly描述了一个内存安全的沙盒执行环境,甚至可以在现有的JavaScript虚拟机中实现。当嵌入到web中时,WebAssembly将实施浏览器的同源和权限安全策略。


WebAssembly 被设计成文本格式,用于调试、测试、实验、优化、学习、教学和手工编写程序。在网上查看Wasm模块的源代码时,将使用文本格式。同时,WebAssembly 被作为网络平台的一部分,其旨在维护 Web 的无版本、经过功能测试和向后兼容的特性。WebAssembly 模块将能够调入和调出 JavaScript 上下文,并通过可从 JavaScript 访问的相同 Web APIs 访问浏览器功能。WebAssembly 还支持 非web嵌入



本文针对 Web 前端 以及 NodeJS 或基于NodeJS 的桌面(如electron)或其它场景应用的开发人员进行讲解,假定已有 浏览器 或 NodeJS 的开发经验。


本文从零搭建一个 Rust 项目,在其中穿插讲解需要用到的一些知识,如 Rust 的 wask-pack 模块。然后在实际项目中简单将 Rust 项目编译好,分别讲述如何在 浏览器NodeJS 中运行它。



请参考博文 《开发环境搭建与 rust 工具介绍》,文本不再赘述。


新建 Rust 项目 hello-wasm

cargo new hello-wasm

进入该项目:

cd hello-wasm

可以看到有以下目录和文件:
在这里插入图片描述

 src 目录下,有一个名为 main.rs 的文件。我们可以使用 VScode 或者 Vim 等程序编辑它,修改为我们自己的代码。

vim src/main.rs

在这里插入图片描述
由于我们目标是写一个用于 npm 的模块,这个自动生成的代码没有任何作用的。

然后,我们另外编辑一个 src/lib.rs 文件:

# 此处假定当前位于项目根目录
# 如果使用 VSCode,则使用命令 code src/lib.rs
vim src/lib.rs

内容如下:

extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern {
    pub fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet(name: &str) {
    alert(&format!("Hello, {}!", name));
}


前一节的代码中用到了 wasm_bindgen 模块,该模块用于 Rust 与 JavaScript 交互。另外还有 wasm-pack 模块用于构建 wasm,它们都需要单独在项目中安装。你可以直接使用 cargo 工具添加:

cargo add wasm-pack
cargo add wasm_bindgen

完成后,在项目配置文件中增加依赖项的记录:

在这里插入图片描述

关于 添加 wasm-pack 模块 更详细的方法请参考 《3.2 将 wasm-pack 模块添加到你的 Rust 项目》 小节。

然后编辑 Cargo.toml  lib 部分,以告诉 Rust 为我们的包建立一个 cdylib 版本。添加以下内容:

[lib]
crate-type = ["cdylib", "rlib"]

在这里插入图片描述



在本文对应的实操,将会用到各种工具,由于不同的人习惯不一样,尤其是某些软件在官方不发布二进制文件仅仅发布源代码时,大家习惯于使用不同的社区构建版本。在这里博主我已经为读者提前下载好了各种工具,提供这些工具安装方式的介绍,这些工具都在我的资源上传区可以找到。rust 语言自生就需要各种依赖,如:

  • python 3 or 2.7
  • git
  • 一个C编译器 (为主机搭建时,cc就够了;交叉编译可能需要额外的编译器)
  • curl (在Windows上不需要)
  • pkg-config 如果您在Linux上编译并以Linux为目标
  • libiconv (已经包含在基于Debian的发行版的glibc中)

要构建Cargo,还需要OpenSSL(大多数Unix发行版上的 libssl-dev 或 openssl-devel)。如果从源代码构建LLVM,您将需要额外的工具:

  • g++, clang++, 或LLVM文档中列出的 MSVC 版本
  • ninja, 或 GNU make 3.81 或更新 (推荐Ninja,特别是在Windows上)
  • cmake 3.13.4 或更新
  • libstdc++-static 在一些Linux发行版上可能需要,比如Fedora和Ubuntu

这一节记录相关的一些工具是如何安装的。

Rust的MSVC版本还需要安装Visual Studio 2017(或更高版本),以便 rustc 可以使用其链接器。最简单的方法就是通过 Visual Studio 安装。
你可以访问 https://visualstudio.microsoft.com/zh-hans/thank-you-downloading-visual-studio/?sku=Community&channel=Release&version=VS2022&source=VSLandingPage&passive=false&cid=2030下载 Visual Studio 安装工具,然后选择安装 Visual Studio 生成工具安装:

在这里插入图片描述


MinGW(Minimalist GNU on Windows)也就是 GCC 的 Windows 版本,其中GNU 编译器集合包括 C、C++、 Objective-C, Fortran, Ada、Go 和 D,以及这些语言的库 (libstdc++,…),而 GCC 最初是作为 GNU 操作系统的编译器编写的。
针对 MSVC 的 Windows 平台(例如,您的目标三端in -msvc)要求cl.exe,可用并在 path 环境变量中。这通常出现在标准的Visual Studio 安装中,并且可以通过运行适当的开发人员工具 shell 来设置路径。
面向 MinGW 的 Windows平台(例如-gnu中的目标三端)要求cc在 path 环境变量中可用。推荐使用 Win-builds 安装系统的 MinGW-w64 发行版。您也可以通过 MSYS2 获得它。确保安装与你的 rustc 安装相对应的适当架构。来自老 MinGW 项目的 GCC 仅与 32位 rust编译器 兼容。

MinGW踩坑记录:安装构建rust后会提示没有 clang,其bin目录也确实没有这个文件。

在这里插入图片描述
然后通过它选择包完成安装。

你也可以自己在 https://sourceforge.net/projects/mingw-w64/files/mingw-w64/mingw-w64-release/ 上下载 MinGW-w64 的不同版本:

在这里插入图片描述

而 MinGW-w64 官方网站的地址是:http://mingw-w64.org ,你可以在 https://sourceforge.net/projects/mingw-w64/ 寻找下载链接。

choco

在 Windows 上安装 mingw 的另外一个方式是使用 choco 包管理工具,你需要单独安装该工具。然后:

choco install mingw

msys2

或者使用 MSYS2 ,下载会相对容易些。其官网为:https://www.msys2.org/。你也可以访问 Mingw-w64 的官网 https://www.mingw-w64.org/ 安装其它版本。

MSYS2踩坑记录:安装后会提示没有 clang,打开其目录,有一个clang64.exe和一个 clang32.exe。我尝试将它们改为 clang.exe 然后构建 rust,有弹框报错,不能处理文件。

安装完成后,将你的安装目录添加到系统的 path 环境变量中,然后运行:

msys2_shell -mingw64

以开 mingw64 窗口,在该窗口中输入以下命令:

# 更新软件包镜像(如果您全新安装了MSYS2,则可能需要)
pacman -Sy pacman-mirrors

在这里插入图片描述

# 安装Rust所需的构建工具。如果您正在构建32位编译器,那么请将下面的“x86_64”替换为“i686”。
# 如果您已经安装了Git、Python或CMake,并且在 PATH 中,您可以从这个列表中删除它们。
# 请注意,不要**使用“msys2”子系统中的“python2”、“cmake”和“ninja”包,这一点很重要。众所周知,使用这些包时,构建会失败。
pacman -S git \
            make \
            diffutils \
            tar \
            mingw-w64-x86_64-python \
            mingw-w64-x86_64-cmake \
            mingw-w64-x86_64-gcc \
            mingw-w64-x86_64-ninja

这写都将安装到 mingw 的目录下,建议如果已经独立按照就不需要再安装了。

安装完成以上这些后,在你的电脑上将可以导航到你的 Rust源代码,然后这样构建它:

python x.py setup user && python x.py build && python x.py install

如果内有安装,也不想使用 pacman 命令来安装。在 Windows也可以运行 winget 工具安装:

winget install -e Python.Python.3
winget install -e Kitware.CMake
winget install -e Git.Git

然后使用 pip 工具安装:

pip install setuptools-rust
pip install cryptography
pip install paramiko

LLVM

或者使用 LLVM,这个我尝试时是好用的。

关于在类 Unix 系统上安装

在 类 Unix 平台要求 cc 是 C 编译器。例如,这可以通过在 Linux 发行版上安装 cc/clang 和在 macOS 上安装 Xcode 来找到。


在 Windows 上使用 wasm-pack 构建 WebAssembly 时,需要 OpenSSL 库。请确保你的系统上已安装 OpenSSL。

a. 下载 OpenSSL:OpenSSL 官方不以二进制形式分发包,因此你无法下载官方版本的二进制安装包。
可以先将源码下载过来:

git clone https://github.com/openssl/openssl.git

你需要参考:https://github.com/openssl/openssl/blob/master/NOTES-WINDOWS.md进行构建。
社区提供了一些构建好的二进制版本,可以在社区的页面查看:
https://wiki.openssl.org/index.php/Binaries
(或者使用我准备地这个版本:

  1. 设置 OpenSSL 环境变量:如果已经安装了 OpenSSL,但 wasm-pack 无法找到它,您可以尝试手动设置 OpenSSL 的环境变量。

    a. 打开系统环境变量设置:在 Windows 11 上,点击 “开始” 按钮,搜索并打开 “编辑系统环境变量”。

    b. 点击 “环境变量” 按钮:在 “系统属性” 窗口中,点击 “环境变量” 按钮。

    c. 添加 OpenSSL 环境变量:在 “系统变量” 部分,点击 “新建” 按钮,并添加以下变量:

    • 变量名:OPENSSL_DIR
    • 变量值:OpenSSL 安装目录的路径(例如:D:\Program Files\OpenSSL

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ggx32EAJ-1685289910260)(null)]

    d. 点击 “确定” 保存设置,并关闭所有打开的窗口。

  2. 重新运行 cargo install wasm-pack:在设置完 OpenSSL 环境变量后,重新运行 cargo install wasm-pack 命令,看是否仍然出现错误。这样做会将 OpenSSL 的路径信息传递给构建过程,以便成功编译和安装 wasm-pack

如果上述步骤仍然无法解决问题,请确保您按照正确的顺序执行了上述步骤,并且 OpenSSL 的安装路径正确设置。如果问题仍然存在,请尝试使用较新版本的 OpenSSL 或 wasm-pack,以确保软件版本的兼容性。

如果问题仍然存在,请提供更多错误信息或运行命令时使用 RUST_BACKTRACE=1 环境变量,以便我能够更详细地了解问题并提供进一步的帮助。

Win32OpenSSL

Win32OpenSSL 也是一个社区预构建的 Windows 二进制版本:

https://slproweb.com/products/Win32OpenSSL.html 上找到你需要的安装包版本,然后安装到指定文件夹。

接着使用 Powershell 设置环境变量(依据你的安装位置修改):

# 设置 OpenSSL-Win64 目录
[System.Environment]::SetEnvironmentVariable('OPENSSL_DIR','D:\Program Files\OpenSSL-Win64','User')
# 设置 OpenSSL-Win64 的 bin 目录
[System.Environment]::SetEnvironmentVariable('OPENSSL_LIB_DIR','D:\Program Files\OpenSSL-Win64\bin','User')
# 设置 OpenSSL-Win64 的 include 目录
[System.Environment]::SetEnvironmentVariable('OPENSSL_INCLUDE_DIR','D:\Program Files\OpenSSL-Win64\include','User')

某些模块在构建时期用到 Perl 运行时。我已经为读者朋友准备好了一个 Perl 运行时,其下载地址为:

https://download.csdn.net/download/qq_28550263/87837427。下载好后可以自行安装。
在这里插入图片描述

安装完成后,需要将 Perl 的二进制文件目录添加到系统的环境变量,以方便我们和程序日后进行访问或调用 Perl 解释器:

比如我将 Strawberry 安装到 D 盘下,那么 Perl 的目录位于:D:\Strawberry\perl\bin。

 Windows 菜单中搜索 环境变量编辑器,将其单击打开:

在这里插入图片描述
点击 “环境变量”:

在这里插入图片描述

打开系统变量的 Path 变量名,点击新建,将我们刚刚的路径新增进去,然后保存退出。

你可以使用以下命令查看是否安装成功:

perl -v

如果看到版本号等信息,说明你已经在你的计算机上成功地部署了 Perl

在这里插入图片描述



【注意】:构建前你需要将 wasm-pack 的二进制目录注册到path环境变量。 windows 系统上如果一直使用的是安装到当前用户而非安装到系统, windows 系统上,wasm-pack 全局安装后,.wasm-pack位于 当前用户目录下的 AppData(通常为隐藏目录)下的Local目录下。 比如我的系统当前用户名为a2911,则对应 C:\Users\a2911\AppData\Local.wasm-pack。其中以 wasm-bindgen-cargo-install 开头的目录及其下的 bin 目录就是我们要添加到 Path 环境目录下的。比如我这里是 C:\Users\a2911\AppData\Local.wasm-pack\wasm-bindgen-cargo-install-0.2.86\bin

可能你对一些地方还不是很明白,不过作为快速上手,可以跟着本文先构建上面的 rust 项目为 npm 模块,这需要用到 wasm-pack CLI,可以参考 《3.3 安装 wasm-pack 的命令行工具》 进行安装。

安装后,回到本项目的根目录,使用该脚手架的 build 命令进行构建,格式如下:

wasm-pack build --target web

第一次执行这个命令会需要很长一段时间,尤其是第一次执行时,需要下载一些相关模块,需要耐心等待。如图:
build

一旦执行完成,可以在项目根目录下的 pkg 目录中找到为你生成的 npm模块,如图所示:

在这里插入图片描述
这个目录就是一个包含 xxx.wasmxxxx.wasm.d.tsxxxx.jsxxxx.d.ts 的 一个 npm包:

在这里插入图片描述


wasm-pack build 将做以下几件事:

  1. 编译Rust:将你的 Rust 代码编译成 WebAssembly。
  2. 生成JS接口:在编译好的 WebAssembly 代码基础上运行 wasm-bindgen,生成一个 JavaScript 文件将 WebAssembly 文件包装成一个模块以便 npm 能够识别它。
  3. 移入新建pkg目录:创建一个 pkg 文件夹并将 JavaScript 文件和生成的 WebAssembly 代码移到其中。
  4. 转换项目配置文件:读取你的 Cargo.toml 并生成相应的 package.json。
  5. 复制 readme.md 文件:复制你的 README.md (如果有的话) 到文件夹中。
  6. 结果:在 pkg 文件夹下得到了一个 npm 包。


依据该模块主页对自身的描述,该工具旨在成为 构建和使用 rust 生成的 WebAssembly 的 “一站式商店”(one-stop shop),你可以在 浏览器  Node.js 中使用 javascript 进行交互。

wasm-pack 帮助您 构建 rust 生成的 WebAssembly 包,您可以将其发布到 npm 注册表,或者在您已经使用的工作流中与任何 JavaScript 包一起使用,如 Webpack


和其它任意 Rust 模块一样,有两种方式添加该模块到你的项目中。第一种方式是直接使用 cargo 包管理工具:

cargo add wasm-pack

或者使用第二种方式,编辑项目的 Cargo.toml 文件,在模块依赖项处添加该行:

wasm-pack = "0.11.1"

【注意】:此项目需要 Rust 1.30.0 或更高版本。

其中右侧双引号中的是该模块的版本号,本文写作时最新的版本号为"0.11.1",读者可以指定自己尝试时的最新版本号。


wasm-pack 模块提供了一个命令行工具,你需要手动安装它。


你可以直接下载该初始化工具:
https://github.com/rustwasm/wasm-pack/releases/download/v0.11.1/wasm-pack-init.exe,这个工具仅支持 64 位系统。下载完成后运行,将显示一个命令行窗口并很快就能够完成,完成后退出即可。一旦安装完成,将可以在你的系统中使用 wasm-pack 命令。


如果你是Linux或者Linux的Windows子系统用户,请在您的终端中运行以下程序,然后按照屏幕上的说明安装wasm-pack:

curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

该命令的语法格式为:

wasm-pack [FLAGS] [OPTIONS] <SUBCOMMAND>

其中,FLAGS 表示标志。提供以下标志项:

命令 别名 描述
-h --help 打印帮助信息
-q --quiet 不输出信息到控制台
-V --version 打印版本信息
-v --verbose 日志详细程度基于使用的v的数量

其中,OPTIONS 表示选项。提供以下选项:

选项 描述
--log-level <log-level> 表示 wasm-pack 应记录的最大消息级别。 可能的值包括: info, warn, error。默认值为:info

其中,SUBCOMMAND 表示子命令。提供以下子命令:

子命令 描述
build 🏗️ 构建您的 npm 包!
help 打印该消息或给定子命令的帮助
login 👤 添加npm注册表用户帐户!(别名:adduseradd-user)
new 🐑 使用模板创建新项目
pack 🍱 为你的 npm包 创建一个 tar,但是不要发布!
publish 🎆 打包你的 npm包 并发布!

wasm-pack 模块在内部使用 env_logger 模块作为其日志器。要配置日志级别,请使用 RUST_LOG 环境变量。例如(powershell):

  • 仅仅在当前窗口中有效的设置:
$env:RUST_LOG="info"
  • 长期保存到当前用户的环境变量设置:
[System.Environment]::SetEnvironmentVariable('RUSTUP_LOG','info','User')

 《3.3.3 wasm-pack CLI 的用法解析》 小节中,我们介绍了 wasm-pack 提供的脚手架,其中有一个 new 命令也是可以用来创建 Rust 项目的。现在我们使用该项目来“实操”一下:

wasm-pack new hello-wasm

该命令创建了一个名为 hello-wasm 的项目,其中包含了以下子目录和文件:
在这里插入图片描述

该使用 wasm-pack 创建的项目配置文件 Cargo.toml 初始内容为:

[package]
name = "hello-wasm"
version = "0.1.0"
authors = ["your_user_name <xxxxxx+xxxxx@users.noreply.github.com>"]
edition = "2018"

[lib]
crate-type = ["cdylib", "rlib"]

[features]
default = ["console_error_panic_hook"]

[dependencies]
wasm-bindgen = "0.2.63"

# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
console_error_panic_hook = { version = "0.1.6", optional = true }

# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
# compared to the default allocator's ~10K. It is slower than the default
# allocator, however.
wee_alloc = { version = "0.4.5", optional = true }

[dev-dependencies]
wasm-bindgen-test = "0.3.13"

[profile.release]
# Tell `rustc` to optimize for small code size.
opt-level = "s"

其中默认为我们安装了 wasm-bindgen 模块,该模块用于 促进 Wasm模块  JavaScript之间的高级交互。


要在将 Rust 程序编译为 WebAssembly(Wasm),您可以按照以下步骤进行操作:

  1. 安装 Rust:首先,您需要安装 Rust 编程语言的工具链。您可以从 Rust 官方网站(https://www.rust-lang.org)下载并安装 Rust。

  2. 安装 wasm-pack:wasm-pack 是一个用于打包和构建 WebAssembly 的工具。您可以使用 Cargo(Rust 的包管理器)安装 wasm-pack。在命令行中运行以下命令来安装 wasm-pack:

    cargo install wasm-pack
    
  3. 创建 Rust 项目:在命令行中,进入您的 Rust 项目的根目录,并执行以下命令创建一个新的 Rust 项目:

    cargo new my_project
    
  4. 进入项目目录:使用 cd 命令进入新创建的项目目录:

    cd my_project
    
  5. 编写 Rust 代码:使用您喜欢的文本编辑器编写 Rust 代码。将您的 Rust 代码保存在 src 目录下的 .rs 文件中。

  6. 配置 Cargo.toml:在项目的根目录中,打开 Cargo.toml 文件,并添加以下内容来配置您的项目以构建为 WebAssembly:

    [lib]
    crate-type = ["cdylib"]
    
    [dependencies]
    wasm-bindgen = "0.2"
    
  7. 构建 WebAssembly:在命令行中,执行以下命令以构建 WebAssembly:

     wasm-pack build --scope xxxxx --target web
    

    这将使用 wasm-pack 将 Rust 项目构建为 WebAssembly 模块。生成的 WebAssembly 文件将位于 pkg 目录下。

  8. 集成 WebAssembly 到 Web 项目:将生成的 WebAssembly 文件(.wasm  .js 文件)复制到您的 Web 项目中,并通过 JavaScript 脚本加载和使用 WebAssembly。

这些步骤将帮助您在 Windows 11 上将 Rust 程序编译为 WebAssembly。请注意,要成功编译为 WebAssembly,您的 Rust 代码和相关依赖库需要与 WebAssembly 目标兼容。在开发过程中,您可能还需要学习和了解 wasm-bindgen,它是一个用于在 Rust 和 JavaScript 之间进行交互的工具库。


在运行 wasm-pack build 后,生成的 pkg 目录是用于存放 WebAssembly 包(Wasm 包)及其相关文件的目录。这个目录中的文件用于在 Web 环境中使用和调用生成的 WebAssembly 模块。先前我们已经展示了该项目的结构:

在这里插入图片描述

your_package_bg.wasm

这是编译生成的 WebAssembly 模块文件,其中包含了你的 Rust 代码编译后的机器码。它是使用 Rust 编写的代码的编译结果,可以在 Web 环境中加载和执行。

your_package_bg.wasm.d.ts

这是一个 TypeScript 类型定义文件,用于提供与 your_package_bg.wasm 文件交互的类型信息。它包含了与 WebAssembly 模块中导出函数的类型定义,以便在 TypeScript 代码中正确使用这些函数。

这些文件的结合使用,可以使你在 Web 环境中方便地使用 Rust 编写的 WebAssembly 模块。你可以使用 your_package.js 文件作为入口点,在你的 JavaScript 或 TypeScript 代码中调用和使用 WebAssembly 模块中的函数和对象。

比如在我们的示例中,该文件的内容为:

/* tslint:disable */
/* eslint-disable */
export const memory: WebAssembly.Memory;
export function greet(a: number, b: number): void;
export function __wbindgen_malloc(a: number): number;
export function __wbindgen_realloc(a: number, b: number, c: number): number;

your_package.js

这是与 WebAssembly 模块交互的 JavaScript 模块。它提供了与 WebAssembly 模块通信的接口,包括导入和导出函数,以及其他必要的功能。该文件提供了一个高级的 JavaScript API,使得在网页中可以方便地使用 WebAssembly 模块。

比如在我们的示例中,该文件的内容为:

let wasm;

const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );

if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); };

let cachedUint8Memory0 = null;

function getUint8Memory0() {
    if (cachedUint8Memory0 === null || cachedUint8Memory0.byteLength === 0) {
        cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer);
    }
    return cachedUint8Memory0;
}

function getStringFromWasm0(ptr, len) {
    ptr = ptr >>> 0;
    return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
}

let WASM_VECTOR_LEN = 0;

const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } );

const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
    ? function (arg, view) {
    return cachedTextEncoder.encodeInto(arg, view);
}
    : function (arg, view) {
    const buf = cachedTextEncoder.encode(arg);
    view.set(buf);
    return {
        read: arg.length,
        written: buf.length
    };
});

function passStringToWasm0(arg, malloc, realloc) {

    if (realloc === undefined) {
        const buf = cachedTextEncoder.encode(arg);
        const ptr = malloc(buf.length) >>> 0;
        getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf);
        WASM_VECTOR_LEN = buf.length;
        return ptr;
    }

    let len = arg.length;
    let ptr = malloc(len) >>> 0;

    const mem = getUint8Memory0();

    let offset = 0;

    for (; offset < len; offset++) {
        const code = arg.charCodeAt(offset);
        if (code > 0x7F) break;
        mem[ptr + offset] = code;
    }

    if (offset !== len) {
        if (offset !== 0) {
            arg = arg.slice(offset);
        }
        ptr = realloc(ptr, len, len = offset + arg.length * 3) >>> 0;
        const view = getUint8Memory0().subarray(ptr + offset, ptr + len);
        const ret = encodeString(arg, view);

        offset += ret.written;
    }

    WASM_VECTOR_LEN = offset;
    return ptr;
}
/**
* @param {string} name
*/
export function greet(name) {
    const ptr0 = passStringToWasm0(name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
    const len0 = WASM_VECTOR_LEN;
    wasm.greet(ptr0, len0);
}

async function __wbg_load(module, imports) {
    if (typeof Response === 'function' && module instanceof Response) {
        if (typeof WebAssembly.instantiateStreaming === 'function') {
            try {
                return await WebAssembly.instantiateStreaming(module, imports);

            } catch (e) {
                if (module.headers.get('Content-Type') != 'application/wasm') {
                    console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);

                } else {
                    throw e;
                }
            }
        }

        const bytes = await module.arrayBuffer();
        return await WebAssembly.instantiate(bytes, imports);

    } else {
        const instance = await WebAssembly.instantiate(module, imports);

        if (instance instanceof WebAssembly.Instance) {
            return { instance, module };

        } else {
            return instance;
        }
    }
}

function __wbg_get_imports() {
    const imports = {};
    imports.wbg = {};
    imports.wbg.__wbg_alert_8755b7883b6ce0ef = function(arg0, arg1) {
        alert(getStringFromWasm0(arg0, arg1));
    };

    return imports;
}

function __wbg_init_memory(imports, maybe_memory) {

}

function __wbg_finalize_init(instance, module) {
    wasm = instance.exports;
    __wbg_init.__wbindgen_wasm_module = module;
    cachedUint8Memory0 = null;


    return wasm;
}

function initSync(module) {
    if (wasm !== undefined) return wasm;

    const imports = __wbg_get_imports();

    __wbg_init_memory(imports);

    if (!(module instanceof WebAssembly.Module)) {
        module = new WebAssembly.Module(module);
    }

    const instance = new WebAssembly.Instance(module, imports);

    return __wbg_finalize_init(instance, module);
}

async function __wbg_init(input) {
    if (wasm !== undefined) return wasm;

    if (typeof input === 'undefined') {
        input = new URL('hello_wasm_bg.wasm', import.meta.url);
    }
    const imports = __wbg_get_imports();

    if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) {
        input = fetch(input);
    }

    __wbg_init_memory(imports);

    const { instance, module } = await __wbg_load(await input, imports);

    return __wbg_finalize_init(instance, module);
}

export { initSync }
export default __wbg_init;

your_package.d.ts

这是 TypeScript 类型定义文件,用于提供与 WebAssembly 模块交互的类型信息。它定义了与 JavaScript 模块中的函数和对象进行交互时使用的类型签名,以提供静态类型检查和类型提示的支持。

比如在我们的示例中,该文件的内容为:

/* tslint:disable */
/* eslint-disable */
/**
* @param {string} name
*/
export function greet(name: string): void;

export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;

export interface InitOutput {
  readonly memory: WebAssembly.Memory;
  readonly greet: (a: number, b: number) => void;
  readonly __wbindgen_malloc: (a: number) => number;
  readonly __wbindgen_realloc: (a: number, b: number, c: number) => number;
}

export type SyncInitInput = BufferSource | WebAssembly.Module;
/**
* Instantiates the given `module`, which can either be bytes or
* a precompiled `WebAssembly.Module`.
*
* @param {SyncInitInput} module
*
* @returns {InitOutput}
*/
export function initSync(module: SyncInitInput): InitOutput;

/**
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
* for everything else, calls `WebAssembly.instantiate` directly.
*
* @param {InitInput | Promise<InitInput>} module_or_path
*
* @returns {Promise<InitOutput>}
*/
export default function __wbg_init (module_or_path?: InitInput | Promise<InitInput>): Promise<InitOutput>;

README.md

这里再做一些补充说明。这里的 README.md 文件是 wasm-pack 自动地帮我们复制过来地,这就意味着我们只需要再原始的 Rust 项目中写好该模块的介绍。

package.json

这个用于 NodeJS 的项目配置文件是再原始 Rsut 项目配置文件 cargo.toml 的基础上进行生成的,并与之在一些基本信息上保持一致,比如相同的版本号,等等。

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。