# WasmEdge Module 介紹


## 簡介

簡單介紹 WasmEdge 裡面具名跟匿名模組的差別。

## 簡單例子

假設我們有兩個簡單的 Wasm 檔案：

1. `provider.wat`: 提供一個 `add` 函式
2. `consumer.wat`: 導入 (Import) `provider` 的 `add` 函式並有一個 `run` 函式執行

```wat
// provider.wat
(module
  (func $add (param i32 i32) (result i32)
    local.get 0
    local.get 1
    i32.add)
  (export "add" (func $add))
)

// consumer.wat
(module
  (import "provider" "add" (func $add (param i32 i32) (result i32)))
  (func (export "run") (param i32 i32) (result i32)
    local.get 0
    local.get 1
    call $add)
)
```

我們可以使用 `wat2wasm` 工具將它們編譯成 `.wasm` 檔案，使用 `wat2wasm provider.wat -o provider.wasm`。

接下來，我們使用 WasmEdge 的 C API 來撰寫一段執行邏輯：

```cpp
#include <iostream>
#include <wasmedge/wasmedge.h>

int main() {
  // 1. 建立環境配置與 VM 容器
  WasmEdge_ConfigureContext *ConfCxt = WasmEdge_ConfigureCreate();
  WasmEdge_VMContext *VMCxt = WasmEdge_VMCreate(ConfCxt, NULL);

  std::cout << "[Step 1] Registering 'provider' module..." << std::endl;
  // 這裡會將 provider.wasm 載入、實例化並註冊到 Store 裡，名稱叫做 "provider"
  WasmEdge_String ProviderName = WasmEdge_StringCreateByCString("provider"); // Wasm 自己的字串類型
  WasmEdge_Result Res1 =
      WasmEdge_VMRegisterModuleFromFile(VMCxt, ProviderName, "provider.wasm");

  if (!WasmEdge_ResultOK(Res1)) {
    std::cerr << "Registration failed: " << WasmEdge_ResultGetMessage(Res1)
              << std::endl;
    return 1;
  }

  std::cout << "[Step 2] Running 'consumer.wasm'..." << std::endl;
  // 執行時，它會自動去 Store 找名為 "provider" 的模組
  WasmEdge_String FuncName = WasmEdge_StringCreateByCString("run");
  WasmEdge_Value Params[2] = {WasmEdge_ValueGenI32(10),
                              WasmEdge_ValueGenI32(20)};
  WasmEdge_Value Returns[1];

  WasmEdge_Result Res2 = WasmEdge_VMRunWasmFromFile(
      VMCxt, "consumer.wasm", FuncName, Params, 2, Returns, 1);

  if (WasmEdge_ResultOK(Res2)) {
    std::cout << ">>> Execution Result: " << WasmEdge_ValueGetI32(Returns[0])
              << " <<<" << std::endl;
  } else {
    std::cerr << "Execution failed: " << WasmEdge_ResultGetMessage(Res2)
              << std::endl;
  }

  // 清理資源
  WasmEdge_StringDelete(ProviderName);
  WasmEdge_StringDelete(FuncName);
  WasmEdge_VMDelete(VMCxt);
  WasmEdge_ConfigureDelete(ConfCxt);

  return 0;
}
```

以上面來說，會有兩個 Module：

1. 具名模組 (Named Module)，在 Step 1 中，我們使用了 `WasmEdge_VMRegisterModuleFromFile`，不僅會建立一個實例，還會賦予它一個標籤 "provider"
2. 匿名模組 (Anonymous Module) ，在 Step 2 中，我們使用了 `WasmEdge_VMRunWasmFromFile`，WasmEdge 同樣會實例化這個模組，但不會給它名字，它被視為一個「臨時執行者」或「主程式」

在 WasmEdge 的執行時期架構中，每一個載入的 WebAssembly 模組在底層都會被實例化為一個 `ModuleInstance` 類別的物件。其中，具名模組會被註冊並儲存於 `StoreManager` 類別內部的成員變數中（通常是一個由名稱映射至實例的 std::map），以便進行後續的檢索與呼叫。

![alt text](image.png)

我們可以實際看 Log，如下：

![alt text](image-1.png)

### 多個匿名模組例子

