目錄

WasmEdge Executor 執行詳解

程式碼

可以看程式碼,大概可以看出 Executor 在初始化會做什麼事情。

Expect<std::unique_ptr<Runtime::Instance::ModuleInstance>> Executor::instantiate(Runtime::StoreManager &StoreMgr, const AST::Module &Mod, std::optional<std::string_view> Name)

參數原型如上,可以看到有參數

  1. Runtime::StoreManager &StoreMgr: 提供導入(Import)時所需的外部實例尋找,並在實例化成功後負責「註冊」該模組。它維護了整個運行時的狀態空間
  2. const AST::Module &Mod: 靜態藍圖,這是由載入器(Loader)解析並驗證後的抽象語法樹(AST)。包含了 WebAssembly 檔案的所有原始資訊(如指令、段數據、類型定義)。它是唯讀的(const)
  3. std::optional<std::string_view> Name: 定此模組是否為「具名模組」。若有值,該實例會以該名稱註冊進 StoreMgr,供其他模組後續引用;若無值(std::nullopt),則視為匿名模組

回傳值則是使用 Expect 處理錯誤,並且使用 unique_ptr 保證只有一個獨占此指標,最後回傳一個 Runtime::Instance::ModuleInstance 要,這是最終產物。它包含了所有運行時需要的數據結構:跳轉表、動態分配的記憶體快、初始化的全域變數值,以及指向具體執行代碼的指針。

詳細在來看一下函式做了什麼事情。

  1. 先用 AST Module 自己的函式檢查模組是否合法

  2. 創建 StackManager

  3. 檢查 Module 是否有名字,有的話看 StoreMgr 裡面有沒有重複的名字

  4. 直接給一個 ModuleInstance

  5. 將 AST(靜態藍圖)中的類型定義,複製到 ModInst(動態實例)中。這樣以後在進行函式調用或導入檢查時,實例才能快速查詢類型是否匹配

  6. Import Section: 根據導入清單,去 StoreManager 尋找對應的外部函式、記憶體或全域變數並進行連結(Matching)

    // Instantiate ImportSection and do import matching. (ImportSec)
    const AST::ImportSection &ImportSec = Mod.getImportSection();
    EXPECTED_TRY(instantiate(
                    [&StoreMgr](std::string_view ModName)
                        -> const WasmEdge::Runtime::Instance::ModuleInstance * {
                        return StoreMgr.findModule(ModName);
                    },
                    *ModInst, ImportSec)
                    .map_error(ReportError(ASTNodeAttr::Sec_Import)));

    可以詳細看下程式碼,基本上他是呼叫重載函式 instantiate(…)lib/executor/instantiate/import.cpp 裡面。

    第一個參數是一個 lambda 函式,基本上只是他現現在這個 Module 要 import 另外一個 Module,傳入的參數是 ModName 也就是那個 Module 的名字,我們就直接在 StoreMgr 裡面找給他,最後回傳一個 ModuleInstance

    第二參數就是現在的 ModInst,第三個參數則是 ImportSec,我們會用 for loop 看裡面一個一個找要 import 誰,這時候就會用到第一個參數的 lambda 函式找東西了。

    最前面的 EXPECTED_TRY 巨集,它會執行括號內的函式,如果函式回傳「成功」(Success),它就把結果拿出來,程式繼續往下跑。如果函式回傳「失敗」(Unexpect/Error),它會立刻中斷當前的 instantiate 函式,並直接將錯誤往上層丟,也就是後面的 .map_error()

  7. 準備一些執行環境,比較特別是,StackMgr 是為了暫時在這邊計算 Wasm Global 變數初始值,因為 Wasm 的 Global 初始值可以是動態計算的(雖然受限),這需要一個運作中的 Stack

  8. 最後,假如是具名模組,也就是有 Name,那就在 StoreMgr 註冊

Import 程式碼

實際我們可以在看 重載函式 instantiate(…)lib/executor/instantiate/import.cpp 裡面的東西。

可以看到他第一個參數也就是 ModuleFinder,我們的 lambda 函式

// Instantiate imports. See "include/executor/executor.h".
Expect<void> Executor::instantiate(
    std::function<const Runtime::Instance::ModuleInstance *(std::string_view)>
        ModuleFinder,
    Runtime::Instance::ModuleInstance &ModInst,
    const AST::ImportSection &ImportSec) {

    ...

    const auto *ImpModInst = ModuleFinder(ModName);