WebAssembly 中可定義區域變數與全域變數。區域變數定義在函式之中,必須使用 local
預先宣告,變數的預設值對整數來說是 0
,對浮點數來說是 +0.
。
想讀出變數值置入堆疊,要使用 get_local
,set_local
取出堆疊頂端的值設定為變數值,tee_local
取出堆疊頂端的值設定為變數值,並傳回設定後的值置入堆疊。
來看看底下這個操作:
int a = 10;
int b = 20;
int c = a + b;
只使用 get_local
、set_local
的話,就必須寫為:
(module
(import "env" "log" (func $log (param i32)))
(func $main
(local $a i32) (local $b i32) (local $c i32)
i32.const 10
set_local $a
i32.const 20
set_local $b
get_local $a
get_local $b
i32.add
set_local $c
get_local $c
call $log
)
(start $main)
)
變數名稱只是增加可讀性,每個變數會使用以 0 開始的索引識別,上面的程式,使用 tee_local
會比較方便:
(module
(import "env" "log" (func $log (param i32)))
(func $main
(local $a i32) (local $b i32) (local $c i32)
i32.const 10
tee_local $a
i32.const 20
tee_local $b
i32.add
tee_local $c
call $log
)
(start $main)
)
全域變數使用 global
宣告,要在模組區段定義,預設是不可變動,因此可以在宣告全域變數時一併指定其值,可以使用 get_global
來讀出變數值置入堆疊:
(module
(import "env" "log" (func $log (param f32)))
(global $PI f32 (f32.const 3.14159))
(func $main
get_global $PI
call $log
)
(start $main)
)
一樣地,全域變數也是使用從 0 開始的索引識別,名稱只是便於撰寫與閱讀。
如果想定義可變動的全域變數,必須加上 mut
,之後可以使用 set_global
來設定變數:
(module
(import "env" "log" (func $log (param f32)))
(global $PI (mut f32) (f32.const 3.14159))
(func $main
get_global $PI
call $log
f32.const 3.14
set_global $PI
get_global $PI
call $log
)
(start $main)
)
也可以將 JavaScript 中的變數匯入成為全域變數:
(module
(import "env" "PI" (global $PI f32))
(import "env" "log" (func $log (param f32)))
(func $main
get_global $PI
call $log
)
(start $main)
)
JavaScript 的部份,匯入物件上有對應的特性就可以匯入:
const importObj = {
env: {
PI : 3.14159,
log(n) {
console.log(n);
}
}
};
WebAssembly.instantiateStreaming(fetch('program.wasm'), importObj);
然而,若匯入之後要成為可變動的全域變數:
(module
(import "env" "PI" (global $PI (mut f32)))
(import "env" "log" (func $log (param f32)))
(func $main
get_global $PI
call $log
f32.const 3.14
set_global $PI
get_global $PI
call $log
)
(start $main)
)
JavaScript 的部份,匯入物件上對應的特性,必須是 WebAssembly.Global
實例:
const importObj = {
env: {
PI : new WebAssembly.Global({value : 'f32', mutable : true}, 3.14159),
log(n) {
console.log(n);
}
}
};
WebAssembly.instantiateStreaming(fetch('program.wasm'), importObj)
.then(_ => {
console.log(importObj.env.PI.value);
});
而且這麼一來,在 WebAssembly 中變動了全域變數的值,JavaScript 中對應的 WebAssembly.Global
實例,其 value
也會反映變動後的值。
若要從 WebAssembly 中匯出全域變數,可以使用 export
:
(module
(global $PI f32 (f32.const 3.14159))
(export "PI" (global $PI))
(func $nop)
)
(在這個範例中,(func $nop)
是必須的,似乎要匯出東西的模組,至少得有一個函式的樣子,拿掉該行會發生錯誤!)
被匯出的變數,會成為 WebAssembly.Instance
實例上 exports
的特性,型態會是 WebAssembly.Global
,可以透過 value
特性來取得,例如:
WebAssembly.instantiateStreaming(fetch('program.wasm'))
.then(prog => console.log(prog.instance.exports.PI.value));
在 WebAssembly 中,只有不可變動的變數才可以匯出,否則會發生錯誤。