{{< image
    src="image-2.png"
    alt="[URL](https://wasmedge.org/docs/embed/c/reference/upgrade_to_0.10.0/#wasmedge-executor-changes)"
    caption="[URL](https://wasmedge.org/docs/embed/c/reference/upgrade_to_0.10.0/#wasmedge-executor-changes)"
>}}

如文件，在 0.10.0 版本，WasmEdge 新增支援多個匿名模組，因為你可能想，如果只使用 `WasmEdge_VMRunWasmFromFile` 這個「高階懶人包」API，確實一次只會剩下一個活動中的匿名模組（因為 VM 會在跑下一個之前，自動把前一個 `ActiveMod` 釋放掉），有了這個改動，你可以用更多匿名模組了。

```cpp
#include <iostream>
#include <wasmedge/wasmedge.h>

int main() {
  // 1. 環境初始化
  WasmEdge_ConfigureContext *Conf = WasmEdge_ConfigureCreate();
  WasmEdge_StoreContext *Store = WasmEdge_StoreCreate();
  WasmEdge_ExecutorContext *Exec = WasmEdge_ExecutorCreate(Conf, NULL);
  WasmEdge_LoaderContext *Loader = WasmEdge_LoaderCreate(Conf);
  WasmEdge_ValidatorContext *Validator = WasmEdge_ValidatorCreate(Conf);

  // 2. --- 關鍵：註冊具名模組 'provider' ---
  WasmEdge_ASTModuleContext *AST_P = NULL;
  WasmEdge_LoaderParseFromFile(Loader, &AST_P, "provider.wasm");
  WasmEdge_ValidatorValidate(Validator, AST_P);

  WasmEdge_String P_Name = WasmEdge_StringCreateByCString("provider");
  WasmEdge_ModuleInstanceContext *P_Inst = NULL;
  // 使用 Register API：這會實例化並把 "provider" 存進 Store 的 Map 裡
  WasmEdge_ExecutorRegister(Exec, &P_Inst, Store, AST_P, P_Name);
  std::cout << "Provider registered at: " << P_Inst << std::endl;

  // 3. --- 載入 Consumer 藍圖 ---
  WasmEdge_ASTModuleContext *AST_C = NULL;
  WasmEdge_LoaderParseFromFile(Loader, &AST_C, "consumer.wasm");
  WasmEdge_ValidatorValidate(Validator, AST_C);

  // 4. --- 產生兩個匿名 Consumer 實例 ---
  WasmEdge_ModuleInstanceContext *ModInstA = NULL;
  WasmEdge_ModuleInstanceContext *ModInstB = NULL;

  // 實例化 A：這時 Executor 會去 Store 找 "provider" 並成功連結
  WasmEdge_ExecutorInstantiate(Exec, &ModInstA, Store, AST_C);
  // 實例化 B：同樣連結到同一個 "provider" 實例
  WasmEdge_ExecutorInstantiate(Exec, &ModInstB, Store, AST_C);

  std::cout << "Anonymous Instance A (Consumer) at: " << ModInstA << std::endl;
  std::cout << "Anonymous Instance B (Consumer) at: " << ModInstB << std::endl;

  // 5. 執行 (略，同前一個範例)
  // ... 執行 ModInstA 與 ModInstB 的 "run" ...

  // 6. 清理
  WasmEdge_ModuleInstanceDelete(ModInstA); // 匿名 A 消失，provider 引用數 -1
  WasmEdge_ModuleInstanceDelete(ModInstB); // 匿名 B 消失，provider 引用數 -1
  WasmEdge_ModuleInstanceDelete(P_Inst); // 具名 provider 消失

  // ... 其他清理 ...
  WasmEdge_StringDelete(P_Name);
  return 0;
}
```

結果如下：

```bash
kola:~/proj/WasmEdge/my_test$ LD_LIBRARY_PATH=../build/lib/api ./anonymous/ano 
\Provider registered at: 0x6269b494e570
[2026-03-24 21:13:59.029] [info] >>> [Dependency Debug] Consumer '' is looking for Provider 'provider'
[2026-03-24 21:13:59.029] [info] >>> [Dependency Debug] Found Provider 'provider'! Registering edge...
[2026-03-24 21:13:59.029] [info] >>> [Dependency Debug] Consumer '' is looking for Provider 'provider'
[2026-03-24 21:13:59.029] [info] >>> [Dependency Debug] Found Provider 'provider'! Registering edge...
Anonymous Instance A (Consumer) at: 0x6269b4946860
Anonymous Instance B (Consumer) at: 0x6269b4947ae0
```

