WebAssembly核心编程[3]: Module 与 Instance

WebAssembly程序总是以模块来组织,模块是基本的部署、加载和编译单元。在JavaScript编程接口中,模块通过WebAssembly.Module类型表示。WebAssembly.Module通过加载的.wasm二进制文件创建而成,它承载了描述wasm模块的元数据,类似于描述程序集的Assembly对象。WebAssembly.Module自身是只读且无状态的,有状态的是根据它结合指定的导入对象创建的模块实例,后者通过WebAssembly.Instance表示。这两个类型提供了几个核心API,解析我们就通过它们来介绍WebAssembly的这两个核心对象(源代码)。

  • WebAssembly.Module.customSections
  • WebAssembly.Module.imports
  • WebAssembly.Module.exports
  • WebAssembly.Instance.exports

一、WebAssembly.Module.customSections

我们在wasm模块中定义任意不同类型的成员,在编译生成的.wasm二进制文件中,这些成员会根据类型分布到对应的区域(section)中,确切地说"已知区域(known section)"。除了针对具体成员类型的已知区域, wasm模块还可以开辟一组命名的"自定义区域(custom section)",静态方法WebAssembly.Module.customSections返回的ArrayBuffer指定名称的自定义区域在指定模块中的内容。目前的WebAssembly模块中大体可以定义如下11种类型的成员,对应的已知区域具有固定的代码(1-11)。

image

自定义区域的区域代码均为0,但是我们可以给它们进行命名。自定义区域赋予了我们在wasm模块文件中内嵌任意数据的能力。但是我们不能在.wat程序中为生成的.wasm添加自定义区域,但是如果我们在执行wat2wasm命令添加"--debug-names "开关,编译后的.wasm中将自动添加一个名为"name"的自定义区域,该区域会将WAT程序中针对各种对象的命名(程序执行的时候不需要这些名称)存储起来,它们将会显示在我们的"调试视图"中以增强可读性。为了演示针对自定义区域的读取,我们采用WAT格式定义了如下这个程序(文件名为app.wat)。

复制代码
(module
   (func (import "imports" "func"))
   (memory (import "imports" "memory") 1)
   (table (import "imports" "table") 4 externref)
   (global (import "imports"  "global") (mut i32))

   (func (export "func"))
   (memory (export "memory") 1)
   (table (export "table") 4 externref)
   (global (export "global") (mut i32) (i32.const 0))
)

如上面的代码片段所示,我们导入和导出了4种类型的对象(函数、Memory、Table和Global)。由于我们使用了两个Memory对象,wat2wasm编译工具在默认情况下并不支持,所以除了添加--debug-names开关,还需要添加--enable-multi-memory开关,完整的命令行如下所示。

复制代码
wat2wasm app.wat -o app.wasm --enable-multi-memory --debug-names

针对自定义区域"name"的读取按照如下的形式实现在index.html页面中:在调用fetch函数成功下app.wasm模块文件后,我们之间调用构造函数根据得到的字节内容创建了一个WebAssembly.Module对象,然后将它和区域名称"name"作为参数调用静态方法customSections。

复制代码
<html>
    <head></head>
    <body>
        <script>
           fetch("app.wasm")
                .then((response) => response.arrayBuffer())
                .then(bytes => {
                    var module = new WebAssembly.Module(bytes);
                    var sections = WebAssembly.Module.customSections(module, "name");
                    console.log(sections);
                })
        </script>
    </body>
</html>

得到的自定义区域内容体现为一个ArrayBuffer对象,它在网页调试控制台中有如下的显示。

image

二、WebAssembly.Module.imports & WebAssembly.Module.exports

WebAssembly.Module还定义了两个名称为imports 和exports的静态方法,我们可以利用它们得到wasm模块导入和导出对象的描述,接下来我们就将它们应用到我们的演示程序中。在index.html页面中,WebAssembly.Module对象创建出来后,我们将它作为参数传入上述两个静态方法中,然后将它们组合成又给对象,并以JSON的形式直接显示在页面里。

复制代码
<html>
    <head></head>
    <body>
        <pre><code id="code"></code></pre>
        <script>
           fetch("app.wasm")
                .then((response) => response.arrayBuffer())
                .then(bytes => {
                    var module = new WebAssembly.Module(bytes);
                    var imports = WebAssembly.Module.imports(module);
                    var exports = WebAssembly.Module.exports(module);
                    document.getElementById("code").innerText = JSON.stringify({"imports":imports, "exports":exports}, null, 2);
                })
        </script>
    </body>
</html>

针对导入/导出描述的JSON以下的形式承载的页面中,可以看出导入描述中包含了每个导入对象的路径("{module}.{name}")和类型(function、table、memory和global)。导出描述包含了每个导出对象的导出名称和类型。

image

三、WebAssembly.Instance.exports

WebAssembly.Module仅仅是对加载的wasm模块的描述,宿主程序真正消费的是根据它创建的实例,该实例通过WebAssembly.Instance类型表示。WebAssembly.Instance构造函数具有两个参数,分别是提供描述元数据的WebAssembly.Module和指定的导入对象。宿主程序能够使用的仅仅是该实例导出的成员,它们通过WebAssembly.Instance对象的exports属性暴露出来。在如下所示的代码片段中,我们对index.html作了相应的修改来演示WebAssembly.Instance对象的导出列表。

复制代码
<html>
    <head></head>
    <body>
        <script>
           fetch("app.wasm")
                .then((response) => response.arrayBuffer())
                .then(bytes => {
                    var module = new WebAssembly.Module(bytes);
                    var imports = {
                        "func": ()=> {},
                        "memory":  new WebAssembly.Memory({ initial: 1 }),
                        "table": new WebAssembly.Table({ initial: 4, element: "externref" }),
                        "global": new WebAssembly.Global({ value: "i32", mutable:true, initial:0})
                    };
                    var instance = new WebAssembly.Instance(module, {imports});
                    console.log(instance);
                })
        </script>
    </body>
</html>

如代码片段所示,在得到描述wasm模块的WebAssembly.Module对象后,我们创建出对应的导入对象,并将它们作为参数调用构造函数将WebAssembly.Instance对象创建出来,并将其exports属性代表的导出对象输出到调试控制台上。下图展示了导出列表在控制台中的输出,可以看出它们与app.wat程序是一致的。

image
相关推荐
小猪猪屁6 天前
WebAssembly 从零到实战:前端性能革命完全指南
前端·vue.js·webassembly
pepedd8648 天前
WebAssembly简单入门
前端·webassembly·trae
受之以蒙11 天前
Rust & WebAssembly 实践:构建一个简单实时的 Markdown 编辑器
笔记·rust·webassembly
wayhome在哪13 天前
3 分钟上手!用 WebAssembly 优化前端图片处理性能(附完整代码)
javascript·性能优化·webassembly
yangholmes888815 天前
EMSCRIPTEN File System 入门
前端·webassembly
yangholmes888820 天前
如何在 web 应用中使用 GDAL (三)
前端·webassembly
yangholmes888821 天前
如何在 web 应用中使用 GDAL (二)
前端·webassembly
yangholmes888822 天前
如何在 web 应用中使用 GDAL (一)
webassembly
DogDaoDao1 个月前
WebAssembly技术详解:从浏览器到云原生的高性能革命
云原生·音视频·编译·wasm·webassembly·流媒体·多媒体
受之以蒙1 个月前
Rust & WebAssembly 性能调优指南:从毫秒级加速到KB级瘦身
笔记·rust·webassembly