From 2bf2e872e916988e72e72dd46b19d71708a5da3e Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Wed, 18 Nov 2020 19:26:00 +0100 Subject: [PATCH] api: Add function for resolving imported globals --- lib/fizzy/instantiate.cpp | 105 +++++++++++++++++++++++++----------- lib/fizzy/instantiate.hpp | 24 +++++++-- test/unittests/api_test.cpp | 42 +++++++-------- 3 files changed, 112 insertions(+), 59 deletions(-) diff --git a/lib/fizzy/instantiate.cpp b/lib/fizzy/instantiate.cpp index 85deb070db..574146d96d 100644 --- a/lib/fizzy/instantiate.cpp +++ b/lib/fizzy/instantiate.cpp @@ -227,6 +227,62 @@ Value eval_constant_expression(ConstantExpression expr, return globals[global_idx - imported_globals.size()]; } +ExternalFunction find_imported_function(const std::string& module, const std::string& name, + FuncType module_func_type, const std::vector& imported_functions) +{ + const auto it = std::find_if(imported_functions.begin(), imported_functions.end(), + [module, name](const auto& func) { return module == func.module && name == func.name; }); + + if (it == imported_functions.end()) + { + throw instantiate_error{"imported function " + module + "." + name + " is required"}; + } + + if (module_func_type.inputs != it->inputs) + { + throw instantiate_error{"function " + module + "." + name + + " input types don't match imported function in module"}; + } + if (module_func_type.outputs.empty() && it->output.has_value()) + { + throw instantiate_error{ + "function " + module + "." + name + " has output but is defined void in module"}; + } + if (!module_func_type.outputs.empty() && + (!it->output.has_value() || module_func_type.outputs[0] != *it->output)) + { + throw instantiate_error{"function " + module + "." + name + + " output type doesn't match imported function in module"}; + } + + return {it->function, module_func_type}; +} + +ExternalGlobal find_imported_global(const std::string& module, const std::string& name, + GlobalType module_global_type, const std::vector& imported_globals) +{ + const auto it = std::find_if(imported_globals.begin(), imported_globals.end(), + [module, name](const auto& func) { return module == func.module && name == func.name; }); + + if (it == imported_globals.end()) + { + throw instantiate_error{"imported global " + module + "." + name + " is required"}; + } + + if (module_global_type.value_type != it->type) + { + throw instantiate_error{"global " + module + "." + name + + " value type doesn't match imported global in module"}; + } + if (module_global_type.is_mutable != it->is_mutable) + { + throw instantiate_error{"global " + module + "." + name + + " mutability doesn't match imported global in module"}; + } + + return {it->value, module_global_type}; +} + std::optional find_export(const Module& module, ExternalKind kind, std::string_view name) { const auto it = std::find_if(module.exportsec.begin(), module.exportsec.end(), @@ -369,7 +425,7 @@ std::unique_ptr instantiate(std::unique_ptr module, } std::vector resolve_imported_functions( - const Module& module, std::vector imported_functions) + const Module& module, const std::vector& imported_functions) { std::vector external_functions; for (const auto& import : module.importsec) @@ -377,43 +433,30 @@ std::vector resolve_imported_functions( if (import.kind != ExternalKind::Function) continue; - const auto it = std::find_if( - imported_functions.begin(), imported_functions.end(), [&import](const auto& func) { - return import.module == func.module && import.name == func.name; - }); - - if (it == imported_functions.end()) - { - throw instantiate_error{ - "imported function " + import.module + "." + import.name + " is required"}; - } - assert(import.desc.function_type_index < module.typesec.size()); const auto& module_func_type = module.typesec[import.desc.function_type_index]; - if (module_func_type.inputs != it->inputs) - { - throw instantiate_error{"function " + import.module + "." + import.name + - " input types don't match imported function in module"}; - } - if (module_func_type.outputs.empty() && it->output.has_value()) - { - throw instantiate_error{"function " + import.module + "." + import.name + - " has output but is defined void in module"}; - } - if (!module_func_type.outputs.empty() && - (!it->output.has_value() || module_func_type.outputs[0] != *it->output)) - { - throw instantiate_error{"function " + import.module + "." + import.name + - " output type doesn't match imported function in module"}; - } - - external_functions.emplace_back(ExternalFunction{it->function, module_func_type}); + external_functions.emplace_back(find_imported_function( + import.module, import.name, module_func_type, imported_functions)); } - return external_functions; } +std::vector resolve_imported_globals( + const Module& module, const std::vector& imported_globals) +{ + std::vector external_globals; + for (const auto& import : module.importsec) + { + if (import.kind != ExternalKind::Global) + continue; + + external_globals.emplace_back( + find_imported_global(import.module, import.name, import.desc.global, imported_globals)); + } + return external_globals; +} + std::optional find_exported_function(const Module& module, std::string_view name) { return find_export(module, ExternalKind::Function, name); diff --git a/lib/fizzy/instantiate.hpp b/lib/fizzy/instantiate.hpp index 8a80fcb00a..7cbb07c040 100644 --- a/lib/fizzy/instantiate.hpp +++ b/lib/fizzy/instantiate.hpp @@ -107,7 +107,7 @@ std::unique_ptr instantiate(std::unique_ptr module, std::vector imported_globals = {}, uint32_t memory_pages_limit = DefaultMemoryPagesLimit); -// Function that should be used by instantiate as imports, identified by module and function name. +// Function that should be used by instantiate as import, identified by module and function name. struct ImportedFunction { std::string module; @@ -118,10 +118,26 @@ struct ImportedFunction }; // Create vector of ExternalFunctions ready to be passed to instantiate. -// imported_functions may be in any order, -// but must contain functions for all of the imported function names defined in the module. +// imported_functions may be in any order, but must contain functions for all of the imported +// function names defined in the module. std::vector resolve_imported_functions( - const Module& module, std::vector imported_functions); + const Module& module, const std::vector& imported_functions); + +// Global that should be used by instantiate as import, identified by module and global name. +struct ImportedGlobal +{ + std::string module; + std::string name; + Value* value = nullptr; + ValType type = ValType::i32; + bool is_mutable = false; +}; + +// Create vector of ExternalGlobals ready to be passed to instantiate. +// imported_globals may be in any order, but must contain globals for all of the imported global +// names defined in the module. +std::vector resolve_imported_globals( + const Module& module, const std::vector& imported_globals); // Find exported function index by name. std::optional find_exported_function(const Module& module, std::string_view name); diff --git a/test/unittests/api_test.cpp b/test/unittests/api_test.cpp index 5a77e42054..bac11a1b1d 100644 --- a/test/unittests/api_test.cpp +++ b/test/unittests/api_test.cpp @@ -82,15 +82,14 @@ TEST(api, resolve_imported_functions) "04666f6f320001046d6f643204666f6f310001046d6f643204666f6f320002046d6f64310167037f00"); const auto module = parse(wasm); - std::vector imported_functions = { + const std::vector imported_functions = { {"mod1", "foo1", {}, ValType::i32, function_returning_value(0)}, {"mod1", "foo2", {ValType::i32}, ValType::i32, function_returning_value(1)}, {"mod2", "foo1", {ValType::i32}, ValType::i32, function_returning_value(2)}, {"mod2", "foo2", {ValType::i64, ValType::i32}, std::nullopt, function_returning_void}, }; - const auto external_functions = - resolve_imported_functions(*module, std::move(imported_functions)); + const auto external_functions = resolve_imported_functions(*module, imported_functions); EXPECT_EQ(external_functions.size(), 4); @@ -105,7 +104,7 @@ TEST(api, resolve_imported_functions) EXPECT_THAT(execute(*instance, 3, {0, 0}), Result()); - std::vector imported_functions_reordered = { + const std::vector imported_functions_reordered = { {"mod2", "foo1", {ValType::i32}, ValType::i32, function_returning_value(2)}, {"mod1", "foo2", {ValType::i32}, ValType::i32, function_returning_value(1)}, {"mod1", "foo1", {}, ValType::i32, function_returning_value(0)}, @@ -113,11 +112,11 @@ TEST(api, resolve_imported_functions) }; const auto external_functions_reordered = - resolve_imported_functions(*module, std::move(imported_functions_reordered)); + resolve_imported_functions(*module, imported_functions_reordered); EXPECT_EQ(external_functions_reordered.size(), 4); - auto instance_reordered = instantiate(*module, external_functions_reordered, {}, {}, - std::vector(external_globals)); + auto instance_reordered = + instantiate(*module, external_functions_reordered, {}, {}, external_globals); EXPECT_THAT(execute(*instance_reordered, 0, {}), Result(0)); EXPECT_THAT(execute(*instance_reordered, 1, {Value{0}}), Result(1)); @@ -125,7 +124,7 @@ TEST(api, resolve_imported_functions) EXPECT_THAT(execute(*instance_reordered, 3, {0, 0}), Result()); - std::vector imported_functions_extra = { + const std::vector imported_functions_extra = { {"mod1", "foo1", {}, ValType::i32, function_returning_value(0)}, {"mod1", "foo2", {ValType::i32}, ValType::i32, function_returning_value(1)}, {"mod2", "foo1", {ValType::i32}, ValType::i32, function_returning_value(2)}, @@ -135,11 +134,10 @@ TEST(api, resolve_imported_functions) }; const auto external_functions_extra = - resolve_imported_functions(*module, std::move(imported_functions_extra)); + resolve_imported_functions(*module, imported_functions_extra); EXPECT_EQ(external_functions_extra.size(), 4); - auto instance_extra = instantiate( - *module, external_functions_extra, {}, {}, std::vector(external_globals)); + auto instance_extra = instantiate(*module, external_functions_extra, {}, {}, external_globals); EXPECT_THAT(execute(*instance_extra, 0, {}), Result(0)); EXPECT_THAT(execute(*instance_extra, 1, {Value{0}}), Result(1)); @@ -147,48 +145,45 @@ TEST(api, resolve_imported_functions) EXPECT_THAT(execute(*instance_extra, 3, {0, 0}), Result()); - std::vector imported_functions_missing = { + const std::vector imported_functions_missing = { {"mod1", "foo1", {}, ValType::i32, function_returning_value(0)}, {"mod1", "foo2", {ValType::i32}, ValType::i32, function_returning_value(1)}, {"mod2", "foo1", {ValType::i32}, ValType::i32, function_returning_value(2)}, }; - EXPECT_THROW_MESSAGE(resolve_imported_functions(*module, std::move(imported_functions_missing)), + EXPECT_THROW_MESSAGE(resolve_imported_functions(*module, imported_functions_missing), instantiate_error, "imported function mod2.foo2 is required"); - std::vector imported_functions_invalid_type1 = { + const std::vector imported_functions_invalid_type1 = { {"mod1", "foo1", {ValType::i32}, ValType::i32, function_returning_value(0)}, {"mod1", "foo2", {ValType::i32}, ValType::i32, function_returning_value(1)}, {"mod2", "foo1", {ValType::i32}, ValType::i32, function_returning_value(2)}, {"mod2", "foo2", {ValType::i64, ValType::i32}, std::nullopt, function_returning_void}, }; - EXPECT_THROW_MESSAGE( - resolve_imported_functions(*module, std::move(imported_functions_invalid_type1)), + EXPECT_THROW_MESSAGE(resolve_imported_functions(*module, imported_functions_invalid_type1), instantiate_error, "function mod1.foo1 input types don't match imported function in module"); - std::vector imported_functions_invalid_type2 = { + const std::vector imported_functions_invalid_type2 = { {"mod1", "foo1", {}, ValType::i32, function_returning_value(0)}, {"mod1", "foo2", {ValType::i32}, ValType::i32, function_returning_value(1)}, {"mod2", "foo1", {ValType::i32}, ValType::i32, function_returning_value(2)}, {"mod2", "foo2", {ValType::i64, ValType::i32}, ValType::i64, function_returning_value(3)}, }; - EXPECT_THROW_MESSAGE( - resolve_imported_functions(*module, std::move(imported_functions_invalid_type2)), + EXPECT_THROW_MESSAGE(resolve_imported_functions(*module, imported_functions_invalid_type2), instantiate_error, "function mod2.foo2 has output but is defined void in module"); - std::vector imported_functions_invalid_type3 = { + const std::vector imported_functions_invalid_type3 = { {"mod1", "foo1", {}, ValType::i32, function_returning_value(0)}, {"mod1", "foo2", {ValType::i32}, ValType::i64, function_returning_value(1)}, {"mod2", "foo1", {ValType::i32}, ValType::i32, function_returning_value(2)}, {"mod2", "foo2", {ValType::i64, ValType::i32}, std::nullopt, function_returning_void}, }; - EXPECT_THROW_MESSAGE( - resolve_imported_functions(*module, std::move(imported_functions_invalid_type3)), + EXPECT_THROW_MESSAGE(resolve_imported_functions(*module, imported_functions_invalid_type3), instantiate_error, "function mod1.foo2 output type doesn't match imported function in module"); } @@ -207,8 +202,7 @@ TEST(api, resolve_imported_function_duplicate) {"mod1", "foo1", {ValType::i32}, ValType::i32, function_returning_value(42)}, }; - const auto external_functions = - resolve_imported_functions(*module, std::move(imported_functions)); + const auto external_functions = resolve_imported_functions(*module, imported_functions); EXPECT_EQ(external_functions.size(), 2);