如上，這次我們使用 `WasmEdge_ExecutorInstantiate` 註冊匿名模組，可以看到同時有兩個匿名模組，但使用同樣的 AST (`WasmEdge_LoaderParseFromFile` 產生 AST)

{{< admonition type=info >}}
VM 其實是一個包裝器，他是一個高階 API 包裝像是 Executor, Module (匿名、命名), StoreManager ... 等元件的包裝，像是我們在上面最簡單的例子使用 WasmEdge_VMXXX 函式。

但實際上我們也可以完全不用 VM，就是這邊多重匿名模組的例子，我們沒有 `WasmEdge_VMContext`，但就會需要自己建立 Loader, Validator 等物件，這代表你就是那台「虛擬機」的管理者，你必須手動安排 Parse -> Validate -> Instantiate 的流程。
{{< /admonition >}}

我們可以看看 `ModuleInstance` 物件的內容。

### 多個模組

參考 [Multiple WASM Module Example](https://wasmedge.org/docs/zh-tw/embed/c/multiple_modules/)。

第一二三種：

* 例子 1：從檔案載入 (`WasmEdge_VMRegisterModuleFromFile`)
* 例子 2：從記憶體 Buffer 載入 (`WasmEdge_VMRegisterModuleFromBuffer`)
* 例子 3：從 AST (抽象語法樹) 載入 (`WasmEdge_VMRegisterModuleFromASTModule`)

然後讓 Consumer 在 VM 裡面 Register 使用他。

寫法 4：使用者自己實例化，再借給 VM，所以最後要自己刪除模組。
寫法 5：完全不用 VM，只又 Executor，使用者自己建立了一個共用的 StoreCxt，然後用 Executor 分別把 lib.wasm 和 test.wasm 都 Instantiate 進同一個 Store 裡面，最後用 ExecutorInvoke 執行

## ModuleInstance and StoreManager 原始碼

請參考 [include/runtime/instance/module.h](https://github.com/WasmEdge/WasmEdge/blob/99394eb0e53dab489a80b78f6dc93f44d7add690/include/runtime/instance/module.h)，他的成員基本上如下：

* `ModName` (const std::string)
  * 具名模組：這裡存的是註冊名稱（如 "provider"）
  * 匿名模組：這裡存的是空字串 ""
* `LinkedStore`: 紀錄這個模組目前跟哪些 Store 關聯。當模組自毀（Destructor）時，它會透過這裡的 Callback 通知 Store
* 資源所有權 (Owned Instances)，成員如 `OwnedFuncInsts`, `OwnedMemInsts`, `OwnedGlobInsts`。這些是這一個模組自己定義並創造的資源。如果別人來 Import 這些東西，這個模組就是 Provider
* 資源存取清單 (Imported & Added Instances)，成員如 `FuncInsts`, `MemInsts`, `GlobInsts` 等。這裡存的是 Raw Pointer。這份清單包含了「自己擁有的」加上「從別人那裡導入的（Imported）」所有資源
* `HostData` 外部的資料，有自己的 finalizer `HostDataFinalizer`

所以匿名跟具名的差別是，匿名的 ModName 是空字串，並且具名模組你可以在 `StoreManager` 裡面找到，所以匿名你要自己管理好，自己刪除，因為 `StoreManager` 不幫你管理。

在看一下他的一些重要函式（除了一些 get/add/find 函式）：

* Destructor，很特別

  ```c
  virtual ~ModuleInstance() noexcept {
    // When destroying this module instance, call the callbacks to unlink to the
    // store managers.

    // 遍歷 LinkedStore 容器中的每一個元素
    // 每個 Pair first 是 StoreManager, Second 是 CallBack
    for (auto &&Pair : LinkedStore) {
      assuming(Pair.second); // 確保 Pair.second（回調函數）確實存在且有效
      Pair.second(Pair.first, this); // 這個 CallBack 是傳參書 StoreManager and 自己 ModuleInstance
    }
    if (HostDataFinalizer.operator bool()) { // Finalizer
      HostDataFinalizer(HostData);
    }
  }
  ```

* `linkStore` and `unlinkStore`: 就是設定 `LinkStore` 的地方

---

馬上來看 `StoreManager`，跟 `ModuleInstace` 有很大的關係，請參考 [include/runtime/storemgr.h](https://github.com/WasmEdge/WasmEdge/blob/master/include/runtime/storemgr.h)。

他有的成員像是：

* `Mutex`: Lock
* `NamedMod`: 主要的 Map 存所有 named module

可以看一下函式：

* Destructor 很特別，直接呼叫 `reset()`，也就是呼叫 `unlinkStore()` 所有 `ModuleInstace` 在 `NameMod` 裡面，這樣這些 `ModuleInstance` 在自己 destruct 的時候就不會自己再一次呼叫 `unlinkStore()` 對這個 `StorManager` 了
* `registerModule()`: 基本上就是真的確認 `NameMod` 沒有他，並且加入他，然後給這個 named `ModuleInstance` 一個 callback，這個 callback 基本上只是簡單的從 `NameMod` 刪除這個 `ModuleInstance`
* `unregisterModule()`: 相反的事情，跟 `reset(` 類似，從 `NameMod` 刪除他並且 `unlinkStore()`

## 整體 VM 裡面的 Module 流程

```cpp
#include <iostream>
#include <wasmedge/wasmedge.h>

int main() {
  // --- Step 0: 初始化環境 ---
  WasmEdge_ConfigureContext *ConfCxt = WasmEdge_ConfigureCreate();
  WasmEdge_VMContext *VMCxt = WasmEdge_VMCreate(ConfCxt, nullptr);
  WasmEdge_String ProviderName = WasmEdge_StringCreateByCString("provider");

  std::cout << "--- Step 1: Register Named Module [provider] ---" << std::endl;
  WasmEdge_Result Res =
      WasmEdge_VMRegisterModuleFromFile(VMCxt, ProviderName, "provider.wasm");
  if (!WasmEdge_ResultOK(Res)) {
    std::cerr << "Failed to register provider.wasm: "
              << WasmEdge_ResultGetMessage(Res) << std::endl;
    return -1;
  }

  std::cout
      << "\n--- Step 2: Load and Instantiate Anonymous Module [consumer] ---"
      << std::endl;
  WasmEdge_ASTModuleContext *ASTCxt = nullptr;
  WasmEdge_LoaderContext *LoadCxt = WasmEdge_VMGetLoaderContext(VMCxt);

  // 1. 解析 consumer.wasm
  Res = WasmEdge_LoaderParseFromFile(LoadCxt, &ASTCxt, "consumer.wasm");
  if (!WasmEdge_ResultOK(Res)) {
    std::cerr << "Failed to parse consumer.wasm" << std::endl;
    return -1;
  }

  // 2. 將 AST 載入 VM
  WasmEdge_VMLoadWasmFromASTModule(VMCxt, ASTCxt);

  // --- [修正] 2.5 執行驗證 (Validate) ---
  // 必須要有這一步，否則下一步 Instantiate 會回報 WrongVMWorkflow
  Res = WasmEdge_VMValidate(VMCxt);
  if (!WasmEdge_ResultOK(Res)) {
    std::cerr << "Failed to validate consumer.wasm: "
              << WasmEdge_ResultGetMessage(Res) << std::endl;
    return -1;
  }

  // 3. 執行實體化
  Res = WasmEdge_VMInstantiate(VMCxt);
  if (!WasmEdge_ResultOK(Res)) {
    std::cerr << "Failed to instantiate consumer.wasm: "
              << WasmEdge_ResultGetMessage(Res) << std::endl;
    return -1;
  }

  // 取得剛剛產生的匿名實例 (Active Module)
  const WasmEdge_ModuleInstanceContext *ConsumerInst =
      WasmEdge_VMGetActiveModule(VMCxt);
  std::cout << "Consumer Instance (Anonymous) created at: " << ConsumerInst
            << std::endl;

  // std::cout << "\n--- Step 3: Try to Force Delete [provider] while Consumer is "
  //              "alive ---"
  //           << std::endl;
  // // 呼叫你新實作的 API
  // WasmEdge_VMForceDeleteRegisteredModule(VMCxt, ProviderName);
  // std::cout << "Note: Check spdlog output for deferred deletion." << std::endl;

  std::cout << "\n--- Step 3.5: Execute Consumer code after ForceDelete ---"
            << std::endl;

  // 1. 名字要改對，改成 "run"
  WasmEdge_String FuncName = WasmEdge_StringCreateByCString("run");

  // 2. 準備兩個 i32 參數 (例如 10 + 20)
  WasmEdge_Value Params[2] = {WasmEdge_ValueGenI32(10),
                              WasmEdge_ValueGenI32(20)};
  WasmEdge_Value Returns[1];

  // 3. 執行
  Res = WasmEdge_VMExecute(VMCxt, FuncName, Params, 2, Returns, 1);

  if (WasmEdge_ResultOK(Res)) {
    std::cout << "Execution Success! Result: "
              << WasmEdge_ValueGetI32(Returns[0]) << std::endl;
    std::cout << "This proves that even if 'provider' is logically deleted, "
                 "'consumer' can still use its memory."
              << std::endl;
  } else {
    std::cerr << "Execution Failed: " << WasmEdge_ResultGetMessage(Res)
              << std::endl;
  }

  WasmEdge_StringDelete(FuncName);

  std::cout
      << "\n--- Step 4: Now Delete [consumer] to trigger cascading cleanup ---"
      << std::endl;

  // WasmEdge_VMCleanup(VMCxt);

  // --- Step 5: 清理環境 ---
  std::cout << "\n--- Step 5: Final Cleanup ---" << std::endl;
  WasmEdge_VMDelete(VMCxt);
  WasmEdge_ConfigureDelete(ConfCxt);
  WasmEdge_StringDelete(ProviderName);
  WasmEdge_ASTModuleDelete(ASTCxt); // 注意：Load 完後 AST 通常可以提早刪除

  std::cout << "Test finished successfully." << std::endl;
  return 0;
}
```

以上面的測試程式，流程包含：

{{< image
    src="image-3.png"
    alt="一開始會先註冊一些 WASM 內部的模組，都是 was 開頭的模組"
    caption="一開始會先註冊一些 WASM 內部的模組，都是 was 開頭的模組"
>}}

{{< image
    src="image-4.png"
    alt="開始註冊我們自己的模組，有些有依賴就會找相關的模組引入，並且最後執行程式碼"
    caption="開始註冊我們自己的模組，有些有依賴就會找相關的模組引入，並且最後執行程式碼"
>}}

{{< image
    src="image-5.png"
    alt="最後刪除 VM，會把所有東西刪除，包含 Module"
    caption="最後刪除 VM，會把所有東西刪除，包含 Module"
>}}

注意可以看到在 delete `ModuleInstance` 的時候，callback 沒被呼叫，這是因為 `StoreManager` 先被刪除，他已經呼叫 `reset()` 把所有 link 刪除了。

## VM 物件介紹

我們可以看看 `VM`，他是最大的物件，包含所有相關的成員，我們可以看一下原始碼 [include/vm/vm.h](https://github.com/WasmEdge/WasmEdge/blob/master/include/vm/vm.h)，如下特別標示跟 Module 有關的成員：

```cpp
{
  ...

  /// \name VM environment.
  /// @{
  const Configure Conf;
  Statistics::Statistics Stat;
  VMStage Stage;
  mutable std::shared_mutex Mutex;
  /// @}

  /// \name VM components.
  /// @{
  Loader::Loader LoaderEngine;
  Validator::Validator ValidatorEngine;
  Executor::Executor ExecutorEngine;
  /// @}

  /// \name VM Storage.
  /// @{
  /// Loaded AST module.
  std::unique_ptr<AST::Module> Mod;
  std::unique_ptr<AST::Component::Component> Comp;
  /// Active module instance.
  // 現在 Active 正要跑的的模組
  std::unique_ptr<Runtime::Instance::ModuleInstance> ActiveModInst;
  std::unique_ptr<Runtime::Instance::ComponentInstance> ActiveCompInst;
  /// Registered module instances by user.
  // 存放的所有模組
  std::vector<std::unique_ptr<Runtime::Instance::ModuleInstance>> RegModInsts;
  /// Built-in module instances mapped to the configurations. For WASI.
   // 存放內建的 Host 模組（如 WASI）
  std::unordered_map<HostRegistration,
                     std::unique_ptr<Runtime::Instance::ModuleInstance>>
      BuiltInModInsts;
  /// Loaded module instances from plug-ins.
  // 存放從 Plug-ins 載入的 Host 模組（如 Tensorflow, Image 等）
  std::vector<std::unique_ptr<Runtime::Instance::ModuleInstance>>
      PlugInModInsts;
  std::vector<std::unique_ptr<Runtime::Instance::ComponentInstance>>
      PlugInCompInsts;
  /// Self-owned store (nullptr if an outside store is assigned in constructor).
  std::unique_ptr<Runtime::StoreManager> Store;
  /// Reference to the store.
  Runtime::StoreManager &StoreRef;
  /// @}
};
```

特別對最下面的 Store 有兩種情況：

* 情況 A (外部提供)：如果你建立 VM 時傳入了一個現成的 Store，那 VM 就不擁有這個 Store。這時 Store (unique_ptr) 是 nullptr，而 StoreRef (引用) 指向外部那個 Store
* 情況 B (內部建立)：如果你建立 VM 時沒給 Store，VM 會自己 new 一個。這時 Store (unique_ptr) 會持有它，而 StoreRef 同樣指向它

然後 `RegModInsts` 成員，他就是存放所有模組實體的地方，使用 `unique_ptr` 就表示他是唯一擁有的人，`StoreManager` 裡面也有 vector 存放他 (`namedMod`) 但那邊就只是用 raw pointer。

{{< admonition type=info >}}
簡單來說，「擁有權」就是一份關於「誰該負責收屍（釋放記憶體）」的契約。

在 C 語言裡，確實沒有語法層面的擁有權概念；但在 C++ 中，這是一套保護系統不崩潰的核心機制。

在 C 語言中，當你執行 malloc()，系統只會給你一個位址（指標）。

* 誰都能刪：任何拿到這個位址的人都可以呼叫 free()
* 沒人負責：如果大家都以為對方會 free()，就會造成 Memory Leak（記憶體洩漏）

C++ 引入了 std::unique_ptr 來把這份「腦袋裡的契約」變成「強制執行的法律」。

* 獨佔性：一個物件在同一時間只能被一個 unique_ptr 擁有。
* 自動化：當這個 unique_ptr 消失（離開作用域或被刪除）時，它會自動呼叫 delete
* 禁止複製：你不能把 unique_ptr 複製給別人。如果你想把擁有權轉移，必須用 std::move()，這就像是辦理過戶，原主人會立刻失去權限

禁止複製：你不能把 unique_ptr 複製給別人。如果你想把擁有權轉移，必須用 std::move()，這就像是辦理過戶，原主人會立刻失去權限。
{{< /admonition >}}

函式的話，我們可以看他們的 cleanup 函式

```cpp
/// ======= Functions which are stageless. =======
/// Clean up VM status
void cleanup() {
std::unique_lock Lock(Mutex);
return unsafeCleanup();
}
  
void VM::unsafeCleanup() {
  if (Mod) {
    Mod.reset();
  }
  if (Comp) {
    Comp.reset();
  }
  if (ActiveModInst) {
    ActiveModInst.reset(); // 把 ActiveMod 真的 delete
  }
  if (ActiveCompInst) {
    ActiveCompInst.reset();
  }
  StoreRef.reset(); // 清空 StoreManager 地圖而已
  RegModInsts.clear(); // 把所有註冊模組真的刪除
  Stat.clear();
  unsafeLoadBuiltInHosts();
  unsafeLoadPlugInHosts();
  unsafeRegisterBuiltInHosts();
  unsafeRegisterPlugInHosts();
  LoaderEngine.reset();
  Stage = VMStage::Inited;
}
```

基本上就是原廠重製，呼叫不同的成員的 reset() 函式。

