diff --git a/include/proxy-wasm/null_vm.h b/include/proxy-wasm/null_vm.h index a0a3798f..703266df 100644 --- a/include/proxy-wasm/null_vm.h +++ b/include/proxy-wasm/null_vm.h @@ -63,6 +63,8 @@ struct NullVm : public WasmVm { void terminate() override {} bool usesWasmByteOrder() override { return false; } + void warm() override {} + std::string plugin_name_; std::unique_ptr plugin_; }; diff --git a/include/proxy-wasm/v8.h b/include/proxy-wasm/v8.h index 73c91b95..7d2aaa9d 100644 --- a/include/proxy-wasm/v8.h +++ b/include/proxy-wasm/v8.h @@ -21,6 +21,7 @@ namespace proxy_wasm { +bool initv8Engine(); std::unique_ptr createV8Vm(); } // namespace proxy_wasm diff --git a/include/proxy-wasm/wamr.h b/include/proxy-wasm/wamr.h index 98ff72e3..32a05e94 100644 --- a/include/proxy-wasm/wamr.h +++ b/include/proxy-wasm/wamr.h @@ -21,6 +21,7 @@ namespace proxy_wasm { +bool initWamrEngine(); std::unique_ptr createWamrVm(); } // namespace proxy_wasm diff --git a/include/proxy-wasm/wasm_vm.h b/include/proxy-wasm/wasm_vm.h index db54ebd8..c14624ac 100644 --- a/include/proxy-wasm/wasm_vm.h +++ b/include/proxy-wasm/wasm_vm.h @@ -308,6 +308,11 @@ class WasmVm { */ virtual bool usesWasmByteOrder() = 0; + /** + * Warm the VM such as engine and runtime. + */ + virtual void warm() = 0; + bool isFailed() { return failed_ != FailState::Ok; } void fail(FailState fail_state, std::string_view message) { integration()->error(message); diff --git a/include/proxy-wasm/wasmtime.h b/include/proxy-wasm/wasmtime.h index e3fe4b48..11c4ae7a 100644 --- a/include/proxy-wasm/wasmtime.h +++ b/include/proxy-wasm/wasmtime.h @@ -18,6 +18,7 @@ namespace proxy_wasm { +bool initWasmtimeEngine(); std::unique_ptr createWasmtimeVm(); } // namespace proxy_wasm diff --git a/src/v8/v8.cc b/src/v8/v8.cc index bc5b8285..f7db6cef 100644 --- a/src/v8/v8.cc +++ b/src/v8/v8.cc @@ -103,10 +103,12 @@ class V8 : public WasmVm { void terminate() override; bool usesWasmByteOrder() override { return true; } + void warm() override; + private: wasm::own trap(std::string message); - std::string getFailMessage(std::string_view function_name, wasm::own trap); + std::string getPluginFailMessage(std::string_view function_name, wasm::own trap); template void registerHostFunctionImpl(std::string_view module_name, std::string_view function_name, @@ -124,6 +126,9 @@ class V8 : public WasmVm { void getModuleFunctionImpl(std::string_view function_name, std::function *function); + // Initialize the V8 engine and store if necessary. + void initStore(); + wasm::own store_; wasm::own module_; wasm::own> shared_module_; @@ -260,9 +265,16 @@ template constexpr T convertValTypesToArgsTuple(const U // V8 implementation. +void V8::initStore() { + if (store_ != nullptr) { + return; + } + store_ = wasm::Store::make(engine()); +} + bool V8::load(std::string_view bytecode, std::string_view precompiled, const std::unordered_map &function_names) { - store_ = wasm::Store::make(engine()); + initStore(); if (store_ == nullptr) { return false; } @@ -638,7 +650,8 @@ void V8::getModuleFunctionImpl(std::string_view function_name, } if (trap) { - fail(FailState::RuntimeError, getFailMessage(std::string(function_name), std::move(trap))); + fail(FailState::RuntimeError, + getPluginFailMessage(std::string(function_name), std::move(trap))); return; } if (log) { @@ -690,7 +703,8 @@ void V8::getModuleFunctionImpl(std::string_view function_name, } if (trap) { - fail(FailState::RuntimeError, getFailMessage(std::string(function_name), std::move(trap))); + fail(FailState::RuntimeError, + getPluginFailMessage(std::string(function_name), std::move(trap))); return R{}; } R rvalue = results[0].get::type>(); @@ -708,8 +722,10 @@ void V8::terminate() { isolate->TerminateExecution(); } -std::string V8::getFailMessage(std::string_view function_name, wasm::own trap) { - auto message = "Function: " + std::string(function_name) + " failed: "; +void V8::warm() { initStore(); } + +std::string V8::getPluginFailMessage(std::string_view function_name, wasm::own trap) { + auto message = "Plugin Crash: Function: " + std::string(function_name) + " failed: "; message += std::string(trap->message().get(), trap->message().size()); if (function_names_index_.empty()) { @@ -741,6 +757,8 @@ std::string V8::getFailMessage(std::string_view function_name, wasm::own createV8Vm() { return std::make_unique(); } } // namespace proxy_wasm diff --git a/src/wamr/wamr.cc b/src/wamr/wamr.cc index 482a59bf..b2431580 100644 --- a/src/wamr/wamr.cc +++ b/src/wamr/wamr.cc @@ -90,6 +90,8 @@ class Wamr : public WasmVm { void terminate() override {} bool usesWasmByteOrder() override { return true; } + void warm() override {} + private: template void registerHostFunctionImpl(std::string_view module_name, std::string_view function_name, @@ -119,9 +121,16 @@ class Wamr : public WasmVm { std::unordered_map module_functions_; }; +void Wamr::initStore() { + if (store_ != nullptr) { + return; + } + store_ = wasm_store_new(engine()); +} + bool Wamr::load(std::string_view bytecode, std::string_view precompiled, const std::unordered_map & /*function_names*/) { - store_ = wasm_store_new(engine()); + initStore(); if (store_ == nullptr) { return false; } @@ -697,8 +706,12 @@ void Wamr::getModuleFunctionImpl(std::string_view function_name, }; }; +void Wamr::warm() { initStore(); } + } // namespace wamr +bool initWamrEngine() { return wamr::engine() != nullptr; } + std::unique_ptr createWamrVm() { return std::make_unique(); } } // namespace proxy_wasm diff --git a/src/wasmedge/wasmedge.cc b/src/wasmedge/wasmedge.cc index 596af0c9..c2e29591 100644 --- a/src/wasmedge/wasmedge.cc +++ b/src/wasmedge/wasmedge.cc @@ -264,6 +264,7 @@ class WasmEdge : public WasmVm { }; 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, @@ -284,6 +285,8 @@ class WasmEdge : public WasmVm { void terminate() override {} bool usesWasmByteOrder() override { return true; } + void warm() override {} + WasmEdgeLoaderPtr loader_; WasmEdgeValidatorPtr validator_; WasmEdgeExecutorPtr executor_; @@ -314,13 +317,19 @@ bool WasmEdge::load(std::string_view bytecode, std::string_view /*precompiled*/, return true; } +void WasmEdge::initStore() { + if (store_ != nullptr) { + return; + } + store_ = WasmEdge_StoreCreate(); +} + bool WasmEdge::link(std::string_view /*debug_name*/) { assert(ast_module_ != nullptr); // Create store and register imports. - if (store_ == nullptr) { - store_ = WasmEdge_StoreCreate(); - } + initStore(); + if (store_ == nullptr) { return false; } @@ -609,8 +618,12 @@ void WasmEdge::getModuleFunctionImpl(std::string_view function_name, }; } +void WasmEdge::warm() { initStore(); } + } // namespace WasmEdge +bool initWasmEdgeEngine() { return wasmedge::engine() != nullptr; } + std::unique_ptr createWasmEdgeVm() { return std::make_unique(); } } // namespace proxy_wasm diff --git a/src/wasmtime/wasmtime.cc b/src/wasmtime/wasmtime.cc index c4a7646f..5e98fc19 100644 --- a/src/wasmtime/wasmtime.cc +++ b/src/wasmtime/wasmtime.cc @@ -80,6 +80,7 @@ class Wasmtime : public WasmVm { }; 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, @@ -100,6 +101,11 @@ class Wasmtime : public WasmVm { void terminate() override {} bool usesWasmByteOrder() override { return true; } + void warm() override {} + + // Initialize the V8 engine and store if necessary. + void initStore(); + WasmStorePtr store_; WasmModulePtr module_; WasmSharedModulePtr shared_module_; @@ -111,590 +117,603 @@ class Wasmtime : public WasmVm { std::unordered_map module_functions_; }; -bool Wasmtime::load(std::string_view bytecode, std::string_view /*precompiled*/, - const std::unordered_map & /*function_names*/) { - store_ = wasm_store_new(engine()); - if (store_ == nullptr) { - return false; +void Wasmtime::initStore() { + if (store_ != nullptr) { + return; } + store_ = wasm_store_new(engine()); - WasmByteVec vec; - wasm_byte_vec_new(vec.get(), bytecode.size(), bytecode.data()); - - module_ = wasm_module_new(store_.get(), vec.get()); - if (module_ == nullptr) { - return false; - } + bool Wasmtime::load(std::string_view bytecode, std::string_view /*precompiled*/, + const std::unordered_map & /*function_names*/) { + initStore(); + if (store_ == nullptr) { + return false; + } - shared_module_ = wasm_module_share(module_.get()); - if (shared_module_ == nullptr) { - return false; - } + WasmByteVec vec; + wasm_byte_vec_new(vec.get(), bytecode.size(), bytecode.data()); - return true; -} + module_ = wasm_module_new(store_.get(), vec.get()); + if (module_ == nullptr) { + return false; + } -std::unique_ptr Wasmtime::clone() { - assert(shared_module_ != nullptr); + shared_module_ = wasm_module_share(module_.get()); + if (shared_module_ == nullptr) { + return false; + } - auto clone = std::make_unique(); - if (clone == nullptr) { - return nullptr; + return true; } - clone->store_ = wasm_store_new(engine()); - if (clone->store_ == nullptr) { - return nullptr; - } + std::unique_ptr Wasmtime::clone() { + assert(shared_module_ != nullptr); - clone->module_ = wasm_module_obtain(clone->store_.get(), shared_module_.get()); - if (clone->module_ == nullptr) { - return nullptr; - } + auto clone = std::make_unique(); + if (clone == nullptr) { + return nullptr; + } - auto *integration_clone = integration()->clone(); - if (integration_clone == nullptr) { - return nullptr; - } - clone->integration().reset(integration_clone); + clone->store_ = wasm_store_new(engine()); + if (clone->store_ == nullptr) { + return nullptr; + } - return clone; -} + clone->module_ = wasm_module_obtain(clone->store_.get(), shared_module_.get()); + if (clone->module_ == nullptr) { + return nullptr; + } -static bool equalValTypes(const wasm_valtype_vec_t *left, const wasm_valtype_vec_t *right) { - if (left->size != right->size) { - return false; + auto *integration_clone = integration()->clone(); + if (integration_clone == nullptr) { + return nullptr; + } + clone->integration().reset(integration_clone); + + return clone; } - for (size_t i = 0; i < left->size; i++) { - if (wasm_valtype_kind(left->data[i]) != wasm_valtype_kind(right->data[i])) { + static bool equalValTypes(const wasm_valtype_vec_t *left, const wasm_valtype_vec_t *right) { + if (left->size != right->size) { return false; } - } - return true; -} + for (size_t i = 0; i < left->size; i++) { + if (wasm_valtype_kind(left->data[i]) != wasm_valtype_kind(right->data[i])) { + return false; + } + } -static std::string printValue(const wasm_val_t &value) { - switch (value.kind) { - case WASM_I32: - return std::to_string(value.of.i32); - case WASM_I64: - return std::to_string(value.of.i64); - case WASM_F32: - return std::to_string(value.of.f32); - case WASM_F64: - return std::to_string(value.of.f64); - default: - return "unknown"; + return true; } -} -static std::string printValues(const wasm_val_vec_t *values) { - if (values->size == 0) { - return ""; + static std::string printValue(const wasm_val_t &value) { + switch (value.kind) { + case WASM_I32: + return std::to_string(value.of.i32); + case WASM_I64: + return std::to_string(value.of.i64); + case WASM_F32: + return std::to_string(value.of.f32); + case WASM_F64: + return std::to_string(value.of.f64); + default: + return "unknown"; + } } - std::string s; - for (size_t i = 0; i < values->size; i++) { - if (i != 0U) { - s.append(", "); + static std::string printValues(const wasm_val_vec_t *values) { + if (values->size == 0) { + return ""; } - s.append(printValue(values->data[i])); - } - return s; -} -static const char *printValKind(wasm_valkind_t kind) { - switch (kind) { - case WASM_I32: - return "i32"; - case WASM_I64: - return "i64"; - case WASM_F32: - return "f32"; - case WASM_F64: - return "f64"; - case WASM_EXTERNREF: - return "externref"; - case WASM_FUNCREF: - return "funcref"; - default: - return "unknown"; + std::string s; + for (size_t i = 0; i < values->size; i++) { + if (i != 0U) { + s.append(", "); + } + s.append(printValue(values->data[i])); + } + return s; } -} -static std::string printValTypes(const wasm_valtype_vec_t *types) { - if (types->size == 0) { - return "void"; + static const char *printValKind(wasm_valkind_t kind) { + switch (kind) { + case WASM_I32: + return "i32"; + case WASM_I64: + return "i64"; + case WASM_F32: + return "f32"; + case WASM_F64: + return "f64"; + case WASM_EXTERNREF: + return "externref"; + case WASM_FUNCREF: + return "funcref"; + default: + return "unknown"; + } } - std::string s; - s.reserve(types->size * 8 /* max size + " " */ - 1); - for (size_t i = 0; i < types->size; i++) { - if (i != 0U) { - s.append(" "); + static std::string printValTypes(const wasm_valtype_vec_t *types) { + if (types->size == 0) { + return "void"; } - s.append(printValKind(wasm_valtype_kind(types->data[i]))); + + std::string s; + s.reserve(types->size * 8 /* max size + " " */ - 1); + for (size_t i = 0; i < types->size; i++) { + if (i != 0U) { + s.append(" "); + } + s.append(printValKind(wasm_valtype_kind(types->data[i]))); + } + return s; } - return s; -} -bool Wasmtime::link(std::string_view /*debug_name*/) { - assert(module_ != nullptr); - - WasmImporttypeVec import_types; - wasm_module_imports(module_.get(), import_types.get()); - - std::vector imports; - for (size_t i = 0; i < import_types.get()->size; i++) { - const wasm_name_t *module_name_ptr = wasm_importtype_module(import_types.get()->data[i]); - const wasm_name_t *name_ptr = wasm_importtype_name(import_types.get()->data[i]); - const wasm_externtype_t *extern_type = wasm_importtype_type(import_types.get()->data[i]); - - std::string_view module_name(module_name_ptr->data, module_name_ptr->size); - std::string_view name(name_ptr->data, name_ptr->size); - assert(name_ptr->size > 0); - switch (wasm_externtype_kind(extern_type)) { - case WASM_EXTERN_FUNC: { - auto it = host_functions_.find(std::string(module_name) + "." + std::string(name)); - if (it == host_functions_.end()) { + bool Wasmtime::link(std::string_view /*debug_name*/) { + assert(module_ != nullptr); + + WasmImporttypeVec import_types; + wasm_module_imports(module_.get(), import_types.get()); + + std::vector imports; + for (size_t i = 0; i < import_types.get()->size; i++) { + const wasm_name_t *module_name_ptr = wasm_importtype_module(import_types.get()->data[i]); + const wasm_name_t *name_ptr = wasm_importtype_name(import_types.get()->data[i]); + const wasm_externtype_t *extern_type = wasm_importtype_type(import_types.get()->data[i]); + + std::string_view module_name(module_name_ptr->data, module_name_ptr->size); + std::string_view name(name_ptr->data, name_ptr->size); + assert(name_ptr->size > 0); + switch (wasm_externtype_kind(extern_type)) { + case WASM_EXTERN_FUNC: { + auto it = host_functions_.find(std::string(module_name) + "." + std::string(name)); + if (it == host_functions_.end()) { + fail(FailState::UnableToInitializeCode, + std::string("Failed to load Wasm module due to a missing import: ") + + std::string(module_name) + "." + std::string(name)); + return false; + } + + auto *func = it->second->callback_.get(); + const wasm_functype_t *exp_type = wasm_externtype_as_functype_const(extern_type); + WasmFunctypePtr actual_type = wasm_func_type(it->second->callback_.get()); + if (!equalValTypes(wasm_functype_params(exp_type), + wasm_functype_params(actual_type.get())) || + !equalValTypes(wasm_functype_results(exp_type), + wasm_functype_results(actual_type.get()))) { + fail(FailState::UnableToInitializeCode, + std::string( + "Failed to load Wasm module due to an import type mismatch for function ") + + std::string(module_name) + "." + std::string(name) + + ", want: " + printValTypes(wasm_functype_params(exp_type)) + " -> " + + printValTypes(wasm_functype_results(exp_type)) + + ", but host exports: " + printValTypes(wasm_functype_params(actual_type.get())) + + " -> " + printValTypes(wasm_functype_results(actual_type.get()))); + return false; + } + imports.push_back(wasm_func_as_extern(func)); + } break; + case WASM_EXTERN_GLOBAL: { + // TODO(mathetake): add support when/if needed. fail(FailState::UnableToInitializeCode, - std::string("Failed to load Wasm module due to a missing import: ") + - std::string(module_name) + "." + std::string(name)); + "Failed to load Wasm module due to a missing import: " + std::string(module_name) + + "." + std::string(name)); return false; + } break; + case WASM_EXTERN_MEMORY: { + assert(memory_ == nullptr); + const wasm_memorytype_t *memory_type = + wasm_externtype_as_memorytype_const(extern_type); // owned by `extern_type` + if (memory_type == nullptr) { + return false; + } + memory_ = wasm_memory_new(store_.get(), memory_type); + if (memory_ == nullptr) { + return false; + } + imports.push_back(wasm_memory_as_extern(memory_.get())); + } break; + case WASM_EXTERN_TABLE: { + assert(table_ == nullptr); + const wasm_tabletype_t *table_type = + wasm_externtype_as_tabletype_const(extern_type); // owned by `extern_type` + if (table_type == nullptr) { + return false; + } + table_ = wasm_table_new(store_.get(), table_type, nullptr); + if (table_ == nullptr) { + return false; + } + imports.push_back(wasm_table_as_extern(table_.get())); + } break; } + } - auto *func = it->second->callback_.get(); - const wasm_functype_t *exp_type = wasm_externtype_as_functype_const(extern_type); - WasmFunctypePtr actual_type = wasm_func_type(it->second->callback_.get()); - if (!equalValTypes(wasm_functype_params(exp_type), wasm_functype_params(actual_type.get())) || - !equalValTypes(wasm_functype_results(exp_type), - wasm_functype_results(actual_type.get()))) { - fail( - FailState::UnableToInitializeCode, - std::string("Failed to load Wasm module due to an import type mismatch for function ") + - std::string(module_name) + "." + std::string(name) + - ", want: " + printValTypes(wasm_functype_params(exp_type)) + " -> " + - printValTypes(wasm_functype_results(exp_type)) + - ", but host exports: " + printValTypes(wasm_functype_params(actual_type.get())) + - " -> " + printValTypes(wasm_functype_results(actual_type.get()))); - return false; - } - imports.push_back(wasm_func_as_extern(func)); - } break; - case WASM_EXTERN_GLOBAL: { - // TODO(mathetake): add support when/if needed. - fail(FailState::UnableToInitializeCode, - "Failed to load Wasm module due to a missing import: " + std::string(module_name) + "." + - std::string(name)); + if (import_types.get()->size != imports.size()) { return false; - } break; - case WASM_EXTERN_MEMORY: { - assert(memory_ == nullptr); - const wasm_memorytype_t *memory_type = - wasm_externtype_as_memorytype_const(extern_type); // owned by `extern_type` - if (memory_type == nullptr) { - return false; - } - memory_ = wasm_memory_new(store_.get(), memory_type); - if (memory_ == nullptr) { - return false; - } - imports.push_back(wasm_memory_as_extern(memory_.get())); - } break; - case WASM_EXTERN_TABLE: { - assert(table_ == nullptr); - const wasm_tabletype_t *table_type = - wasm_externtype_as_tabletype_const(extern_type); // owned by `extern_type` - if (table_type == nullptr) { - return false; - } - table_ = wasm_table_new(store_.get(), table_type, nullptr); - if (table_ == nullptr) { - return false; + } + + wasm_extern_vec_t imports_vec = {imports.size(), imports.data()}; + instance_ = wasm_instance_new(store_.get(), module_.get(), &imports_vec, nullptr); + if (instance_ == nullptr) { + fail(FailState::UnableToInitializeCode, "Failed to create new Wasm instance"); + return false; + } + + WasmExportTypeVec export_types; + wasm_module_exports(module_.get(), export_types.get()); + + WasmExternVec exports; + wasm_instance_exports(instance_.get(), exports.get()); + + for (size_t i = 0; i < export_types.get()->size; i++) { + const wasm_externtype_t *exp_extern_type = wasm_exporttype_type(export_types.get()->data[i]); + wasm_extern_t *actual_extern = exports.get()->data[i]; + + wasm_externkind_t kind = wasm_extern_kind(actual_extern); + assert(kind == wasm_externtype_kind(exp_extern_type)); + switch (kind) { + case WASM_EXTERN_FUNC: { + WasmFuncPtr func = wasm_func_copy(wasm_extern_as_func(actual_extern)); + const wasm_name_t *name_ptr = wasm_exporttype_name(export_types.get()->data[i]); + module_functions_.insert_or_assign(std::string(name_ptr->data, name_ptr->size), + std::move(func)); + } break; + case WASM_EXTERN_GLOBAL: { + // TODO(mathetake): add support when/if needed. + } break; + case WASM_EXTERN_MEMORY: { + assert(memory_ == nullptr); + memory_ = wasm_memory_copy(wasm_extern_as_memory(actual_extern)); + if (memory_ == nullptr) { + return false; + } + } break; + case WASM_EXTERN_TABLE: { + // TODO(mathetake): add support when/if needed. + } break; } - imports.push_back(wasm_table_as_extern(table_.get())); - } break; } + return true; } - if (import_types.get()->size != imports.size()) { - return false; - } + uint64_t Wasmtime::getMemorySize() { return wasm_memory_data_size(memory_.get()); } - wasm_extern_vec_t imports_vec = {imports.size(), imports.data()}; - instance_ = wasm_instance_new(store_.get(), module_.get(), &imports_vec, nullptr); - if (instance_ == nullptr) { - fail(FailState::UnableToInitializeCode, "Failed to create new Wasm instance"); - return false; + std::optional Wasmtime::getMemory(uint64_t pointer, uint64_t size) { + assert(memory_ != nullptr); + if (pointer + size > wasm_memory_data_size(memory_.get())) { + return std::nullopt; + } + return std::string_view(wasm_memory_data(memory_.get()) + pointer, size); } - WasmExportTypeVec export_types; - wasm_module_exports(module_.get(), export_types.get()); + bool Wasmtime::setMemory(uint64_t pointer, uint64_t size, const void *data) { + assert(memory_ != nullptr); + if (pointer + size > wasm_memory_data_size(memory_.get())) { + return false; + } + ::memcpy(wasm_memory_data(memory_.get()) + pointer, data, size); + return true; + } - WasmExternVec exports; - wasm_instance_exports(instance_.get(), exports.get()); + bool Wasmtime::getWord(uint64_t pointer, Word *word) { + assert(memory_ != nullptr); + constexpr auto size = sizeof(uint32_t); + if (pointer + size > wasm_memory_data_size(memory_.get())) { + return false; + } - for (size_t i = 0; i < export_types.get()->size; i++) { - const wasm_externtype_t *exp_extern_type = wasm_exporttype_type(export_types.get()->data[i]); - wasm_extern_t *actual_extern = exports.get()->data[i]; + uint32_t word32; + ::memcpy(&word32, wasm_memory_data(memory_.get()) + pointer, size); + word->u64_ = wasmtoh(word32, true); + return true; + } - wasm_externkind_t kind = wasm_extern_kind(actual_extern); - assert(kind == wasm_externtype_kind(exp_extern_type)); - switch (kind) { - case WASM_EXTERN_FUNC: { - WasmFuncPtr func = wasm_func_copy(wasm_extern_as_func(actual_extern)); - const wasm_name_t *name_ptr = wasm_exporttype_name(export_types.get()->data[i]); - module_functions_.insert_or_assign(std::string(name_ptr->data, name_ptr->size), - std::move(func)); - } break; - case WASM_EXTERN_GLOBAL: { - // TODO(mathetake): add support when/if needed. - } break; - case WASM_EXTERN_MEMORY: { - assert(memory_ == nullptr); - memory_ = wasm_memory_copy(wasm_extern_as_memory(actual_extern)); - if (memory_ == nullptr) { - return false; - } - } break; - case WASM_EXTERN_TABLE: { - // TODO(mathetake): add support when/if needed. - } break; + bool Wasmtime::setWord(uint64_t pointer, Word word) { + constexpr auto size = sizeof(uint32_t); + if (pointer + size > wasm_memory_data_size(memory_.get())) { + return false; } + uint32_t word32 = htowasm(word.u32(), true); + ::memcpy(wasm_memory_data(memory_.get()) + pointer, &word32, size); + return true; } - return true; -} - -uint64_t Wasmtime::getMemorySize() { return wasm_memory_data_size(memory_.get()); } -std::optional Wasmtime::getMemory(uint64_t pointer, uint64_t size) { - assert(memory_ != nullptr); - if (pointer + size > wasm_memory_data_size(memory_.get())) { - return std::nullopt; + template void assignVal(T t, wasm_val_t & val); + template <> void assignVal(Word t, wasm_val_t & val) { + val.kind = WASM_I32; + val.of.i32 = static_cast(t.u64_); } - return std::string_view(wasm_memory_data(memory_.get()) + pointer, size); -} - -bool Wasmtime::setMemory(uint64_t pointer, uint64_t size, const void *data) { - assert(memory_ != nullptr); - if (pointer + size > wasm_memory_data_size(memory_.get())) { - return false; + template <> void assignVal(uint32_t t, wasm_val_t &val) { + val.kind = WASM_I32; + val.of.i32 = static_cast(t); + } + template <> void assignVal(uint64_t t, wasm_val_t &val) { + val.kind = WASM_I64; + val.of.i64 = static_cast(t); + } + template <> void assignVal(double t, wasm_val_t &val) { + val.kind = WASM_F64; + val.of.f64 = t; } - ::memcpy(wasm_memory_data(memory_.get()) + pointer, data, size); - return true; -} -bool Wasmtime::getWord(uint64_t pointer, Word *word) { - assert(memory_ != nullptr); - constexpr auto size = sizeof(uint32_t); - if (pointer + size > wasm_memory_data_size(memory_.get())) { - return false; + template wasm_val_t makeVal(T t) { + wasm_val_t val{}; + assignVal(t, val); + return val; } - uint32_t word32; - ::memcpy(&word32, wasm_memory_data(memory_.get()) + pointer, size); - word->u64_ = wasmtoh(word32, true); - return true; -} + template struct ConvertWordType { + using type = T; // NOLINT(readability-identifier-naming) + }; + template <> struct ConvertWordType { + using type = uint32_t; // NOLINT(readability-identifier-naming) + }; + + template auto convertArgToValTypePtr(); + template <> auto convertArgToValTypePtr() { return wasm_valtype_new_i32(); }; + template <> auto convertArgToValTypePtr() { return wasm_valtype_new_i32(); }; + template <> auto convertArgToValTypePtr() { return wasm_valtype_new_i64(); }; + template <> auto convertArgToValTypePtr() { return wasm_valtype_new_i64(); }; + template <> auto convertArgToValTypePtr() { return wasm_valtype_new_f64(); }; -bool Wasmtime::setWord(uint64_t pointer, Word word) { - constexpr auto size = sizeof(uint32_t); - if (pointer + size > wasm_memory_data_size(memory_.get())) { - return false; + template T convertValueTypeToArg(wasm_val_t val); + template <> uint32_t convertValueTypeToArg(wasm_val_t val) { + return static_cast(val.of.i32); } - uint32_t word32 = htowasm(word.u32(), true); - ::memcpy(wasm_memory_data(memory_.get()) + pointer, &word32, size); - return true; -} + template <> Word convertValueTypeToArg(wasm_val_t val) { return val.of.i32; } + template <> int64_t convertValueTypeToArg(wasm_val_t val) { return val.of.i64; } + template <> uint64_t convertValueTypeToArg(wasm_val_t val) { + return static_cast(val.of.i64); + } + template <> double convertValueTypeToArg(wasm_val_t val) { return val.of.f64; } -template void assignVal(T t, wasm_val_t &val); -template <> void assignVal(Word t, wasm_val_t &val) { - val.kind = WASM_I32; - val.of.i32 = static_cast(t.u64_); -} -template <> void assignVal(uint32_t t, wasm_val_t &val) { - val.kind = WASM_I32; - val.of.i32 = static_cast(t); -} -template <> void assignVal(uint64_t t, wasm_val_t &val) { - val.kind = WASM_I64; - val.of.i64 = static_cast(t); -} -template <> void assignVal(double t, wasm_val_t &val) { - val.kind = WASM_F64; - val.of.f64 = t; -} + template + constexpr T convertValTypesToArgsTuple(const U &vec, std::index_sequence /*comptime*/) { + return std::make_tuple( + convertValueTypeToArg>::type>( + vec->data[I])...); + } -template wasm_val_t makeVal(T t) { - wasm_val_t val{}; - assignVal(t, val); - return val; -} + template + void convertArgsTupleToValTypesImpl(wasm_valtype_vec_t * types, + std::index_sequence /*comptime*/) { + auto size = std::tuple_size::value; + auto ps = std::array::value>{ + convertArgToValTypePtr::type>()...}; + wasm_valtype_vec_new(types, size, ps.data()); + } -template struct ConvertWordType { - using type = T; // NOLINT(readability-identifier-naming) -}; -template <> struct ConvertWordType { - using type = uint32_t; // NOLINT(readability-identifier-naming) -}; + template ::value>> + void convertArgsTupleToValTypes(wasm_valtype_vec_t * types) { + convertArgsTupleToValTypesImpl(types, Is()); + } -template auto convertArgToValTypePtr(); -template <> auto convertArgToValTypePtr() { return wasm_valtype_new_i32(); }; -template <> auto convertArgToValTypePtr() { return wasm_valtype_new_i32(); }; -template <> auto convertArgToValTypePtr() { return wasm_valtype_new_i64(); }; -template <> auto convertArgToValTypePtr() { return wasm_valtype_new_i64(); }; -template <> auto convertArgToValTypePtr() { return wasm_valtype_new_f64(); }; + template WasmFunctypePtr newWasmNewFuncType() { + WasmValtypeVec params; + WasmValtypeVec results; + convertArgsTupleToValTypes(params.get()); + convertArgsTupleToValTypes>(results.get()); + return wasm_functype_new(params.get(), results.get()); + } -template T convertValueTypeToArg(wasm_val_t val); -template <> uint32_t convertValueTypeToArg(wasm_val_t val) { - return static_cast(val.of.i32); -} -template <> Word convertValueTypeToArg(wasm_val_t val) { return val.of.i32; } -template <> int64_t convertValueTypeToArg(wasm_val_t val) { return val.of.i64; } -template <> uint64_t convertValueTypeToArg(wasm_val_t val) { - return static_cast(val.of.i64); -} -template <> double convertValueTypeToArg(wasm_val_t val) { return val.of.f64; } + template WasmFunctypePtr newWasmNewFuncType() { + WasmValtypeVec params; + WasmValtypeVec results; + convertArgsTupleToValTypes(params.get()); + convertArgsTupleToValTypes>(results.get()); + return wasm_functype_new(params.get(), results.get()); + } -template -constexpr T convertValTypesToArgsTuple(const U &vec, std::index_sequence /*comptime*/) { - return std::make_tuple( - convertValueTypeToArg>::type>( - vec->data[I])...); -} + template + void Wasmtime::registerHostFunctionImpl( + std::string_view module_name, std::string_view function_name, void (*function)(Args...)) { + auto data = + std::make_unique(std::string(module_name) + "." + std::string(function_name)); + + WasmFunctypePtr type = newWasmNewFuncType>(); + WasmFuncPtr func = wasm_func_new_with_env( + store_.get(), type.get(), + [](void *data, const wasm_val_vec_t *params, + wasm_val_vec_t * /*results*/) -> wasm_trap_t * { + 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->name_ + "(" + + printValues(params) + ")"); + } + auto args = convertValTypesToArgsTuple>( + params, std::make_index_sequence{}); + auto fn = reinterpret_cast(func_data->raw_func_); + std::apply(fn, args); + if (log) { + func_data->vm_->integration()->trace("[vm<-host] " + func_data->name_ + + " return: void"); + } + return nullptr; + }, + data.get(), nullptr); + + data->vm_ = this; + data->callback_ = std::move(func); + data->raw_func_ = reinterpret_cast(function); + host_functions_.insert_or_assign(std::string(module_name) + "." + std::string(function_name), + std::move(data)); + }; -template -void convertArgsTupleToValTypesImpl(wasm_valtype_vec_t *types, - std::index_sequence /*comptime*/) { - auto size = std::tuple_size::value; - auto ps = std::array::value>{ - convertArgToValTypePtr::type>()...}; - wasm_valtype_vec_new(types, size, ps.data()); -} + template + void Wasmtime::registerHostFunctionImpl(std::string_view module_name, + std::string_view function_name, R(*function)(Args...)) { + auto data = + std::make_unique(std::string(module_name) + "." + std::string(function_name)); + WasmFunctypePtr type = newWasmNewFuncType>(); + WasmFuncPtr func = wasm_func_new_with_env( + store_.get(), type.get(), + [](void *data, const wasm_val_vec_t *params, wasm_val_vec_t *results) -> wasm_trap_t * { + 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->name_ + "(" + + printValues(params) + ")"); + } + auto args = convertValTypesToArgsTuple>( + params, std::make_index_sequence{}); + auto fn = reinterpret_cast(func_data->raw_func_); + R res = std::apply(fn, args); + assignVal(res, results->data[0]); + if (log) { + func_data->vm_->integration()->trace("[vm<-host] " + func_data->name_ + + " return: " + std::to_string(res)); + } + return nullptr; + }, + data.get(), nullptr); + + data->vm_ = this; + data->callback_ = std::move(func); + data->raw_func_ = reinterpret_cast(function); + host_functions_.insert_or_assign(std::string(module_name) + "." + std::string(function_name), + std::move(data)); + }; -template ::value>> -void convertArgsTupleToValTypes(wasm_valtype_vec_t *types) { - convertArgsTupleToValTypesImpl(types, Is()); -} + template + void Wasmtime::getModuleFunctionImpl(std::string_view function_name, + std::function * function) { -template WasmFunctypePtr newWasmNewFuncType() { - WasmValtypeVec params; - WasmValtypeVec results; - convertArgsTupleToValTypes(params.get()); - convertArgsTupleToValTypes>(results.get()); - return wasm_functype_new(params.get(), results.get()); -} + auto it = module_functions_.find(std::string(function_name)); + if (it == module_functions_.end()) { + *function = nullptr; + return; + } -template WasmFunctypePtr newWasmNewFuncType() { - WasmValtypeVec params; - WasmValtypeVec results; - convertArgsTupleToValTypes(params.get()); - convertArgsTupleToValTypes>(results.get()); - return wasm_functype_new(params.get(), results.get()); -} + WasmValtypeVec exp_args; + WasmValtypeVec exp_returns; + convertArgsTupleToValTypes>(exp_args.get()); + convertArgsTupleToValTypes>(exp_returns.get()); + wasm_func_t *func = it->second.get(); + WasmFunctypePtr func_type = wasm_func_type(func); -template -void Wasmtime::registerHostFunctionImpl(std::string_view module_name, - std::string_view function_name, void (*function)(Args...)) { - auto data = - std::make_unique(std::string(module_name) + "." + std::string(function_name)); - - WasmFunctypePtr type = newWasmNewFuncType>(); - WasmFuncPtr func = wasm_func_new_with_env( - store_.get(), type.get(), - [](void *data, const wasm_val_vec_t *params, wasm_val_vec_t * /*results*/) -> wasm_trap_t * { - 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->name_ + "(" + - printValues(params) + ")"); - } - auto args = convertValTypesToArgsTuple>( - params, std::make_index_sequence{}); - auto fn = reinterpret_cast(func_data->raw_func_); - std::apply(fn, args); - if (log) { - func_data->vm_->integration()->trace("[vm<-host] " + func_data->name_ + " return: void"); - } - return nullptr; - }, - data.get(), nullptr); - - data->vm_ = this; - data->callback_ = std::move(func); - data->raw_func_ = reinterpret_cast(function); - host_functions_.insert_or_assign(std::string(module_name) + "." + std::string(function_name), - std::move(data)); -}; + if (!equalValTypes(wasm_functype_params(func_type.get()), exp_args.get()) || + !equalValTypes(wasm_functype_results(func_type.get()), exp_returns.get())) { + fail(FailState::UnableToInitializeCode, + "Bad function signature for: " + std::string(function_name) + ", want: " + + printValTypes(exp_args.get()) + " -> " + printValTypes(exp_returns.get()) + + ", but the module exports: " + printValTypes(wasm_functype_params(func_type.get())) + + " -> " + printValTypes(wasm_functype_results(func_type.get()))); + return; + } + + *function = [func, function_name, this](ContextBase *context, Args... args) -> void { + const bool log = cmpLogLevel(LogLevel::trace); + SaveRestoreContext saved_context(context); + wasm_val_vec_t results = WASM_EMPTY_VEC; + WasmTrapPtr trap; -template -void Wasmtime::registerHostFunctionImpl(std::string_view module_name, - std::string_view function_name, R (*function)(Args...)) { - auto data = - std::make_unique(std::string(module_name) + "." + std::string(function_name)); - WasmFunctypePtr type = newWasmNewFuncType>(); - WasmFuncPtr func = wasm_func_new_with_env( - store_.get(), type.get(), - [](void *data, const wasm_val_vec_t *params, wasm_val_vec_t *results) -> wasm_trap_t * { - auto *func_data = reinterpret_cast(data); - const bool log = func_data->vm_->cmpLogLevel(LogLevel::trace); + // Workaround for MSVC++ not supporting zero-sized arrays. + if constexpr (sizeof...(args) > 0) { + wasm_val_t params_arr[] = {makeVal(args)...}; + wasm_val_vec_t params = WASM_ARRAY_VEC(params_arr); if (log) { - func_data->vm_->integration()->trace("[vm->host] " + func_data->name_ + "(" + - printValues(params) + ")"); + integration()->trace("[host->vm] " + std::string(function_name) + "(" + + printValues(¶ms) + ")"); } - auto args = convertValTypesToArgsTuple>( - params, std::make_index_sequence{}); - auto fn = reinterpret_cast(func_data->raw_func_); - R res = std::apply(fn, args); - assignVal(res, results->data[0]); + trap.reset(wasm_func_call(func, ¶ms, &results)); + } else { + wasm_val_vec_t params = WASM_EMPTY_VEC; if (log) { - func_data->vm_->integration()->trace("[vm<-host] " + func_data->name_ + - " return: " + std::to_string(res)); + integration()->trace("[host->vm] " + std::string(function_name) + "()"); } - return nullptr; - }, - data.get(), nullptr); - - data->vm_ = this; - data->callback_ = std::move(func); - data->raw_func_ = reinterpret_cast(function); - host_functions_.insert_or_assign(std::string(module_name) + "." + std::string(function_name), - std::move(data)); -}; - -template -void Wasmtime::getModuleFunctionImpl(std::string_view function_name, - std::function *function) { - - auto it = module_functions_.find(std::string(function_name)); - if (it == module_functions_.end()) { - *function = nullptr; - return; - } - - WasmValtypeVec exp_args; - WasmValtypeVec exp_returns; - convertArgsTupleToValTypes>(exp_args.get()); - convertArgsTupleToValTypes>(exp_returns.get()); - wasm_func_t *func = it->second.get(); - WasmFunctypePtr func_type = wasm_func_type(func); - - if (!equalValTypes(wasm_functype_params(func_type.get()), exp_args.get()) || - !equalValTypes(wasm_functype_results(func_type.get()), exp_returns.get())) { - fail(FailState::UnableToInitializeCode, - "Bad function signature for: " + std::string(function_name) + ", want: " + - printValTypes(exp_args.get()) + " -> " + printValTypes(exp_returns.get()) + - ", but the module exports: " + printValTypes(wasm_functype_params(func_type.get())) + - " -> " + printValTypes(wasm_functype_results(func_type.get()))); - return; - } - - *function = [func, function_name, this](ContextBase *context, Args... args) -> void { - const bool log = cmpLogLevel(LogLevel::trace); - SaveRestoreContext saved_context(context); - wasm_val_vec_t results = WASM_EMPTY_VEC; - WasmTrapPtr trap; + trap.reset(wasm_func_call(func, ¶ms, &results)); + } - // Workaround for MSVC++ not supporting zero-sized arrays. - if constexpr (sizeof...(args) > 0) { - wasm_val_t params_arr[] = {makeVal(args)...}; - wasm_val_vec_t params = WASM_ARRAY_VEC(params_arr); - if (log) { - integration()->trace("[host->vm] " + std::string(function_name) + "(" + - printValues(¶ms) + ")"); + if (trap) { + WasmByteVec error_message; + wasm_trap_message(trap.get(), error_message.get()); + std::string message(error_message.get()->data); // NULL-terminated + fail(FailState::RuntimeError, + "Function: " + std::string(function_name) + " failed: " + message); + return; } - trap.reset(wasm_func_call(func, ¶ms, &results)); - } else { - wasm_val_vec_t params = WASM_EMPTY_VEC; if (log) { - integration()->trace("[host->vm] " + std::string(function_name) + "()"); + integration()->trace("[host<-vm] " + std::string(function_name) + " return: void"); } - trap.reset(wasm_func_call(func, ¶ms, &results)); - } + }; + }; - if (trap) { - WasmByteVec error_message; - wasm_trap_message(trap.get(), error_message.get()); - std::string message(error_message.get()->data); // NULL-terminated - fail(FailState::RuntimeError, - "Function: " + std::string(function_name) + " failed: " + message); + template + void Wasmtime::getModuleFunctionImpl(std::string_view function_name, + std::function * function) { + auto it = module_functions_.find(std::string(function_name)); + if (it == module_functions_.end()) { + *function = nullptr; return; } - if (log) { - integration()->trace("[host<-vm] " + std::string(function_name) + " return: void"); + WasmValtypeVec exp_args; + WasmValtypeVec exp_returns; + convertArgsTupleToValTypes>(exp_args.get()); + convertArgsTupleToValTypes>(exp_returns.get()); + wasm_func_t *func = it->second.get(); + WasmFunctypePtr func_type = wasm_func_type(func); + if (!equalValTypes(wasm_functype_params(func_type.get()), exp_args.get()) || + !equalValTypes(wasm_functype_results(func_type.get()), exp_returns.get())) { + fail(FailState::UnableToInitializeCode, + "Bad function signature for: " + std::string(function_name) + ", want: " + + printValTypes(exp_args.get()) + " -> " + printValTypes(exp_returns.get()) + + ", but the module exports: " + printValTypes(wasm_functype_params(func_type.get())) + + " -> " + printValTypes(wasm_functype_results(func_type.get()))); + return; } - }; -}; -template -void Wasmtime::getModuleFunctionImpl(std::string_view function_name, - std::function *function) { - auto it = module_functions_.find(std::string(function_name)); - if (it == module_functions_.end()) { - *function = nullptr; - return; - } - WasmValtypeVec exp_args; - WasmValtypeVec exp_returns; - convertArgsTupleToValTypes>(exp_args.get()); - convertArgsTupleToValTypes>(exp_returns.get()); - wasm_func_t *func = it->second.get(); - WasmFunctypePtr func_type = wasm_func_type(func); - if (!equalValTypes(wasm_functype_params(func_type.get()), exp_args.get()) || - !equalValTypes(wasm_functype_results(func_type.get()), exp_returns.get())) { - fail(FailState::UnableToInitializeCode, - "Bad function signature for: " + std::string(function_name) + ", want: " + - printValTypes(exp_args.get()) + " -> " + printValTypes(exp_returns.get()) + - ", but the module exports: " + printValTypes(wasm_functype_params(func_type.get())) + - " -> " + printValTypes(wasm_functype_results(func_type.get()))); - return; - } + *function = [func, function_name, this](ContextBase *context, Args... args) -> R { + const bool log = cmpLogLevel(LogLevel::trace); + SaveRestoreContext saved_context(context); + wasm_val_t results_arr[1]; + wasm_val_vec_t results = WASM_ARRAY_VEC(results_arr); + WasmTrapPtr trap; + + // Workaround for MSVC++ not supporting zero-sized arrays. + if constexpr (sizeof...(args) > 0) { + wasm_val_t params_arr[] = {makeVal(args)...}; + wasm_val_vec_t params = WASM_ARRAY_VEC(params_arr); + if (log) { + integration()->trace("[host->vm] " + std::string(function_name) + "(" + + printValues(¶ms) + ")"); + } + trap.reset(wasm_func_call(func, ¶ms, &results)); + } else { + wasm_val_vec_t params = WASM_EMPTY_VEC; + if (log) { + integration()->trace("[host->vm] " + std::string(function_name) + "()"); + } + trap.reset(wasm_func_call(func, ¶ms, &results)); + } - *function = [func, function_name, this](ContextBase *context, Args... args) -> R { - const bool log = cmpLogLevel(LogLevel::trace); - SaveRestoreContext saved_context(context); - wasm_val_t results_arr[1]; - wasm_val_vec_t results = WASM_ARRAY_VEC(results_arr); - WasmTrapPtr trap; - - // Workaround for MSVC++ not supporting zero-sized arrays. - if constexpr (sizeof...(args) > 0) { - wasm_val_t params_arr[] = {makeVal(args)...}; - wasm_val_vec_t params = WASM_ARRAY_VEC(params_arr); - if (log) { - integration()->trace("[host->vm] " + std::string(function_name) + "(" + - printValues(¶ms) + ")"); + if (trap) { + WasmByteVec error_message; + wasm_trap_message(trap.get(), error_message.get()); + std::string message(error_message.get()->data); // NULL-terminated + fail(FailState::RuntimeError, + "Function: " + std::string(function_name) + " failed: " + message); + return R{}; } - trap.reset(wasm_func_call(func, ¶ms, &results)); - } else { - wasm_val_vec_t params = WASM_EMPTY_VEC; + R ret = convertValueTypeToArg(results.data[0]); if (log) { - integration()->trace("[host->vm] " + std::string(function_name) + "()"); + integration()->trace("[host<-vm] " + std::string(function_name) + + " return: " + std::to_string(ret)); } - trap.reset(wasm_func_call(func, ¶ms, &results)); - } - - if (trap) { - WasmByteVec error_message; - wasm_trap_message(trap.get(), error_message.get()); - std::string message(error_message.get()->data); // NULL-terminated - fail(FailState::RuntimeError, - "Function: " + std::string(function_name) + " failed: " + message); - return R{}; - } - R ret = convertValueTypeToArg(results.data[0]); - if (log) { - integration()->trace("[host<-vm] " + std::string(function_name) + - " return: " + std::to_string(ret)); - } - return ret; + return ret; + }; }; -}; + + void Wasmtime::warm() { initStore(); } } // namespace wasmtime +bool initWasmtimeEngine() { return wasmtime::engine() != nullptr; } + std::unique_ptr createWasmtimeVm() { return std::make_unique(); } } // namespace proxy_wasm diff --git a/test/wasm_vm_test.cc b/test/wasm_vm_test.cc index 4d084cc7..5c7fa09a 100644 --- a/test/wasm_vm_test.cc +++ b/test/wasm_vm_test.cc @@ -30,6 +30,46 @@ INSTANTIATE_TEST_SUITE_P(WasmEngines, TestVm, testing::ValuesIn(getWasmEngines() return info.param; }); +TEST_P(TestVm, Init) { + std::chrono::time_point time2; + + auto time1 = std::chrono::steady_clock::now(); + if (engine_ == "v8") { +#if defined(PROXY_WASM_HOST_ENGINE_V8) + EXPECT_TRUE(proxy_wasm::initV8Engine()); + time2 = std::chrono::steady_clock::now(); + EXPECT_TRUE(proxy_wasm::initV8Engine()); +#endif + } else if (engine_ == "wamr") { +#if defined(PROXY_WASM_HOST_ENGINE_WAMR) + EXPECT_TRUE(proxy_wasm::initWamrEngine()); + time2 = std::chrono::steady_clock::now(); + EXPECT_TRUE(proxy_wasm::initWamrEngine()); +#endif + } else if (engine_ == "wasmtime") { +#if defined(PROXY_WASM_HOST_ENGINE_WASMTIME) + EXPECT_TRUE(proxy_wasm::initWasmtimeEngine()); + time2 = std::chrono::steady_clock::now(); + EXPECT_TRUE(proxy_wasm::initWasmtimeEngine()); +#endif + } else { + return; + } + auto time3 = std::chrono::steady_clock::now(); + + auto cold = std::chrono::duration_cast(time2 - time1).count(); + auto warm = std::chrono::duration_cast(time3 - time2).count(); + + std::cout << "\"cold\" engine time: " << cold << "ns" << std::endl; + std::cout << "\"warm\" engine time: " << warm << "ns" << std::endl; + + // Verify that getting a "warm" engine takes less than 10us. + EXPECT_LE(warm, 10000); + + // Verify that getting a "warm" engine takes at least 50x less time than getting a "cold" one. + EXPECT_LE(warm * 50, cold); +} + TEST_P(TestVm, Basic) { if (engine_ == "wasmedge") { EXPECT_EQ(vm_->cloneable(), proxy_wasm::Cloneable::NotCloneable);