Host 函数

Host 函数 是在 WebAssembly 之外的函数,它们以导入的方式传递到 WASM 模块中。以下步骤是将 host module 注册到 WasmEdge runtime 的示例。

此示例适用于使用 C++ 编写的 WasmEdge 项目的源代码编译。如果开发者想使用C/C++ 和 WasmEdge C API 来实现 host 函数,而不用 WasmEdge 项目进行编译,请参阅 C API 文档

Host 实例定义

WasmEdge 支持以导入的形式注册 host functionmemorytableglobal 等实例。 详情请参阅 include/host/wasi/test/core/spectest.h 中的示例。

函数

可以按照如下方式声明一个简单的 host 函数类:

  1. #include "common/errcode.h"
  2. #include "runtime/hostfunc.h"
  3. #include "runtime/instance/memory.h"
  4. namespace WasmEdge {
  5. namespace Host {
  6. class TestHost : public Runtime::HostFunction<TestHost> {
  7. public:
  8. Expect<uint32_t> body(Runtime::Instance::MemoryInstance *MemInst, uint32_t Param1, float Param2);
  9. };
  10. } // namespace Host
  11. } // namespace WasmEdge

在上述示例中,返回类型 Expect<T> 表示该 host 函数的预期返回类型 TParam1Param2 的类型表示此 host 函数的参数类型。 host 函数仅支持 WASM 的内置类型(如 uint32_tuint64_tfloat 以及 double)。在实例化时会生成 vec(valtype) -> resulttype 的函数签名,它能被 WASM 模块导入。

注意:目前 host 函数仅支持单个返回值。

另一种情况是传递需要由 host function 体访问的环境或信息。 以下示例展示了如何实现 host 函数集群:

  1. #include "common/errcode.h"
  2. #include "runtime/hostfunc.h"
  3. #include "runtime/instance/memory.h"
  4. #include <vector>
  5. namespace WasmEdge {
  6. namespace Host {
  7. template <typename T> class TestCluster : public Runtime::HostFunction<T> {
  8. public:
  9. TestCluster(std::vector<uint8_t> &Vec) : Data(Vec) {}
  10. protected:
  11. std::vector<uint8_t> &Data;
  12. };
  13. class TestHost1 : public TestCluster<TestHost1> {
  14. public:
  15. TestHost1(std::vector<uint8_t> &Vec) : TestCluster(Vec) {}
  16. Expect<uint32_t> body(Runtime::Instance::MemoryInstance *MemInst, uint32_t Param1, float Param2) {
  17. /// Operations to `Data` ...
  18. return {};
  19. }
  20. };
  21. class TestHost2 : public TestCluster<TestHost2> {
  22. public:
  23. TestHost2(std::vector<uint8_t> &Vec) : TestCluster(Vec) {}
  24. Expect<uint64_t> body(Runtime::Instance::MemoryInstance *MemInst, uint64_t Param1, double Param2) {
  25. /// Operations to `Data` ...
  26. return {};
  27. }
  28. };
  29. } // namespace Host
  30. } // namespace WasmEdge

表、 内存、 以及全局实例

要创建 host tablememory 以及 global 实例,唯一的办法就是使用 host module 中它们各自的构造器来创建。以下关于 host module 的章节将提供更详细的示例。

Host Modules/ host 模块

Host module 是一个可以注册到 WasmEdge runtime 的对象。Host module 包含了 host functionstablesmemoriesglobals 以及其它用户自定义的数据。WasmEdge 提供 API 来注册 host modules。在注册后,host modules 中的这些 host 实例可以被 WASM 模块所导入。

声明

Host module 提供了导出的模块名称,并且可以包含自定义数据。 构建 host modules 时需要填写模块名称。

  1. #include "common/errcode.h"
  2. #include "runtime/hostfunc.h"
  3. #include "runtime/importobj.h"
  4. namespace WasmEdge {
  5. namespace Host {
  6. class TestModule : public Runtime::ImportObject {
  7. public:
  8. TestModule() : ImportObject("test");
  9. virtual ~TestModule() = default;
  10. };
  11. } // namespace Host
  12. } // namespace WasmEdge

添加实例

