以 WasmVM 向 WebAssembly 說哈囉!
  • 封面 & 目錄
  • 下載和編譯 WasmVM
  • 第一個 WebAssembly 程式
  • 堆疊
  • 數值型別
  • 算術、參數和控制指令
  • 儲存空間
    • 變數
    • 記憶體
    • 函式
    • 函式表
  • 模組
  • 位元格式
  • 虛擬機架構
  • 系統呼叫
  • 參考資料
Powered by GitBook
On this page
  • 一般架構
  • 函式型別 (Types)
  • 函式 (Functions)
  • 函式表 (Tables)
  • 記憶體 (Memories)
  • 全域變數 (Global Variables)
  • 函式表元素 (Elements)
  • 記憶體資料 (Data)
  • 起始函式 (Start function)
  • 引入 (Imports)
  • 輸出 (Exports)
  • 執行架構

模組

終於進入到重頭戲了!前面我們常常提到模組,模組是 WebAssembly 程式發佈、載入和執行的基本單位。依據執行時期的不同,分別有2種不同的結構

一般架構

模組在打包成位元檔,以及剛載入時的架構

在 WasmVM 裡,一個檔案只能包含一個模組

一般架構的模組分成以下幾個部份:

函式型別 (Types)

這邊的函式型別和之前講的 i32、i64.....等等的數值型別不一樣,是表示函式的參數(Parameter)和回傳值種類。在一些程式語言中會被稱為函式簽名 (Function Signature)

(module
    (type (func (param i32) (result i32)))
    (type $void (func))
    (func $a1 (type 0)
        get_local 0
    )
    (func $a2 (param i32) (result i32)
        get_local 0
    )
    (func $a3 (result i32)
        i32.const 5
    )
    (func $main (type $void)
          i32.const 3
        call $a1
          i32.const 4
        call $a2
        call $a3
        unreachable
    )
    (start $main)
)

我們宣告了 2 種函數型別,並且讓下面的 $a1 函數使用第 1 個(0 號)型別。

$a2 雖然沒有用我們宣告的型別,不過因為回傳值的型別,以及參數的型別和種類都與 0 號型別一樣,所以會自動套用 0 號型別

$a3 的函數型別沒有宣告,這個型別會接著有宣告的編號,自動被宣告成 2 號型別

和 memory 或 table 一樣,函式行別也能指定名稱。$main 函式就用了 $void 這個型別

函式 (Functions)

函式表 (Tables)

記憶體 (Memories)

全域變數 (Global Variables)

函式表元素 (Elements)

記憶體資料 (Data)

可以利用這個部份,填入記憶體最初始的資料

(module
    (memory 0)
    (data 0 (i32.const 4) "hahaha")
)

第一個 0 表示第 0 號記憶體,因為在目前的規格中一個模組只能有一塊記憶體,所以這邊只能填 0 ,或是記憶體的名稱

後面的 i32.const 表示要從記憶體的哪個位置開始填,這邊只能用 i32.const,不能用其他指令

當記憶體的長度不夠時,如果沒超出記憶體最大值,或是記憶體沒有最大值,會自動增加到足夠的長度

起始函式 (Start function)

表示當這個模組作為程式的主要模組時,首先呼叫的函式

這個函式不能有參數,也不能有回傳值

在之前的範例中是指定函數名稱 $main,其實也不一定要叫作 $main,可以用各種名稱,甚至只用編號也行

(module
    (func
    )
    (start 0)
)

引入 (Imports)

這個部份可以從其他檔案載入模組,並從其他模組中引入函式、函式表、記憶體或全域變數

(module
    (import "test2.wasm" "fun" (func))
    (func $main
        call 0
        i32.const 1
        unreachable
    )
    (start $main)
)

第一個字串是要引入的模組名稱(在 WasmVM 裡就是檔案名稱,去掉資料夾路徑),如果這個檔案沒被載入過,WasmVM 會自動從目前的工作資料夾中尋找這個檔案,並載入其中的模組。已經載入過的模組會直接從載入的資料中提取要引入的部份,不會重複載入。

第二個字串是物件的輸出名稱。在輸出時會指定一個輸出名稱給要輸出的物件,這邊必須要和輸出時指定的輸出名稱相符

後面則是要引入的物件型別:

  • 函式:照函式型別的格式,如果有的話,必須包含 param 和 result,不需要 local 和 裏面的指令

  • 函式表:照 table 的宣告格式,必須包含最小值和 anyfunc,如果有指定的話要包含最大值

  • 記憶體:照 memory 的宣告格式,必須包含最小值,如果有指定的話要包含最大值,不需要 data

  • 全域變數: (global 數值型別)

以上物件都可以指定以 $ 開頭的名稱,加在 table, memory, func 或 global 後面

上面範例中引入的 test2.wasm

(module
    (func
        i32.const 2
        unreachable
    )
    (export "fun" (func 0))
)

輸出結果

Values in the stack:
Type: i32, Value: 2
Values in the stack:
Type: i32, Value: 1

注意事項

  1. 一個模組最多只能有一個函式表、一個記憶體,所以如果已經引入函式表、記憶體,就不能再引入或宣告函式表或記憶體讓數量超過;如果已經宣告函式表、記憶體,也不能再引入或宣告函式表或記憶體讓數量超過

  2. 目前的規格中只能引入不可變動的全域變數

輸出 (Exports)

這個部份可以輸出模組中的函式、函式表、記憶體或全域變數,讓其他模組引入

(module
    (func
        i32.const 2
        unreachable
    )
    (export "fun" (func 0))
)

語法上和引入的差別在於 import 換成 ,然後只有輸出名稱的字串,以及物件型別。物件型別的格式和引入一樣

注意事項

只能輸出不可變動的全域變數

執行架構

在模組載入之後,大部分的物件都已經被儲存到儲存空間裡,或是指定初始值用的物件已經功成身退不需要了,所以剩下這些部份:

  • 函式型別 (types)

    • 這部份保留參數和回傳值,作為之後檢查型別用

  • 輸出物件 (Exports)

    • 輸出物件不是儲存在儲存空間,而是儲存在模組,所以這部份完整保留

接下來的位址都是表示這些物件在儲存空間中的位址,因為物件已經載入到儲存空間了,只要留下位址就好

  • 函式位址 (function addresses)

  • 函式表位址 (function addresses)

  • 記憶體位址 (function addresses)

  • 全域變數位址 (function addresses)

Previous函式表Next位元格式

Last updated 6 years ago

這部份在 章節已經有詳細說明

這部份在 章節已經有詳細說明

這部份在 章節已經有詳細說明

這部份在 章節已經有詳細說明

這部份在 章節已經有詳細說明

接著是初始資料的字串,在 章節有詳細的說明

函式
函式表
記憶體
變數
函式表
記憶體