From f09f1f428629ce59b391fceee8fe0aa52c8e10b2 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Wed, 4 Nov 2020 16:27:37 +0100 Subject: [PATCH] capi: Add function to find exported function --- include/fizzy/fizzy.h | 21 ++++++++++++ lib/fizzy/capi.cpp | 42 ++++++++++++++++++++++++ test/unittests/capi_test.cpp | 63 +++++++++++++++++++++++++----------- 3 files changed, 108 insertions(+), 18 deletions(-) diff --git a/include/fizzy/fizzy.h b/include/fizzy/fizzy.h index 27bd19b8b..baf12ce6d 100644 --- a/include/fizzy/fizzy.h +++ b/include/fizzy/fizzy.h @@ -80,6 +80,9 @@ typedef struct FizzyExternalFunction void* context; } FizzyExternalFunction; +/// The opaque data type representing a context of an exported function. +typedef struct FizzyExportedFunctionContext FizzyExportedFunctionContext; + /// Global type. typedef struct FizzyGlobalType { @@ -243,6 +246,24 @@ uint8_t* fizzy_get_instance_memory_data(FizzyInstance* instance); /// @note Function returns memory size regardless of whether memory is exported or not. size_t fizzy_get_instance_memory_size(FizzyInstance* instance); +/// Find exported function by name. +/// +/// @param instance Pointer to instance. +/// @param name The table name. NULL-terminated string. Cannot be NULL. +/// @param out_function Pointer to output struct to store found table. Cannot be NULL. +/// @param out_context_ptr Pointer to opaque output pointer to a context associated with this +/// exported function. Context must exist as long as the returned function +/// can be called by some other instance, and should be destroyed with +/// fizzy_free_exported_function_context afterwards. Cannot be NULL. +/// @returns true if function was found, false otherwise. +bool fizzy_find_exported_function(FizzyInstance* instance, const char* name, + FizzyExternalFunction* out_function, FizzyExportedFunctionContext** out_context_ptr); + +/// Free resources associated with exported function context. +/// +/// If passed pointer is NULL, has no effect. +void fizzy_free_exported_function_context(FizzyExportedFunctionContext* context); + /// Find exported table by name. /// /// @param instance Pointer to instance. diff --git a/lib/fizzy/capi.cpp b/lib/fizzy/capi.cpp index 590d3a3ae..bd609d29a 100644 --- a/lib/fizzy/capi.cpp +++ b/lib/fizzy/capi.cpp @@ -120,6 +120,32 @@ inline auto unwrap(FizzyExternalFn func, void* context) noexcept }; } +inline FizzyExportedFunctionContext* wrap(fizzy::ExternalFunction* external_func) noexcept +{ + return reinterpret_cast(external_func); +} + +inline fizzy::ExternalFunction* unwrap(FizzyExportedFunctionContext* context) noexcept +{ + return reinterpret_cast(context); +} + +inline std::tuple wrap( + fizzy::ExternalFunction external_func) +{ + FizzyExternalFn c_function = [](void* context, FizzyInstance* instance, const FizzyValue* args, + int depth) -> FizzyExecutionResult { + auto* func = static_cast(context); + return wrap((func->function)(*unwrap(instance), unwrap(args), depth)); + }; + + auto context = std::make_unique(std::move(external_func)); + auto c_type = wrap(context->type); + FizzyExportedFunctionContext* c_context = wrap(context.release()); + FizzyExternalFunction func = {c_type, c_function, c_context}; + return {func, c_context}; +} + inline fizzy::ExternalFunction unwrap(const FizzyExternalFunction& external_func) { return fizzy::ExternalFunction{ @@ -380,6 +406,22 @@ size_t fizzy_get_instance_memory_size(FizzyInstance* instance) return memory->size(); } +bool fizzy_find_exported_function(FizzyInstance* instance, const char* name, + FizzyExternalFunction* out_external_function, FizzyExportedFunctionContext** out_context_ptr) +{ + auto optional_func = fizzy::find_exported_function(*unwrap(instance), name); + if (!optional_func) + return false; + + std::tie(*out_external_function, *out_context_ptr) = wrap(std::move(*optional_func)); + return true; +} + +void fizzy_free_exported_function_context(FizzyExportedFunctionContext* context) +{ + delete unwrap(context); +} + FizzyExecutionResult fizzy_execute( FizzyInstance* instance, uint32_t func_idx, const FizzyValue* args, int depth) { diff --git a/test/unittests/capi_test.cpp b/test/unittests/capi_test.cpp index e1c18e911..8043af9b3 100644 --- a/test/unittests/capi_test.cpp +++ b/test/unittests/capi_test.cpp @@ -98,6 +98,45 @@ TEST(capi, find_exported_function_index) fizzy_free_module(module); } +TEST(capi, find_exported_function) +{ + /* wat2wasm + (module + (func $f (export "foo") (result i32) (i32.const 42)) + (global (export "g1") i32 (i32.const 42)) + (table (export "tab") 10 30 anyfunc) + (memory (export "mem") 1 2) + ) + */ + const auto wasm = from_hex( + "0061736d010000000105016000017f0302010004050170010a1e0504010101020606017f00412a0b0718040366" + "6f6f00000267310300037461620100036d656d02000a06010400412a0b"); + + auto module = fizzy_parse(wasm.data(), wasm.size()); + ASSERT_NE(module, nullptr); + + auto instance = fizzy_instantiate(module, nullptr, 0, nullptr, nullptr, 0); + ASSERT_NE(instance, nullptr); + + FizzyExternalFunction function; + FizzyExportedFunctionContext* context; + ASSERT_TRUE(fizzy_find_exported_function(instance, "foo", &function, &context)); + EXPECT_EQ(function.type.inputs_size, 0); + EXPECT_EQ(function.type.output, FizzyValueTypeI32); + EXPECT_EQ(function.context, context); + ASSERT_NE(function.function, nullptr); + EXPECT_THAT(function.function(context, instance, nullptr, 0), CResult(42)); + + fizzy_free_exported_function_context(context); + + EXPECT_FALSE(fizzy_find_exported_function(instance, "foo2", &function, &context)); + EXPECT_FALSE(fizzy_find_exported_function(instance, "g1", &function, &context)); + EXPECT_FALSE(fizzy_find_exported_function(instance, "tab", &function, &context)); + EXPECT_FALSE(fizzy_find_exported_function(instance, "mem", &function, &context)); + + fizzy_free_instance(instance); +} + TEST(capi, find_exported_table) { /* wat2wasm @@ -623,6 +662,10 @@ TEST(capi, imported_function_from_another_module) auto instance1 = fizzy_instantiate(module1, nullptr, 0, nullptr, nullptr, 0); ASSERT_NE(instance1, nullptr); + FizzyExternalFunction func; + FizzyExportedFunctionContext* context; + ASSERT_TRUE(fizzy_find_exported_function(instance1, "sub", &func, &context)); + /* wat2wasm (module (func $sub (import "m1" "sub") (param $lhs i32) (param $rhs i32) (result i32)) @@ -640,29 +683,13 @@ TEST(capi, imported_function_from_another_module) auto module2 = fizzy_parse(bin2.data(), bin2.size()); ASSERT_NE(module2, nullptr); - uint32_t func_idx; - ASSERT_TRUE(fizzy_find_exported_function_index(module1, "sub", &func_idx)); - - auto host_context = std::make_pair(instance1, func_idx); - - auto sub = [](void* context, FizzyInstance*, const FizzyValue* args, - int depth) -> FizzyExecutionResult { - const auto* instance_and_func_idx = - static_cast*>(context); - return fizzy_execute( - instance_and_func_idx->first, instance_and_func_idx->second, args, depth + 1); - }; - - const FizzyValueType inputs[] = {FizzyValueTypeI32, FizzyValueTypeI32}; - - FizzyExternalFunction host_funcs[] = {{{FizzyValueTypeI32, &inputs[0], 2}, sub, &host_context}}; - - auto instance2 = fizzy_instantiate(module2, host_funcs, 1, nullptr, nullptr, 0); + auto instance2 = fizzy_instantiate(module2, &func, 1, nullptr, nullptr, 0); ASSERT_NE(instance2, nullptr); FizzyValue args[] = {{44}, {2}}; EXPECT_THAT(fizzy_execute(instance2, 1, args, 0), CResult(42)); + fizzy_free_exported_function_context(context); fizzy_free_instance(instance2); fizzy_free_instance(instance1); }