Host module 提供了以下四种方法 addHostFunc()addHostTable()addHostMemory()addHostGlobal() 来插入具有唯一名称的实例。插入操作可以在构造器中完成。下面的示例代码中还展示了如何创建 host memoriestablesglobals

  1. #include "common/errcode.h"
  2. #include "runtime/hostfunc.h"
  3. #include "runtime/importobj.h"
  4. #include <memory>
  5. #include <vector>
  6. namespace WasmEdge {
  7. namespace Host {
  8. template <typename T> class TestCluster : public Runtime::HostFunction<T> {
  9. public:
  10. TestCluster(std::vector<uint8_t> &Vec) : Data(Vec) {}
  11. protected:
  12. std::vector<uint8_t> &Data;
  13. };
  14. class TestHost1 : public TestCluster<TestHost1> {
  15. public:
  16. TestHost1(std::vector<uint8_t> &Vec) : TestCluster(Vec) {}
  17. Expect<uint32_t> body(Runtime::Instance::MemoryInstance *MemInst, uint32_t Param1, float Param2) {
  18. /// Operations to `Data` ...
  19. return {};
  20. }
  21. };
  22. class TestHost2 : public TestCluster<TestHost2> {
  23. public:
  24. TestHost2(std::vector<uint8_t> &Vec) : TestCluster(Vec) {}
  25. Expect<uint64_t> body(Runtime::Instance::MemoryInstance *MemInst, uint64_t Param1, double Param2) {
  26. /// Operations to `Data` ...
  27. return {};
  28. }
  29. };
  30. class TestModule : public Runtime::ImportObject {
  31. public:
  32. TestModule(std::vector<uint8_t> &Vec) : ImportObject("test"), Data(Vec) {
  33. /// Add function instances with exporting name
  34. addHostFunc("test_func1", std::make_unique<TestHost1>(Data));
  35. addHostFunc("test_func2", std::make_unique<TestHost2>(Data));
  36. /// Add table instance with exporting name
  37. addHostTable("table", std::make_unique<Runtime::Instance::TableInstance>(
  38. TableType(RefType::FuncRef, 10, 20)));
  39. /// Add memory instance with exporting name
  40. addHostMemory("memory", std::make_unique<Runtime::Instance::MemoryInstance>(
  41. MemoryType(1, 2)));
  42. /// Add global instance with exporting name
  43. addHostGlobal("global_i32",
  44. std::make_unique<Runtime::Instance::GlobalInstance>(
  45. GlobalType(ValType::I32, ValMut::Const), uint32_t(666)));
  46. addHostGlobal("global_i64",
  47. std::make_unique<Runtime::Instance::GlobalInstance>(
  48. GlobalType(ValType::I64, ValMut::Const), uint64_t(666)));
  49. addHostGlobal("global_f32",
  50. std::make_unique<Runtime::Instance::GlobalInstance>(
  51. GlobalType(ValType::F32, ValMut::Const), float(666)));
  52. addHostGlobal("global_f64",
  53. std::make_unique<Runtime::Instance::GlobalInstance>(
  54. GlobalType(ValType::F64, ValMut::Const), double(666)));
  55. }
  56. virtual ~TestModule() = default;
  57. private:
  58. std::vector<uint8_t> &Data;
  59. };
  60. } // namespace Host
  61. } // namespace WasmEdge

Host module 提供了以下四种方法 getFuncs()getTables()getMems()getGlobals() 用来通过唯一的导出名称搜索已注册的实例。更多细节和 APIs 请参阅 include/runtime/importobj.h

在 WasmEdge 中注册 host module

用户可以通过 WasmEdge::VM::registerModule() API 来注册 host module

  1. #include "common/configure.h"
  2. #include "vm/vm.h"
  3. #include <vector>
  4. WasmEdge::Configure Conf;
  5. WasmEdge::VM::VM VM(Conf);
  6. std::vector<uint8_t> Data;
  7. WasmEdge::Host::TestModule TestMod(Data);
  8. VM.registerModule(TestMod);

在 CMakeFile 中链接库以及包含目录

为了从 WasmEdge 的包含目录中查找头文件以及链接静态库,CMakeFile 需要如下设置

  1. add_library(wasmedgeHostModuleTest # Static library name of host modules
  2. test.cpp # Path to host modules cpp files
  3. )
  4. target_include_directories(wasmedgeHostModuleTest
  5. PUBLIC
  6. ${Boost_INCLUDE_DIRS}
  7. ${PROJECT_SOURCE_DIR}/include
  8. )

host 函数体实现

以下是一些实现 host 函数体的小技巧。

使用时检查 Memory 实例

Host 函数能够访问 WASM 内存,这些内存通常以 MemoryInstance * 参数的形式被传递。 当发生函数调用 时,被调用函数所属的带有模块的帧将被压入堆栈。 在 host 函数的例子中,堆栈顶部栈帧中的内存实例将作为 host 函数体的参数被传递。然而,一个 WASM 模块中可以不包含内存实例。因此,用户在访问时应检查内存实例指针是否为空指针。

预期返回

在我们的机制里, include/common/errcode.h 中声明的 Expect<T> 被用作函数体的结果类型。在 Expect<void> 的情况下,预期场景需要 return {}; 。其它情况下, 预期场景需要 return Value; ,其中 ValueT 类型的一个变量。如果出现意外情况,用户可以调用 return Unexpect(Code); 来返回一个错误,其中 Code 是枚举 ErrCode 的一个元素。

强制终止

WasmEdge 提供了一种在 host 函数中终止 WASM 执行的方法。开发者可以返回 ErrCode::Terminated 来触发当前执行的强制终止,并将 ErrCode::Terminated 传递给 host 函数的调用者。