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 会在名为 fooSource 模块中查找该符号。这些导入直到 WebAssembly 模块实例的成员在给定上下文中首次被访问或执行时才会解析。

模块实例对象

通过 Polyglot API 评估 WebAssembly 模块,您可以访问模块实例对象。模块实例对象公开了从 WebAssembly 模块导出的每个符号的成员。您可以使用 getMemberKeys 获取所有导出符号的列表,使用 getMember 访问单个导出,以及在可变全局变量的情况下,使用 putMember 设置其值。

以下是各种 WebAssembly 导出如何映射到 Polyglot 值的方式

  • 函数

    函数作为可执行值导出,您可以使用 execute 调用它们。函数参数和返回值通过类型映射在 WebAssembly 值类型和 Polyglot 值之间进行映射。如果函数返回多个值,这些值将被包装在一个互操作数组中。

  • 全局变量

    当您使用 getMember 访问导出的全局变量时,您将获得全局变量的值,该值已通过类型映射进行映射。如果全局变量是可变的,您还可以使用 putMember 更新其值。目前,设置全局变量仅适用于数字类型,其值根据类型映射进行映射。

  • 内存

    导出的内存同时实现了数组接口和缓冲区接口。数组接口允许您使用 getArrayElementsetArrayElement 将内存视为字节数组。缓冲区接口允许您使用 readBuffer 进行内存批量复制,并使用 readBuffer*writeBuffer* 方法从内存读写 Java 原生类型。

  • 导出的表是不透明的,不能被查询或修改。

类型映射

每当 WebAssembly 值通过函数调用、返回值或导出的全局访问传递给 Java 代码或另一种 Graal 语言时,它都会被映射到一个 Polyglot 值。下表展示了这种映射的工作方式。WebAssembly 是一种静态类型语言,所有值(局部变量、函数参数、返回值)都具有静态类型。GraalWasm 根据此类型将 Polyglot 值解释为此类型的值,如果类型不匹配则报告类型错误。

作为 Polyglot 值的 WebAssembly 值

此表描述了每种 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.nullref.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>,...。请求的模块用逗号分隔。每个模块可以选择性地以冒号分隔的链接名称作为前缀。如果给定了链接名称,则模块将以给定的链接名称导出。否则,模块将以其内置模块名称导出。

    提供的内置模块包括

  • --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)和任何其他 Polyglot 引擎选项。使用 wasm 启动器时,默认设置 --wasm.Builtins=wasi_snapshot_preview1 选项,以便您可以直接执行针对WebAssembly 系统接口快照预览 1编译的模块。

    可用选项已在选项中记录。您还可以通过向 wasm 启动器传递 --help:wasm 选项来获取 GraalWasm 引擎选项的完整列表。要包含内部选项,请使用 --help:wasm:internal。请注意,这些列表既包括稳定、受支持的选项,也包括实验性选项。

  • [--entry-point=函数名]

    您可以指定 --entry-point 选项来选择哪个导出函数用作模块的入口点,例如 --entry-point=my_custom_main_fn。如果缺少 --entry-point 选项,GraalWasm 将尝试自动检测入口点。它将首先查找名为 _start 的导出函数,然后查找名为 _main 的导出函数。找到的第一个此类函数将由 wasm 启动器作为入口点执行。

  • 文件

    这是要执行的二进制模块的路径。

  • [参数...]

    可通过 WASI args_getargs_sizes_get 函数访问的程序参数。