WebAssembly核心编程之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种类型的成员,对应的已知区域具有固定的代码。

2e8e64d1abe51d1bee5ddf552b5a13ce.png

自定义区域的区域代码均为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对象,它在网页调试控制台中有如下的显示。

9e20d7d1a1bb32c0dc7af20d58a28722.png

二、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)。导出描述包含了每个导出对象的导出名称和类型。

061adc9d0cf12ef4e9257426f364abb4.png

三、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程序是一致的。

0429f0fe62c4e81ad7b1406ceda96e70.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值