WebAssembly 預設的資料型態有 i32
、i64
、f32
、f64
,那麼陣列呢?字串呢?這些在其他程式語言中,常見的基本元素在哪?嗯~必須自行實作!
正如〈堆疊與數值型態〉中談到,真正處理的都是位元,對於一組位元要以什麼觀點來看得,就是資料型態的概念,有了資料型態的概念,就可以用具體的概念來操作一組位元,而不是直接處理 0101 的運算。
因此對於什麼是陣列?何為字串?就視乎你如何看待、處理一組位元,WebAssembly 提供給你的,是可以儲存位元的線性空間。
可以使用 memory
來建立儲存位元的線性空間,一個模組只可以定義一個 memory
:
(module
(memory 1)
...
)
memory
建立的線性空間,與變數、函式表等是隔離的,因此不會有記憶體溢位等安全問題。memory
的第一個數字是初始長度,以 page 為單位,一個 page 是 64 KiB(一個 KiB 是 1024 位元組),可以選擇性地加上第二個數字限制最大長度,若不加上,長度不受限。
記憶體建立之後,全部的位元都會是 0,如果要指定初始值,可以使用 data
,指定從索引 0 開始儲存,儲存時以位元組為單位,例如:
(module
(memory $mem 1)
(data (i32.const 0) "\48\65\6C\6C\6F")
(export "mem" (memory $mem))
(func $nope)
)
模組的記憶體可以匯出,被匯出的記憶體會是 WebAssembly.Memory
的實例,它有個 buffer
特性,是 ArrayBuffer 實例,代表著 WebAssembly 中建立的線性空間。
上面的範例在記憶體中寫入的五個位元組,若以無號 8 位元整數來看,正好是 "Hello"
五個字元的 Unicode 號碼,因此可以如下顯示 Hello:
WebAssembly.instantiateStreaming(fetch('program.wasm'))
.then(prog => {
console.log(String.fromCharCode.apply(null,
new Uint8Array(prog.instance.exports.mem.buffer, 0, 5)
));
});
也可以在 JavaScript 中建立 WebAssembly.Memory
,再匯入 WebAssembly 模組之中:
const mem = new WebAssembly.Memory({initial:1});
const importObj = {
env: {mem}
};
WebAssembly.instantiateStreaming(fetch('program.wasm'), importObj)
.then(prog => {
console.log(String.fromCharCode.apply(null,
new Uint8Array(mem.buffer, 0, 5)
));
});
底下的模組會在匯入的記憶體中寫入資料:
(module
(import "env" "mem" (memory 1))
(data (i32.const 0) "Hello")
(func $nope)
)
可以使用 current_memory
取得記憶體長度置入堆疊,單位是 page,使用 grow_memory
可以試著增加長度,若成功,會將先前記憶體的長度置入堆疊,若失敗(像是已經定義長度上限),會將 -1 置入堆疊。
(module
(import "env" "log" (func $log (param i32)))
(memory $mem 1)
(data (i32.const 0) "\48\65\6C\6C\6F")
(func $main
current_memory
call $log ;; 顯示 1
(grow_memory (i32.const 2))
call $log ;; 顯示 1
)
(start $main)
)