- 适用于 JDK 23 的 GraalVM(最新)
- 适用于 JDK 24 的 GraalVM(抢先体验)
- 适用于 JDK 21 的 GraalVM
- 适用于 JDK 17 的 GraalVM
- 存档
- 开发版
GraalWasm
GraalWasm 是一个开源 WebAssembly 运行时。它以二进制格式运行 WebAssembly 程序,可用于在 Java 应用程序中嵌入和利用 WebAssembly 模块。GraalWasm 正在积极开发中,并且正在跟踪许多 WebAssembly 扩展。
WebAssembly 模块系统 #
使用 GraalWasm,您可以在应用程序中加载 WebAssembly 模块,从 Java 访问它们,并使它们与其他 Graal 语言互操作。为了熟练使用 GraalWasm,首先了解 GraalWasm 如何将 WebAssembly 的模块系统映射到 Polyglot API 很重要。
GraalWasm 使用 WebAssembly 模块的二进制格式作为其语言。您可以使用 GraalWasm 评估的有效 Source 始终是 二进制格式 中的单个 WebAssembly 模块。
以下是一种构建 WebAssembly Source
的方法
Source source = Source.newBuilder("wasm", new File("example.wasm")).name("example").build();
当您评估 WebAssembly Source
时,该模块会被解析和验证,并返回一个模块实例作为结果值。该模块实例也可以稍后从顶层绑定中检索。顶层作用域中的绑定名称与被评估的 Source
的名称相同。
// Evaluate the Source named "example".
Value exampleModule = context.eval(source);
// It is now accessible under the binding name "example".
assert context.getBindings("wasm").getMember("example") == exampleModule;
Source 名称 在 GraalWasm 中很重要,因为它们也被用于解析模块导入。如果一个模块尝试从模块 foo
导入一个符号,那么 GraalWasm 会在 Source
被命名为 foo
的模块中查找该符号。这些导入只有在 WebAssembly 模块实例的成员在给定上下文中首次被访问或执行时才会被解析。
模块实例对象 #
通过 Polyglot API 评估 WebAssembly 模块,您可以访问模块实例对象。模块实例对象为从 WebAssembly 模块导出的每个符号公开成员。您可以使用 getMemberKeys 获取所有导出符号的列表,使用 getMember 访问单个导出,并且在可变全局变量的情况下,使用 putMember 设置其值。
以下是各种 WebAssembly 导出如何映射到 polyglot 值
-
函数
函数被导出为可执行值,您可以使用 execute 调用它们。函数参数和返回值使用 类型映射 在 WebAssembly 值类型和 polyglot 值之间映射。如果函数返回多个值,它们将被包装在一个互操作数组中。
-
全局变量
当您使用
getMember
访问导出的全局变量时,您将获得全局变量的值,使用 类型映射 进行映射。如果全局变量是可变的,您也可以使用putMember
更新其值。目前,设置全局变量仅适用于数值类型,其值根据 类型映射 进行映射。 -
内存
导出的内存同时实现数组接口和缓冲区接口。数组接口允许您使用 getArrayElement 和 setArrayElement 将内存视为字节数组。缓冲区接口允许您使用 readBuffer 对内存进行批量复制,并使用
readBuffer*
和writeBuffer*
方法从内存读写 Java 原语类型。 -
表
导出的表是不透明的,不能查询或修改。
类型映射 #
每当 WebAssembly 值通过函数调用、返回值或导出全局变量访问传递给 Java 代码或其他 Graal 语言时,它都会被映射到一个 polyglot 值。下表显示了这种映射的工作方式。WebAssembly 是一种静态类型语言,所有值(局部变量、函数参数、返回值)都具有静态类型。根据这种类型,GraalWasm 将 polyglot 值解释为该类型的值,或者如果类型不匹配,则报告类型错误。
WebAssembly 值作为 Polyglot 值 #
该表描述了每种 WebAssembly 值类型,结果值实现哪些 polyglot 接口。
WebAssembly 类型 | Polyglot 接口 |
---|---|
i32 |
适合 int 的数字 |
i64 |
适合 long 的数字 |
f32 |
适合 float 的数字 |
f64 |
适合 double 的数字 |
v128 |
16 字节的只读缓冲区 |
funcref |
可执行对象 |
externref |
逐字返回 |
向 WebAssembly 函数传递参数 #
在调用导出的 WebAssembly 函数时,必须尊重其确切的类型签名。下表给出每种可能的 WebAssembly 参数类型所需的预期参数类型。
WebAssembly 参数类型 | 预期参数类型 |
---|---|
i32 |
int |
i64 |
long |
f32 |
float |
f64 |
double |
v128 |
从 WebAssembly 收到的现有 v128 值 |
funcref |
WebAssembly 的 ref.null 或导出的 WebAssembly 函数 |
externref |
可以是任何内容(只有 WebAssembly 的 ref.null 被 ref.is_null 视为 null) |
选项 #
GraalWasm 可以使用多个选项进行配置。在使用 Polyglot API 时,选项以编程方式传递给 Context 对象
Context.newBuilder("wasm").option("wasm.Builtins", "wasi_snapshot_preview1").build();
有关如何以编程方式设置选项的更多信息,请参阅 Polyglot 编程 参考。
可用的选项分为稳定选项和实验选项。实验选项没有保证未来支持,并且可能在版本之间发生变化。如果实验选项与 wasm
启动器一起使用,则必须提供 --experimental-options
选项。在使用 Context
时,必须在 Context.Builder 上调用方法 allowExperimentalOptions(true)。
稳定选项 #
提供以下稳定选项
-
--wasm.Builtins
: 公开一些 GraalWasm 提供的内置模块。值的语法是[<linkingName>:]<builtinModuleName>,[<linkingName>:]<builtinModuleName>,...
。请求的模块以逗号分隔。每个模块可以选择性地以冒号分隔的链接名称为前缀。如果给出链接名称,则模块在给定链接名称下导出。否则,模块在其内置模块名称下导出。提供的内置模块是
spectest
: 一组简单的函数模块,用于编写测试用例。该模块实现了与 WebAssembly 参考解释器的 spectest 模块 相同的接口。使用它可以执行 核心 WebAssembly 规范测试。wasi_snapshot_preview1
: GraalWasm 的 WebAssembly 系统接口快照预览 1 实现。涵盖了大多数已记录的 API,除了套接字和信号支持。
-
--wasm.WasiMapDirs
: 预先打开的目录列表,这些目录应该通过 WebAssembly 系统接口 API 访问。值的语法是[<virtualDir>::]<hostDir>,[<virtualDir>::]<hostDir>,...
。预先打开的目录以逗号分隔。每个目录可以选择性地以双冒号分隔的虚拟路径为前缀。在 WebAssembly 模块内部,该目录在虚拟路径下可用。如果省略虚拟路径,则预先打开的目录将位于与主机文件系统相同的路径上。必须设置此选项才能允许使用 WASI 的模块访问文件系统。仅允许访问这些预先打开的目录的内容。
实验选项 #
请注意,这些选项是实验性的,不保证将来会维护或支持它们。要使用它们,需要 --experimental-options
选项,或者必须在 Context
上启用实验选项,请参阅 上述内容。
以下选项对应于为 WebAssembly 标准添加新功能的功能提案。接受的值是 true
用于启用功能,false
用于禁用功能。已合并到 WebAssembly 规范中的功能在 GraalWasm 中默认启用。尚未合并到规范中的功能默认情况下处于禁用状态。用户可以覆盖默认值以试验即将推出的功能或选择退出标准化功能。
-
--wasm.BulkMemoryAndRefTypes
: 启用对 批量内存操作功能 和 引用类型功能 的支持,公开用于高效内存初始化的指令,并添加对一等不透明引用的支持。默认为true
。 -
--wasm.ExtendedConstExpressions
: 启用对 扩展常量表达式功能 的支持,在常量表达式中添加对算术指令的有限支持。默认为false
。 -
--wasm.Memory64
: 启用对 Memory64 功能 的支持,允许内存大于 4 GiB。默认为false
。 -
--wasm.MultiMemory
: 启用对 多个内存功能 的支持,允许模块具有多个内存。默认为false
。 -
--wasm.MultiValue
: 启用对 多值功能 的支持,允许函数返回多个值。默认为true
。 -
--wasm.SaturatingFloatToInt
: 启用对 非捕获浮点到整数转换功能 的支持,添加浮点到整数转换指令,这些指令会饱和而不是以捕获失败。默认为true
。 -
--wasm.SignExtensionOps
: 启用对 符号扩展运算符功能 的支持,添加用于扩展有符号整数值的指令。默认为true
。 -
--wasm.SIMD
: 启用对 定宽 SIMD 功能 的支持,引入一个新的值类型v128
以及与 SIMD 算术相关的指令。默认为true
。 -
--wasm.Threads
: 启用对 线程功能 的支持,让 WebAssembly 模块可以使用新的原子内存访问指令。默认值为false
。
使用 GraalWasm 启动器 #
GraalWasm 独立程序提供了 wasm
启动器,您可以使用它来执行编译为 WebAssembly 二进制模块的程序。
wasm [OPTION...] [--entry-point=FN] FILE [ARG...]
-
[选项...]
选项包括 GraalWasm 引擎选项,以
--wasm.
为前缀,例如--wasm.WasiMapDirs=preopened-dir
,以及任何其他多语言引擎选项。在使用wasm
启动器时,默认情况下会设置--wasm.Builtins=wasi_snapshot_preview1
选项,这样您就可以直接执行针对 WebAssembly 系统接口快照预览 1 编译的模块。可用选项在 选项 中有文档记录。您还可以通过将
--help:wasm
选项传递给wasm
启动器来获取 GraalWasm 引擎选项的完整列表。要包含内部选项,请使用--help:wasm:internal
。请注意,这些列表都包含稳定、支持的选项和实验性选项。 -
[--entry-point=FN]
您可以指定
--entry-point
选项来选择要作为模块入口点使用的导出函数,例如--entry-point=my_custom_main_fn
。如果缺少--entry-point
选项,GraalWasm 将尝试自动检测入口点。它将首先查找名为_start
的导出函数,然后查找名为_main
的导出函数。找到的第一个这样的函数将由wasm
启动器作为入口点执行。 -
文件
这是将要执行的二进制模块的路径。
-
[参数...]
程序参数,可以通过 WASI args_get 和 args_sizes_get 函数访问程序参数。