在〈Hello 模組〉中看過,Wat 有 type
可以定義 Type 區段,不過 Type 部份通常會自動根據 Wat 內容產生,例如,一個簡單的函式:
(module
(func $main)
)
使用 wasm2wat
將產生的 .wasm 轉為 .wat,可以看到:
(module
(type (;0;) (func))
(func (;0;) (type 0)))
對於有相同簽署的函式,在編譯時會自動產生對應的函式型態,既然函式有型態,那麼它是個值嗎?可以儲存嗎?
堆疊上不能存放函式,變數也不行,若要能儲存函式,必須使用 table
定義表格,就撰寫本文的這個時間點來說,WebAssembly 每個模組只能定義一個表格:
(module
(table $tb 2 anyfunc)
...
)
在這邊,2 表示表格的初始長度,也就是可以儲存兩個函式,可以在這個數字後選擇性地增加另一個數字,表示表格最大長度;anyfunc
表示可以是任何簽署的函式,目前也只支援這個值,未來可能會支援其他值。
想將函式儲存至表格,要使用 elem
,例如:
(module
(import "env" "log" (func $log (param i32)))
(table $tb 2 anyfunc)
(func $f1 (param $p i32)
(i32.add (get_local $p) (i32.const 10))
call $log
)
(func $f2 (param $p i32)
(i32.add (get_local $p) (i32.const 20))
call $log
)
(elem (i32.const 0) $f1 $f2)
...
)
(i32.const 0)
是偏移量,因此在這邊,函式會從索引 0 開始儲存,若必要也可以將表格匯出,例如:
(module
(import "env" "log" (func $log (param i32)))
(table $tb 2 anyfunc)
(func $f1 (param $p i32)
(i32.add (get_local $p) (i32.const 10))
call $log
)
(func $f2 (param $p i32)
(i32.add (get_local $p) (i32.const 20))
call $log
)
(elem (i32.const 0) $f1 $f2)
(export "tb" (table $tb))
)
被匯出的表格,在 JavaScript 中會是個 WebAssembly.Table
實例,可以使用其 get
方法指定索引,取得對應的函式,例如:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script>
const importObj = {
env: {
log(n) {
console.log(n);
}
}
};
WebAssembly.instantiateStreaming(fetch('program.wasm'), importObj)
.then(prog => {
const f1 = prog.instance.exports.tb.get(0);
const f2 = prog.instance.exports.tb.get(1);
f1(10);
f2(10);
});
</script>
</body>
</html>
你也可以在 JavaScript 中建立 WebAssembly.Table
實例,設定函式之後,匯入模組之中:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script>
const tb = new WebAssembly.Table({initial:2, element:"anyfunc"});
const importObj = {
env: {
log(n) {
console.log(n);
},
tb : tb
}
};
WebAssembly.instantiateStreaming(fetch('program.wasm'), importObj)
.then(_ => {
let f1 = tb.get(0);
let f2 = tb.get(1);
f1(10);
f2(10);
tb.set(0, f2); // 必須是 WebAssembly 中定義的函式才可以使用 set
tb.set(1, f1);
f1 = tb.get(0);
f2 = tb.get(1);
f1(10);
f2(10);
});
</script>
</body>
</html>
這個 HTML 中的 JavaScript,建立了 WebAssembly.Table
實例並作為匯入物件的特性,在 WebAssembly 模組裏對表格儲存函式之後,於 JavaScript 呼叫執行。
在這邊也看到,可以透過 WebAssembly.Table
的 set
方法設定函式,然而函式必須是在 WebAssembly 模組中定義之函式。
在 Wat 的部份,可以如下撰寫匯入表格:
(module
(import "env" "log" (func $log (param i32)))
(import "env" "tb" (table $tb 2 anyfunc))
(func $f1 (param $p i32)
(i32.add (get_local $p) (i32.const 10))
call $log
)
(func $f2 (param $p i32)
(i32.add (get_local $p) (i32.const 20))
call $log
)
(elem (i32.const 0) $f1 $f2)
)
既然可以透過 WebAssembly.Table
的 set
方法設定函式,這表示,若某模組中呼叫的函式來自表格,就可以動態地改變表格的函式內容,從而令該某模組呼叫的函式也隨之不同。
至於該如何於模組中呼叫表格中儲存之函式,這會在下一篇文件中討論。