diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a9bf70566..251818335 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -166,6 +166,13 @@ jobs: os: macos-11 arch: x86_64 action: test + - name: 'WasmEdge on Linux/x86_64' + engine: 'wasmedge' + repo: 'com_github_wasmedge_wasmedge' + os: ubuntu-20.04 + arch: x86_64 + action: test + flags: --config=clang - name: 'Wasmtime on Linux/x86_64' engine: 'wasmtime' repo: 'com_github_bytecodealliance_wasmtime' diff --git a/BUILD b/BUILD index 5095d8cc4..1df703bf1 100644 --- a/BUILD +++ b/BUILD @@ -4,6 +4,7 @@ load( "proxy_wasm_select_engine_null", "proxy_wasm_select_engine_v8", "proxy_wasm_select_engine_wamr", + "proxy_wasm_select_engine_wasmedge", "proxy_wasm_select_engine_wasmtime", "proxy_wasm_select_engine_wavm", ) @@ -134,6 +135,28 @@ cc_library( ], ) +cc_library( + name = "wasmedge_lib", + srcs = [ + "src/common/types.h", + "src/wasmedge/types.h", + "src/wasmedge/wasmedge.cc", + ], + hdrs = ["include/proxy-wasm/wasmedge.h"], + defines = [ + "PROXY_WASM_HAS_RUNTIME_WASMEDGE", + "PROXY_WASM_HOST_ENGINE_WASMEDGE", + ], + linkopts = [ + "-lrt", + "-ldl", + ], + deps = [ + ":wasm_vm_headers", + "//external:wasmedge", + ], +) + cc_library( name = "wasmtime_lib", srcs = [ @@ -270,6 +293,8 @@ cc_library( [":v8_lib"], ) + proxy_wasm_select_engine_wamr( [":wamr_lib"], + ) + proxy_wasm_select_engine_wasmedge( + [":wasmedge_lib"], ) + proxy_wasm_select_engine_wasmtime( [":wasmtime_lib"], [":prefixed_wasmtime_lib"], diff --git a/bazel/BUILD b/bazel/BUILD index 78cd55313..2fe0c7924 100644 --- a/bazel/BUILD +++ b/bazel/BUILD @@ -15,6 +15,11 @@ config_setting( values = {"define": "engine=wamr"}, ) +config_setting( + name = "engine_wasmedge", + values = {"define": "engine=wasmedge"}, +) + config_setting( name = "engine_wasmtime", values = {"define": "engine=wasmtime"}, diff --git a/bazel/external/wasmedge.BUILD b/bazel/external/wasmedge.BUILD new file mode 100644 index 000000000..9887a4280 --- /dev/null +++ b/bazel/external/wasmedge.BUILD @@ -0,0 +1,24 @@ +load("@rules_foreign_cc//foreign_cc:defs.bzl", "cmake") + +licenses(["notice"]) # Apache 2 + +package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "srcs", + srcs = glob(["**"]), +) + +cmake( + name = "wasmedge_lib", + cache_entries = { + "WASMEDGE_BUILD_AOT_RUNTIME": "Off", + "WASMEDGE_BUILD_SHARED_LIB": "Off", + "WASMEDGE_BUILD_STATIC_LIB": "On", + "WASMEDGE_BUILD_TOOLS": "Off", + "WASMEDGE_FORCE_DISABLE_LTO": "On", + }, + generate_args = ["-GNinja"], + lib_source = ":srcs", + out_static_libs = ["libwasmedge_c.a"], +) diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index dd1a02758..0c2d07fec 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -181,6 +181,22 @@ def proxy_wasm_cpp_host_repositories(): actual = "@com_github_bytecodealliance_wasm_micro_runtime//:wamr_lib", ) + # WasmEdge with dependencies. + + maybe( + http_archive, + name = "com_github_wasmedge_wasmedge", + build_file = "@proxy_wasm_cpp_host//bazel/external:wasmedge.BUILD", + sha256 = "6724955a967a1457bcf5dc1787a8da95feaba45d3b498ae42768ebf48f587299", + strip_prefix = "WasmEdge-proxy-wasm-0.9.1", + url = "https://github.com/WasmEdge/WasmEdge/archive/refs/tags/proxy-wasm/0.9.1.tar.gz", + ) + + native.bind( + name = "wasmedge", + actual = "@com_github_wasmedge_wasmedge//:wasmedge_lib", + ) + # Wasmtime with dependencies. maybe( diff --git a/bazel/select.bzl b/bazel/select.bzl index eea36c888..d79862ace 100644 --- a/bazel/select.bzl +++ b/bazel/select.bzl @@ -40,6 +40,13 @@ def proxy_wasm_select_engine_wasmtime(xs, xp): "//conditions:default": [], }) +def proxy_wasm_select_engine_wasmedge(xs): + return select({ + "@proxy_wasm_cpp_host//bazel:engine_wasmedge": xs, + "@proxy_wasm_cpp_host//bazel:multiengine": xs, + "//conditions:default": [], + }) + def proxy_wasm_select_engine_wavm(xs): return select({ "@proxy_wasm_cpp_host//bazel:engine_wavm": xs, diff --git a/include/proxy-wasm/wasmedge.h b/include/proxy-wasm/wasmedge.h new file mode 100644 index 000000000..f0b2141ce --- /dev/null +++ b/include/proxy-wasm/wasmedge.h @@ -0,0 +1,26 @@ +// Copyright 2016-2019 Envoy Project Authors +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include + +#include "include/proxy-wasm/wasm_vm.h" + +namespace proxy_wasm { + +std::unique_ptr createWasmEdgeVm(); + +} // namespace proxy_wasm diff --git a/src/wasmedge/types.h b/src/wasmedge/types.h new file mode 100644 index 000000000..e51dede17 --- /dev/null +++ b/src/wasmedge/types.h @@ -0,0 +1,27 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/common/types.h" +#include "wasmedge/wasmedge.h" + +namespace proxy_wasm::WasmEdge { + +using WasmEdgeStorePtr = common::CSmartPtr; +using WasmEdgeVMPtr = common::CSmartPtr; +using WasmEdgeLoaderPtr = common::CSmartPtr; +using WasmEdgeValidatorPtr = common::CSmartPtr; +using WasmEdgeExecutorPtr = common::CSmartPtr; +using WasmEdgeASTModulePtr = common::CSmartPtr; + +} // namespace proxy_wasm::WasmEdge diff --git a/src/wasmedge/wasmedge.cc b/src/wasmedge/wasmedge.cc new file mode 100644 index 000000000..7d3a4e408 --- /dev/null +++ b/src/wasmedge/wasmedge.cc @@ -0,0 +1,610 @@ +// Copyright 2016-2019 Envoy Project Authors +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "include/proxy-wasm/wasmedge.h" +#include "include/proxy-wasm/wasm_vm.h" +#include "src/wasmedge/types.h" + +#include "wasmedge/wasmedge.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace proxy_wasm { +namespace WasmEdge { + +// Helper templates to make values. +template WasmEdge_Value makeVal(T t); +template <> WasmEdge_Value makeVal(Word t) { + return WasmEdge_ValueGenI32(static_cast(t.u64_)); +} +template <> WasmEdge_Value makeVal(uint32_t t) { + return WasmEdge_ValueGenI32(static_cast(t)); +} +template <> WasmEdge_Value makeVal(uint64_t t) { + return WasmEdge_ValueGenI64(static_cast(t)); +} +template <> WasmEdge_Value makeVal(double t) { return WasmEdge_ValueGenF64(t); } + +// Helper function to print values. +std::string printValue(const WasmEdge_Value &value) { + switch (value.Type) { + case WasmEdge_ValType_I32: + return std::to_string(WasmEdge_ValueGetI32(value)); + case WasmEdge_ValType_I64: + return std::to_string(WasmEdge_ValueGetI64(value)); + case WasmEdge_ValType_F32: + return std::to_string(WasmEdge_ValueGetF32(value)); + case WasmEdge_ValType_F64: + return std::to_string(WasmEdge_ValueGetF64(value)); + default: + return "unknown"; + } +} + +std::string printValues(const WasmEdge_Value *values, size_t size) { + if (size == 0) { + return ""; + } + + std::string s; + for (size_t i = 0; i < size; i++) { + if (i != 0U) { + s.append(", "); + } + s.append(printValue(values[i])); + } + return s; +} + +// Helper function to print valtype. +const char *printValType(WasmEdge_ValType kind) { + switch (kind) { + case WasmEdge_ValType_I32: + return "i32"; + case WasmEdge_ValType_I64: + return "i64"; + case WasmEdge_ValType_F32: + return "f32"; + case WasmEdge_ValType_F64: + return "f64"; + case WasmEdge_ValType_ExternRef: + return "anyref"; + case WasmEdge_ValType_FuncRef: + return "funcref"; + default: + return "unknown"; + } +} + +// Helper function to print valtype array. +std::string printValTypes(const WasmEdge_ValType *types, size_t size) { + if (size == 0) { + return "void"; + } + + std::string s; + s.reserve(size * 8 /* max size + " " */ - 1); + for (size_t i = 0; i < size; i++) { + if (i != 0U) { + s.append(" "); + } + s.append(printValType(types[i])); + } + return s; +} + +template struct ConvertWordType { + using type = T; // NOLINT(readability-identifier-naming) +}; +template <> struct ConvertWordType { + using type = uint32_t; // NOLINT(readability-identifier-naming) +}; + +// Helper templates to convert arg to valtype. +template enum WasmEdge_ValType convArgToValType(); +template <> enum WasmEdge_ValType convArgToValType() { return WasmEdge_ValType_I32; } +template <> enum WasmEdge_ValType convArgToValType() { return WasmEdge_ValType_I32; } +template <> enum WasmEdge_ValType convArgToValType() { return WasmEdge_ValType_I64; } +template <> enum WasmEdge_ValType convArgToValType() { return WasmEdge_ValType_I64; } +template <> enum WasmEdge_ValType convArgToValType() { return WasmEdge_ValType_F64; } + +// Helper templates to convert valtype to arg. +template T convValTypeToArg(WasmEdge_Value val); +template <> uint32_t convValTypeToArg(WasmEdge_Value val) { + return static_cast(WasmEdge_ValueGetI32(val)); +} +template <> Word convValTypeToArg(WasmEdge_Value val) { return WasmEdge_ValueGetI32(val); } +template <> int64_t convValTypeToArg(WasmEdge_Value val) { + return WasmEdge_ValueGetI64(val); +} +template <> uint64_t convValTypeToArg(WasmEdge_Value val) { + return static_cast(WasmEdge_ValueGetI64(val)); +} +template <> double convValTypeToArg(WasmEdge_Value val) { + return WasmEdge_ValueGetF64(val); +} + +// Helper templates to convert valtypes to args tuple. +template +constexpr T convValTypesToArgsTupleImpl(const WasmEdge_Value *arr, + std::index_sequence /*comptime*/) { + return std::make_tuple( + convValTypeToArg>::type>(arr[I])...); +} + +template ::value>> +constexpr T convValTypesToArgsTuple(const WasmEdge_Value *arr) { + return convValTypesToArgsTupleImpl(arr, Is()); +} + +// Helper templates to convert args tuple to valtypes. +template +uint32_t convArgsTupleToValTypesImpl(std::vector &types, + std::index_sequence /*comptime*/) { + auto size = std::tuple_size::value; + if (size > 0) { + auto ps = std::array::value>{ + convArgToValType::type>()...}; + types.resize(size); + std::copy_n(ps.data(), size, types.data()); + } + return size; +} + +template ::value>> +uint32_t convArgsTupleToValTypes(std::vector &types) { + return convArgsTupleToValTypesImpl(types, Is()); +} + +// Helper templates to create WasmEdge_FunctionTypeContext. +template WasmEdge_FunctionTypeContext *newWasmEdgeFuncType() { + std::vector params; + std::vector returns; + uint32_t param_nums = convArgsTupleToValTypes(params); + uint32_t return_nums = convArgsTupleToValTypes>(returns); + auto *ftype = WasmEdge_FunctionTypeCreate(params.data(), param_nums, returns.data(), return_nums); + return ftype; +} + +template WasmEdge_FunctionTypeContext *newWasmEdgeFuncType() { + std::vector params; + uint32_t param_nums = convArgsTupleToValTypes(params); + auto *ftype = WasmEdge_FunctionTypeCreate(params.data(), param_nums, nullptr, 0); + return ftype; +} + +struct HostFuncData { + HostFuncData(const std::string_view modname, const std::string_view name) + : modname_(modname), name_(name), callback_(nullptr), raw_func_(nullptr), vm_(nullptr) {} + ~HostFuncData() = default; + + std::string modname_, name_; + WasmEdge_HostFunc_t callback_; + void *raw_func_; + WasmVm *vm_; +}; + +using HostFuncDataPtr = std::unique_ptr; + +struct HostModuleData { + HostModuleData(const std::string_view modname) { + cxt_ = WasmEdge_ImportObjectCreate(WasmEdge_StringWrap(modname.data(), modname.length())); + } + ~HostModuleData() { WasmEdge_ImportObjectDelete(cxt_); } + + WasmEdge_ImportObjectContext *cxt_; +}; + +using HostModuleDataPtr = std::unique_ptr; + +class WasmEdge : public WasmVm { +public: + WasmEdge() { + loader_ = WasmEdge_LoaderCreate(nullptr); + validator_ = WasmEdge_ValidatorCreate(nullptr); + executor_ = WasmEdge_ExecutorCreate(nullptr, nullptr); + store_ = nullptr; + module_ = nullptr; + memory_ = nullptr; + } + + std::string_view getEngineName() override { return "wasmedge"; } + std::string_view getPrecompiledSectionName() override { return ""; } + + Cloneable cloneable() override { return Cloneable::NotCloneable; } + std::unique_ptr clone() override { return nullptr; } + + bool load(std::string_view bytecode, std::string_view precompiled, + const std::unordered_map &function_names) override; + bool link(std::string_view debug_name) override; + uint64_t getMemorySize() override; + std::optional getMemory(uint64_t pointer, uint64_t size) override; + bool setMemory(uint64_t pointer, uint64_t size, const void *data) override; + bool getWord(uint64_t pointer, Word *word) override; + bool setWord(uint64_t pointer, Word word) override; + size_t getWordSize() override { return sizeof(uint32_t); }; + +#define _REGISTER_HOST_FUNCTION(T) \ + void registerCallback(std::string_view module_name, std::string_view function_name, T, \ + typename ConvertFunctionTypeWordToUint32::type f) override { \ + registerHostFunctionImpl(module_name, function_name, f); \ + }; + FOR_ALL_WASM_VM_IMPORTS(_REGISTER_HOST_FUNCTION) +#undef _REGISTER_HOST_FUNCTION + +#define _GET_MODULE_FUNCTION(T) \ + void getFunction(std::string_view function_name, T *f) override { \ + getModuleFunctionImpl(function_name, f); \ + }; + FOR_ALL_WASM_VM_EXPORTS(_GET_MODULE_FUNCTION) +#undef _GET_MODULE_FUNCTION +private: + template + void registerHostFunctionImpl(std::string_view module_name, std::string_view function_name, + void (*function)(Args...)); + + template + void registerHostFunctionImpl(std::string_view module_name, std::string_view function_name, + R (*function)(Args...)); + + template + void getModuleFunctionImpl(std::string_view function_name, + std::function *function); + + template + void getModuleFunctionImpl(std::string_view function_name, + std::function *function); + + void terminate() override {} + + WasmEdgeLoaderPtr loader_; + WasmEdgeValidatorPtr validator_; + WasmEdgeExecutorPtr executor_; + WasmEdgeStorePtr store_; + WasmEdgeASTModulePtr module_; + WasmEdge_MemoryInstanceContext *memory_; + + std::unordered_map host_functions_; + std::unordered_map import_objects_; + std::unordered_set module_functions_; +}; + +bool WasmEdge::load(std::string_view bytecode, std::string_view /*precompiled*/, + const std::unordered_map & /*function_names*/) { + WasmEdge_ASTModuleContext *mod = nullptr; + WasmEdge_Result res = WasmEdge_LoaderParseFromBuffer( + loader_.get(), &mod, reinterpret_cast(bytecode.data()), bytecode.size()); + if (!WasmEdge_ResultOK(res)) { + return false; + } + res = WasmEdge_ValidatorValidate(validator_.get(), mod); + if (!WasmEdge_ResultOK(res)) { + return false; + } + module_ = mod; + return true; +} + +bool WasmEdge::link(std::string_view /*debug_name*/) { + assert(module_ != nullptr); + + // Create store and register imports. + store_ = WasmEdge_StoreCreate(); + if (store_ == nullptr) { + return false; + } + WasmEdge_Result res; + for (auto &&it : import_objects_) { + res = WasmEdge_ExecutorRegisterImport(executor_.get(), store_.get(), it.second->cxt_); + if (!WasmEdge_ResultOK(res)) { + fail(FailState::UnableToInitializeCode, + std::string("Failed to link Wasm module due to import: ") + it.first); + return false; + } + } + // Instantiate module. + res = WasmEdge_ExecutorInstantiate(executor_.get(), store_.get(), module_.get()); + if (!WasmEdge_ResultOK(res)) { + fail(FailState::UnableToInitializeCode, + std::string("Failed to link Wasm module: ") + std::string(WasmEdge_ResultGetMessage(res))); + return false; + } + // Get the function and memory exports. + uint32_t memory_num = WasmEdge_StoreListMemoryLength(store_.get()); + if (memory_num > 0) { + WasmEdge_String name; + WasmEdge_StoreListMemory(store_.get(), &name, 1); + memory_ = WasmEdge_StoreFindMemory(store_.get(), name); + if (memory_ == nullptr) { + return false; + } + } + uint32_t func_num = WasmEdge_StoreListFunctionLength(store_.get()); + if (func_num > 0) { + std::vector names(func_num); + WasmEdge_StoreListFunction(store_.get(), &names[0], func_num); + for (auto i = 0; i < func_num; i++) { + module_functions_.insert(std::string(names[i].Buf, names[i].Length)); + } + } + return true; +} + +uint64_t WasmEdge::getMemorySize() { + if (memory_ != nullptr) { + return 65536ULL * WasmEdge_MemoryInstanceGetPageSize(memory_); + } + return 0; +} + +std::optional WasmEdge::getMemory(uint64_t pointer, uint64_t size) { + char *ptr = reinterpret_cast(WasmEdge_MemoryInstanceGetPointer(memory_, pointer, size)); + if (ptr == nullptr) { + return std::nullopt; + } + return std::string_view(ptr, size); +} + +bool WasmEdge::setMemory(uint64_t pointer, uint64_t size, const void *data) { + auto res = WasmEdge_MemoryInstanceSetData(memory_, reinterpret_cast(data), + pointer, size); + return WasmEdge_ResultOK(res); +} + +bool WasmEdge::getWord(uint64_t pointer, Word *word) { + constexpr auto size = sizeof(uint32_t); + uint32_t word32; + auto res = + WasmEdge_MemoryInstanceGetData(memory_, reinterpret_cast(&word32), pointer, size); + if (WasmEdge_ResultOK(res)) { + word->u64_ = word32; + return true; + } + return false; +} + +bool WasmEdge::setWord(uint64_t pointer, Word word) { + constexpr auto size = sizeof(uint32_t); + uint32_t word32 = word.u32(); + auto res = + WasmEdge_MemoryInstanceSetData(memory_, reinterpret_cast(&word32), pointer, size); + return WasmEdge_ResultOK(res); +} + +template +void WasmEdge::registerHostFunctionImpl(std::string_view module_name, + std::string_view function_name, void (*function)(Args...)) { + auto it = import_objects_.find(std::string(module_name)); + if (it == import_objects_.end()) { + import_objects_.emplace(module_name, std::make_unique(module_name)); + it = import_objects_.find(std::string(module_name)); + } + + auto data = std::make_unique(module_name, function_name); + auto *func_type = newWasmEdgeFuncType>(); + data->vm_ = this; + data->raw_func_ = reinterpret_cast(function); + data->callback_ = [](void *data, WasmEdge_MemoryInstanceContext * /*MemCxt*/, + const WasmEdge_Value *Params, + WasmEdge_Value * /*Returns*/) -> WasmEdge_Result { + auto *func_data = reinterpret_cast(data); + const bool log = func_data->vm_->cmpLogLevel(LogLevel::trace); + if (log) { + func_data->vm_->integration()->trace("[vm->host] " + func_data->modname_ + "." + + func_data->name_ + "(" + + printValues(Params, sizeof...(Args)) + ")"); + } + auto args = convValTypesToArgsTuple>(Params); + auto fn = reinterpret_cast(func_data->raw_func_); + std::apply(fn, args); + if (log) { + func_data->vm_->integration()->trace("[vm<-host] " + func_data->modname_ + "." + + func_data->name_ + " return: void"); + } + return WasmEdge_Result_Success; + }; + + auto *hostfunc_cxt = WasmEdge_FunctionInstanceCreate(func_type, data->callback_, data.get(), 0); + WasmEdge_FunctionTypeDelete(func_type); + if (hostfunc_cxt == nullptr) { + fail(FailState::MissingFunction, "Failed to allocate host function instance"); + return; + } + + WasmEdge_ImportObjectAddFunction( + it->second->cxt_, WasmEdge_StringWrap(function_name.data(), function_name.length()), + hostfunc_cxt); + host_functions_.insert_or_assign(std::string(module_name) + "." + std::string(function_name), + std::move(data)); +} + +template +void WasmEdge::registerHostFunctionImpl(std::string_view module_name, + std::string_view function_name, R (*function)(Args...)) { + auto it = import_objects_.find(std::string(module_name)); + if (it == import_objects_.end()) { + import_objects_.emplace(module_name, std::make_unique(module_name)); + it = import_objects_.find(std::string(module_name)); + } + + auto data = std::make_unique(module_name, function_name); + auto *func_type = newWasmEdgeFuncType>(); + data->vm_ = this; + data->raw_func_ = reinterpret_cast(function); + data->callback_ = [](void *data, WasmEdge_MemoryInstanceContext * /*MemCxt*/, + const WasmEdge_Value *Params, WasmEdge_Value *Returns) -> WasmEdge_Result { + auto *func_data = reinterpret_cast(data); + const bool log = func_data->vm_->cmpLogLevel(LogLevel::trace); + if (log) { + func_data->vm_->integration()->trace("[vm->host] " + func_data->modname_ + "." + + func_data->name_ + "(" + + printValues(Params, sizeof...(Args)) + ")"); + } + auto args = convValTypesToArgsTuple>(Params); + auto fn = reinterpret_cast(func_data->raw_func_); + R res = std::apply(fn, args); + Returns[0] = makeVal(res); + if (log) { + func_data->vm_->integration()->trace("[vm<-host] " + func_data->modname_ + "." + + func_data->name_ + " return: " + std::to_string(res)); + } + return WasmEdge_Result_Success; + }; + + auto *hostfunc_cxt = WasmEdge_FunctionInstanceCreate(func_type, data->callback_, data.get(), 0); + WasmEdge_FunctionTypeDelete(func_type); + if (hostfunc_cxt == nullptr) { + fail(FailState::MissingFunction, "Failed to allocate host function instance"); + return; + } + + WasmEdge_ImportObjectAddFunction( + it->second->cxt_, WasmEdge_StringWrap(function_name.data(), function_name.length()), + hostfunc_cxt); + host_functions_.insert_or_assign(std::string(module_name) + "." + std::string(function_name), + std::move(data)); +} + +template +void WasmEdge::getModuleFunctionImpl(std::string_view function_name, + std::function *function) { + auto *func_cxt = WasmEdge_StoreFindFunction( + store_.get(), WasmEdge_StringWrap(function_name.data(), function_name.length())); + if (!func_cxt) { + *function = nullptr; + return; + } + + std::vector exp_args; + std::vector exp_returns; + convArgsTupleToValTypes>(exp_args); + convArgsTupleToValTypes>(exp_returns); + const auto *functype_cxt = WasmEdge_FunctionInstanceGetFunctionType(func_cxt); + std::vector act_args( + WasmEdge_FunctionTypeGetParametersLength(functype_cxt)); + std::vector act_returns( + WasmEdge_FunctionTypeGetReturnsLength(functype_cxt)); + WasmEdge_FunctionTypeGetParameters(functype_cxt, act_args.data(), act_args.size()); + WasmEdge_FunctionTypeGetReturns(functype_cxt, act_returns.data(), act_returns.size()); + + if (exp_args != act_args || exp_returns != act_returns) { + fail(FailState::UnableToInitializeCode, + "Bad function signature for: " + std::string(function_name) + + ", want: " + printValTypes(exp_args.data(), exp_args.size()) + " -> " + + printValTypes(exp_returns.data(), exp_returns.size()) + + ", but the module exports: " + printValTypes(act_args.data(), act_args.size()) + + " -> " + printValTypes(act_returns.data(), act_returns.size())); + return; + } + + *function = [function_name, this](ContextBase *context, Args... args) -> void { + WasmEdge_Value params[] = {makeVal(args)...}; + const bool log = cmpLogLevel(LogLevel::trace); + if (log) { + integration()->trace("[host->vm] " + std::string(function_name) + "(" + + printValues(params, sizeof...(Args)) + ")"); + } + SaveRestoreContext saved_context(context); + WasmEdge_Result res = + WasmEdge_ExecutorInvoke(executor_.get(), store_.get(), + WasmEdge_StringWrap(function_name.data(), function_name.length()), + params, sizeof...(Args), nullptr, 0); + if (!WasmEdge_ResultOK(res)) { + fail(FailState::RuntimeError, "Function: " + std::string(function_name) + " failed:\n" + + WasmEdge_ResultGetMessage(res)); + return; + } + if (log) { + integration()->trace("[host<-vm] " + std::string(function_name) + " return: void"); + } + }; +} + +template +void WasmEdge::getModuleFunctionImpl(std::string_view function_name, + std::function *function) { + auto *func_cxt = WasmEdge_StoreFindFunction( + store_.get(), WasmEdge_StringWrap(function_name.data(), function_name.length())); + if (!func_cxt) { + *function = nullptr; + return; + } + + std::vector exp_args; + std::vector exp_returns; + convArgsTupleToValTypes>(exp_args); + convArgsTupleToValTypes>(exp_returns); + const auto *functype_cxt = WasmEdge_FunctionInstanceGetFunctionType(func_cxt); + std::vector act_args( + WasmEdge_FunctionTypeGetParametersLength(functype_cxt)); + std::vector act_returns( + WasmEdge_FunctionTypeGetReturnsLength(functype_cxt)); + WasmEdge_FunctionTypeGetParameters(functype_cxt, act_args.data(), act_args.size()); + WasmEdge_FunctionTypeGetReturns(functype_cxt, act_returns.data(), act_returns.size()); + + if (exp_args != act_args || exp_returns != act_returns) { + fail(FailState::UnableToInitializeCode, + "Bad function signature for: " + std::string(function_name) + + ", want: " + printValTypes(exp_args.data(), exp_args.size()) + " -> " + + printValTypes(exp_returns.data(), exp_returns.size()) + + ", but the module exports: " + printValTypes(act_args.data(), act_args.size()) + + " -> " + printValTypes(act_returns.data(), act_returns.size())); + return; + } + + *function = [function_name, this](ContextBase *context, Args... args) -> R { + WasmEdge_Value params[] = {makeVal(args)...}; + WasmEdge_Value results[1]; + const bool log = cmpLogLevel(LogLevel::trace); + if (log) { + integration()->trace("[host->vm] " + std::string(function_name) + "(" + + printValues(params, sizeof...(Args)) + ")"); + } + SaveRestoreContext saved_context(context); + WasmEdge_Result res = + WasmEdge_ExecutorInvoke(executor_.get(), store_.get(), + WasmEdge_StringWrap(function_name.data(), function_name.length()), + params, sizeof...(Args), results, 1); + if (!WasmEdge_ResultOK(res)) { + fail(FailState::RuntimeError, "Function: " + std::string(function_name) + " failed:\n" + + WasmEdge_ResultGetMessage(res)); + return R{}; + } + R ret = convValTypeToArg(results[0]); + if (log) { + integration()->trace("[host<-vm] " + std::string(function_name) + + " return: " + std::to_string(ret)); + } + return ret; + }; +} + +} // namespace WasmEdge + +std::unique_ptr createWasmEdgeVm() { return std::make_unique(); } + +} // namespace proxy_wasm diff --git a/test/utility.cc b/test/utility.cc index 1686a0e14..e4196f8df 100644 --- a/test/utility.cc +++ b/test/utility.cc @@ -24,6 +24,9 @@ std::vector getWasmEngines() { #if defined(PROXY_WASM_HOST_ENGINE_WAMR) "wamr", #endif +#if defined(PROXY_WASM_HOST_ENGINE_WASMEDGE) + "wasmedge", +#endif #if defined(PROXY_WASM_HOST_ENGINE_WASMTIME) "wasmtime", #endif diff --git a/test/utility.h b/test/utility.h index d5d2d2f42..3935723bd 100644 --- a/test/utility.h +++ b/test/utility.h @@ -33,6 +33,9 @@ #if defined(PROXY_WASM_HOST_ENGINE_WASMTIME) #include "include/proxy-wasm/wasmtime.h" #endif +#if defined(PROXY_WASM_HOST_ENGINE_WASMEDGE) +#include "include/proxy-wasm/wasmedge.h" +#endif #if defined(PROXY_WASM_HOST_ENGINE_WAMR) #include "include/proxy-wasm/wamr.h" #endif @@ -144,6 +147,10 @@ class TestVm : public testing::TestWithParam { } else if (engine_ == "wasmtime") { vm = proxy_wasm::createWasmtimeVm(); #endif +#if defined(PROXY_WASM_HOST_ENGINE_WASMEDGE) + } else if (engine_ == "wasmedge") { + vm = proxy_wasm::createWasmEdgeVm(); +#endif #if defined(PROXY_WASM_HOST_ENGINE_WAMR) } else if (engine_ == "wamr") { vm = proxy_wasm::createWamrVm(); diff --git a/test/wasm_vm_test.cc b/test/wasm_vm_test.cc index d29437c9b..642ea636f 100644 --- a/test/wasm_vm_test.cc +++ b/test/wasm_vm_test.cc @@ -31,7 +31,7 @@ INSTANTIATE_TEST_SUITE_P(WasmEngines, TestVm, testing::ValuesIn(getWasmEngines() }); TEST_P(TestVm, Basic) { - if (engine_ == "wamr") { + if (engine_ == "wamr" || engine_ == "wasmedge") { EXPECT_EQ(vm_->cloneable(), proxy_wasm::Cloneable::NotCloneable); } else if (engine_ == "wasmtime" || engine_ == "v8") { EXPECT_EQ(vm_->cloneable(), proxy_wasm::Cloneable::CompiledBytecode);