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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章