From 0b3c94e2f0b64235b6f692bde2046f169537ac2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 22 Mar 2021 10:38:14 +0100 Subject: [PATCH 1/5] Add ExecutionContext type The ExecutionContext type represents the storage for information shared by calls in the same execution "thread". --- lib/fizzy/execute.hpp | 28 ++++++++++++++++++++++++++++ lib/fizzy/instantiate.hpp | 1 + 2 files changed, 29 insertions(+) diff --git a/lib/fizzy/execute.hpp b/lib/fizzy/execute.hpp index dd4793538..0a0b8a154 100644 --- a/lib/fizzy/execute.hpp +++ b/lib/fizzy/execute.hpp @@ -41,6 +41,34 @@ constexpr ExecutionResult Void{true}; /// Shortcut for execution that resulted in a trap. constexpr ExecutionResult Trap{false}; +/// The storage for information shared by calls in the same execution "thread". +/// Users may decide how to allocate the execution context, but some good defaults are available. +class ExecutionContext +{ + /// Call depth increment guard. + /// It will automatically decrement the call depth to the original value + /// when going out of scope. + class [[nodiscard]] Guard + { + ExecutionContext& m_execution_context; ///< Reference to the guarded execution context. + + public: + explicit Guard(ExecutionContext& ctx) noexcept : m_execution_context{ctx} {} + ~Guard() noexcept { --m_execution_context.depth; } + }; + +public: + int depth = 0; ///< Current call depth. + + /// Increments the call depth and returns the guard object which decrements + /// the call depth back to the original value when going out of scope. + Guard increment_call_depth() noexcept + { + ++depth; + return Guard{*this}; + } +}; + /// Execute a function from an instance. /// /// @param instance The instance. diff --git a/lib/fizzy/instantiate.hpp b/lib/fizzy/instantiate.hpp index 25100356e..651a5424f 100644 --- a/lib/fizzy/instantiate.hpp +++ b/lib/fizzy/instantiate.hpp @@ -20,6 +20,7 @@ namespace fizzy { struct ExecutionResult; +class ExecutionContext; struct Instance; /// Function pointer to the execution function. From 4bb28174f659a93551dae30cefe90933bf89b241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 23 Mar 2021 13:48:28 +0100 Subject: [PATCH 2/5] Change host function to receive ExecutionContext& Instead of the call depth value. --- include/fizzy/fizzy.h | 9 ++- lib/fizzy/capi.cpp | 36 +++++++----- lib/fizzy/execute.cpp | 5 +- lib/fizzy/instantiate.cpp | 6 +- lib/fizzy/instantiate.hpp | 5 +- test/unittests/api_test.cpp | 18 +++--- test/unittests/capi_test.cpp | 28 ++++++---- test/unittests/execute_call_depth_test.cpp | 65 ++++++++++++---------- test/unittests/execute_call_test.cpp | 29 ++++++---- test/unittests/execute_test.cpp | 18 +++--- test/unittests/instantiate_test.cpp | 12 ++-- test/utils/fizzy_c_engine.cpp | 3 +- test/utils/fizzy_engine.cpp | 4 +- tools/wasi/wasi.cpp | 16 ++++-- 14 files changed, 148 insertions(+), 106 deletions(-) diff --git a/include/fizzy/fizzy.h b/include/fizzy/fizzy.h index d01d33962..71cdb87f1 100644 --- a/include/fizzy/fizzy.h +++ b/include/fizzy/fizzy.h @@ -46,20 +46,23 @@ typedef struct FizzyExecutionResult FizzyValue value; } FizzyExecutionResult; +/// The opaque data type representing an execution context. +typedef struct FizzyExecutionContext FizzyExecutionContext; + /// Pointer to external function. /// -/// @param context Opaque pointer to execution context. +/// @param host_ctx Opaque pointer to host context. /// @param instance Pointer to module instance. /// @param args Pointer to the argument array. Can be NULL iff function has no inputs. -/// @param depth Call stack depth. +/// @param ctx Opaque pointer to execution context. /// @return Result of execution. /// /// @note /// External functions implemented in C++ must be non-throwing, i.e. the effect of any exception /// escaping the function is std::terminate being called. typedef FizzyExecutionResult (*FizzyExternalFn)( - void* context, FizzyInstance* instance, const FizzyValue* args, int depth); + void* host_ctx, FizzyInstance* instance, const FizzyValue* args, FizzyExecutionContext* ctx); /// Value type. typedef uint8_t FizzyValueType; diff --git a/lib/fizzy/capi.cpp b/lib/fizzy/capi.cpp index f35bb0960..245b52593 100644 --- a/lib/fizzy/capi.cpp +++ b/lib/fizzy/capi.cpp @@ -85,6 +85,16 @@ inline fizzy::Value* unwrap(FizzyValue* value) noexcept return reinterpret_cast(value); } +inline FizzyExecutionContext* wrap(fizzy::ExecutionContext& ctx) noexcept +{ + return reinterpret_cast(&ctx); +} + +inline fizzy::ExecutionContext& unwrap(FizzyExecutionContext* ctx) noexcept +{ + return *reinterpret_cast(ctx); +} + inline FizzyInstance* wrap(fizzy::Instance* instance) noexcept { return reinterpret_cast(instance); @@ -113,11 +123,11 @@ inline fizzy::ExecutionResult unwrap(const FizzyExecutionResult& result) noexcep inline fizzy::ExecuteFunction unwrap(FizzyExternalFn c_function, void* c_host_context) { static constexpr fizzy::HostFunctionPtr function = - [](std::any& host_context, fizzy::Instance& instance, const fizzy::Value* args, - int depth) noexcept { + [](std::any& host_ctx, fizzy::Instance& instance, const fizzy::Value* args, + fizzy::ExecutionContext& ctx) noexcept { const auto [c_func, c_host_ctx] = - *std::any_cast>(&host_context); - return unwrap(c_func(c_host_ctx, wrap(&instance), wrap(args), depth)); + *std::any_cast>(&host_ctx); + return unwrap(c_func(c_host_ctx, wrap(&instance), wrap(args), wrap(ctx))); }; return {function, std::make_any>(c_function, c_host_context)}; @@ -125,17 +135,17 @@ inline fizzy::ExecuteFunction unwrap(FizzyExternalFn c_function, void* c_host_co inline FizzyExternalFunction wrap(fizzy::ExternalFunction external_func) { - static constexpr 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)); + static constexpr FizzyExternalFn c_function = + [](void* host_ctx, FizzyInstance* instance, const FizzyValue* args, + FizzyExecutionContext* ctx) -> FizzyExecutionResult { + auto* func = static_cast(host_ctx); + return wrap((func->function)(*unwrap(instance), unwrap(args), unwrap(ctx))); }; - auto context = std::make_unique(std::move(external_func)); - const auto c_type = wrap(context->input_types, context->output_types); - void* c_context = context.release(); - return {c_type, c_function, c_context}; + auto host_ctx = std::make_unique(std::move(external_func)); + const auto c_type = wrap(host_ctx->input_types, host_ctx->output_types); + void* c_host_ctx = host_ctx.release(); + return {c_type, c_function, c_host_ctx}; } inline fizzy::ExternalFunction unwrap(const FizzyExternalFunction& external_func) diff --git a/lib/fizzy/execute.cpp b/lib/fizzy/execute.cpp index f0f8d59b4..8e59f2fd4 100644 --- a/lib/fizzy/execute.cpp +++ b/lib/fizzy/execute.cpp @@ -559,6 +559,9 @@ inline bool invoke_function(const FuncType& func_type, uint32_t func_idx, Instan ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args, int depth) noexcept { + ExecutionContext ctx; + ctx.depth = depth; + assert(depth >= 0); if (depth >= CallStackLimit) return Trap; @@ -567,7 +570,7 @@ ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args, assert(instance.module->imported_function_types.size() == instance.imported_functions.size()); if (func_idx < instance.imported_functions.size()) - return instance.imported_functions[func_idx].function(instance, args, depth); + return instance.imported_functions[func_idx].function(instance, args, ctx); const auto& code = instance.module->get_code(func_idx); auto* const memory = instance.memory.get(); diff --git a/lib/fizzy/instantiate.cpp b/lib/fizzy/instantiate.cpp index 20b79d0e0..7a0b3f27a 100644 --- a/lib/fizzy/instantiate.cpp +++ b/lib/fizzy/instantiate.cpp @@ -313,12 +313,12 @@ std::optional find_export(const Module& module, ExternalKind kind, std } // namespace ExecutionResult ExecuteFunction::operator()( - Instance& instance, const Value* args, int depth) noexcept + Instance& instance, const Value* args, ExecutionContext& ctx) noexcept { if (m_instance) - return execute(*m_instance, m_func_idx, args, depth); + return execute(*m_instance, m_func_idx, args, ctx.depth); else - return m_host_function(m_host_context, instance, args, depth); + return m_host_function(m_host_context, instance, args, ctx); } std::unique_ptr instantiate(std::unique_ptr module, diff --git a/lib/fizzy/instantiate.hpp b/lib/fizzy/instantiate.hpp index 651a5424f..5879800cc 100644 --- a/lib/fizzy/instantiate.hpp +++ b/lib/fizzy/instantiate.hpp @@ -25,7 +25,7 @@ struct Instance; /// Function pointer to the execution function. using HostFunctionPtr = ExecutionResult (*)( - std::any& host_context, Instance&, const Value* args, int depth) noexcept; + std::any& host_context, Instance&, const Value* args, ExecutionContext& ctx) noexcept; /// Function representing either WebAssembly or host function execution. class ExecuteFunction @@ -64,7 +64,8 @@ class ExecuteFunction {} /// Function call operator. - ExecutionResult operator()(Instance& instance, const Value* args, int depth) noexcept; + ExecutionResult operator()( + Instance& instance, const Value* args, ExecutionContext& ctx) noexcept; /// Function pointer stored inside this object. HostFunctionPtr get_host_function() const noexcept { return m_host_function; } diff --git a/test/unittests/api_test.cpp b/test/unittests/api_test.cpp index 1b2259af6..f23ec099b 100644 --- a/test/unittests/api_test.cpp +++ b/test/unittests/api_test.cpp @@ -18,14 +18,15 @@ namespace ExecuteFunction function_returning_value(Value value) { static constexpr HostFunctionPtr func = [](std::any& host_context, Instance&, const Value*, - int) noexcept -> ExecutionResult { + ExecutionContext&) noexcept -> ExecutionResult { return *std::any_cast(&host_context); }; return {func, std::make_any(value)}; } -ExecutionResult function_returning_void(std::any&, Instance&, const Value*, int) noexcept +ExecutionResult function_returning_void( + std::any&, Instance&, const Value*, ExecutionContext&) noexcept { return Void; } @@ -227,7 +228,8 @@ TEST(api, resolve_imported_function_duplicate_with_context) "0061736d01000000010401600000021902046d6f643104666f6f310000046d6f643104666f6f310000"); const auto module = parse(wasm); - constexpr auto host_func = [](std::any& context, Instance&, const Value*, int) noexcept { + constexpr auto host_func = [](std::any& context, Instance&, const Value*, + ExecutionContext&) noexcept { auto* counter = *std::any_cast(&context); ++(*counter); return Void; @@ -446,7 +448,8 @@ TEST(api, find_exported_function) auto opt_function = find_exported_function(*instance, "foo"); ASSERT_TRUE(opt_function); - EXPECT_THAT(TypedExecutionResult(opt_function->function(*instance, {}, 0), ValType::i32), + ExecutionContext ctx; + EXPECT_THAT(TypedExecutionResult(opt_function->function(*instance, {}, ctx), ValType::i32), Result(42_u32)); EXPECT_TRUE(opt_function->input_types.empty()); ASSERT_EQ(opt_function->output_types.size(), 1); @@ -466,9 +469,8 @@ TEST(api, find_exported_function) "0061736d010000000105016000017f021001087370656374657374036261720000040401700000050401010102" "0606017f0041000b07170403666f6f000001670300037461620100036d656d0200"); - constexpr auto bar = [](std::any&, Instance&, const Value*, int) noexcept -> ExecutionResult { - return Value{42}; - }; + constexpr auto bar = [](std::any&, Instance&, const Value*, + ExecutionContext&) noexcept -> ExecutionResult { return Value{42}; }; const auto bar_type = FuncType{{}, {ValType::i32}}; auto instance_reexported_function = @@ -477,7 +479,7 @@ TEST(api, find_exported_function) auto opt_reexported_function = find_exported_function(*instance_reexported_function, "foo"); ASSERT_TRUE(opt_reexported_function); EXPECT_THAT( - TypedExecutionResult(opt_reexported_function->function(*instance, {}, 0), ValType::i32), + TypedExecutionResult(opt_reexported_function->function(*instance, {}, ctx), ValType::i32), Result(42_u32)); EXPECT_TRUE(opt_reexported_function->input_types.empty()); ASSERT_EQ(opt_reexported_function->output_types.size(), 1); diff --git a/test/unittests/capi_test.cpp b/test/unittests/capi_test.cpp index 61679e938..982a8d182 100644 --- a/test/unittests/capi_test.cpp +++ b/test/unittests/capi_test.cpp @@ -330,7 +330,9 @@ TEST(capi, find_exported_function) EXPECT_EQ(function.type.output, FizzyValueTypeI32); EXPECT_NE(function.context, nullptr); ASSERT_NE(function.function, nullptr); - EXPECT_THAT(function.function(function.context, instance, nullptr, 0), CResult(42_u32)); + fizzy::ExecutionContext ctx; + auto* const c_ctx = reinterpret_cast(&ctx); + EXPECT_THAT(function.function(function.context, instance, nullptr, c_ctx), CResult(42_u32)); fizzy_free_exported_function(&function); @@ -696,7 +698,8 @@ TEST(capi, resolve_instantiate_functions) module = fizzy_parse(wasm.data(), wasm.size()); ASSERT_NE(module, nullptr); - FizzyExternalFn host_fn = [](void* context, FizzyInstance*, const FizzyValue*, int) { + FizzyExternalFn host_fn = [](void* context, FizzyInstance*, const FizzyValue*, + FizzyExecutionContext*) { return FizzyExecutionResult{false, true, *static_cast(context)}; }; @@ -764,7 +767,7 @@ TEST(capi, resolve_instantiate_function_duplicate) auto module = fizzy_parse(wasm.data(), wasm.size()); ASSERT_NE(module, nullptr); - FizzyExternalFn host_fn = [](void*, FizzyInstance*, const FizzyValue*, int) { + FizzyExternalFn host_fn = [](void*, FizzyInstance*, const FizzyValue*, FizzyExecutionContext*) { return FizzyExecutionResult{false, true, FizzyValue{42}}; }; @@ -805,7 +808,7 @@ TEST(capi, resolve_instantiate_globals) module = fizzy_parse(wasm.data(), wasm.size()); ASSERT_NE(module, nullptr); - FizzyExternalFn host_fn = [](void*, FizzyInstance*, const FizzyValue*, int) { + FizzyExternalFn host_fn = [](void*, FizzyInstance*, const FizzyValue*, FizzyExecutionContext*) { return FizzyExecutionResult{true, false, {0}}; }; FizzyImportedFunction mod1foo1 = { @@ -1096,13 +1099,14 @@ TEST(capi, execute_with_host_function) const FizzyValueType inputs[] = {FizzyValueTypeI32, FizzyValueTypeI32}; - FizzyExternalFunction host_funcs[] = {{{FizzyValueTypeI32, nullptr, 0}, - [](void*, FizzyInstance*, const FizzyValue*, int) { - return FizzyExecutionResult{false, true, {42}}; - }, - nullptr}, + FizzyExternalFunction host_funcs[] = { + {{FizzyValueTypeI32, nullptr, 0}, + [](void*, FizzyInstance*, const FizzyValue*, FizzyExecutionContext*) { + return FizzyExecutionResult{false, true, {42}}; + }, + nullptr}, {{FizzyValueTypeI32, &inputs[0], 2}, - [](void*, FizzyInstance*, const FizzyValue* args, int) { + [](void*, FizzyInstance*, const FizzyValue* args, FizzyExecutionContext*) { FizzyValue v; v.i32 = args[0].i32 / args[1].i32; return FizzyExecutionResult{false, true, {v}}; @@ -1134,7 +1138,7 @@ TEST(capi, imported_function_traps) ASSERT_NE(module, nullptr); FizzyExternalFunction host_funcs[] = {{{FizzyValueTypeI32, nullptr, 0}, - [](void*, FizzyInstance*, const FizzyValue*, int) { + [](void*, FizzyInstance*, const FizzyValue*, FizzyExecutionContext*) { return FizzyExecutionResult{true, false, {}}; }, nullptr}}; @@ -1162,7 +1166,7 @@ TEST(capi, imported_function_void) bool called = false; FizzyExternalFunction host_funcs[] = {{{}, - [](void* context, FizzyInstance*, const FizzyValue*, int) { + [](void* context, FizzyInstance*, const FizzyValue*, FizzyExecutionContext*) { *static_cast(context) = true; return FizzyExecutionResult{false, false, {}}; }, diff --git a/test/unittests/execute_call_depth_test.cpp b/test/unittests/execute_call_depth_test.cpp index c40a65f1a..e50250091 100644 --- a/test/unittests/execute_call_depth_test.cpp +++ b/test/unittests/execute_call_depth_test.cpp @@ -61,8 +61,8 @@ TEST(execute_call_depth, execute_imported_host_function) const auto wasm = from_hex("0061736d010000000105016000017f020a0104686f737401660000"); static int recorded_depth; - constexpr auto host_f = [](std::any&, Instance&, const Value*, int depth) noexcept { - recorded_depth = depth; + constexpr auto host_f = [](std::any&, Instance&, const Value*, ExecutionContext& ctx) noexcept { + recorded_depth = ctx.depth; return ExecutionResult{Value{1}}; }; @@ -95,9 +95,10 @@ TEST(execute_call_depth, execute_imported_host_function_calling_wasm_function) from_hex("0061736d010000000105016000017f020a0104686f737401660000030201000a0601040041010b"); static int recorded_depth; - constexpr auto host_f = [](std::any&, Instance& instance, const Value*, int depth) noexcept { - recorded_depth = depth; - return fizzy::execute(instance, 1, {}, depth); + constexpr auto host_f = [](std::any&, Instance& instance, const Value*, + ExecutionContext& ctx) noexcept { + recorded_depth = ctx.depth; + return fizzy::execute(instance, 1, {}, ctx.depth); }; const auto module = parse(wasm); @@ -169,8 +170,8 @@ TEST(execute_call_depth, call_imported_host_function) from_hex("0061736d010000000105016000017f020a0104686f737401660000030201000a0601040010000b"); static int recorded_depth; - constexpr auto host_f = [](std::any&, Instance&, const Value*, int depth) noexcept { - recorded_depth = depth; + constexpr auto host_f = [](std::any&, Instance&, const Value*, ExecutionContext& ctx) noexcept { + recorded_depth = ctx.depth; return ExecutionResult{Value{1}}; }; @@ -209,9 +210,10 @@ TEST(execute_call_depth, call_host_function_calling_wasm_function_inclusive) "b"); static int recorded_depth; - constexpr auto host_f = [](std::any&, Instance& instance, const Value*, int depth) noexcept { - recorded_depth = depth; - return fizzy::execute(instance, 2 /* $leaf */, {}, depth + 1); + constexpr auto host_f = [](std::any&, Instance& instance, const Value*, + ExecutionContext& ctx) noexcept { + recorded_depth = ctx.depth; + return fizzy::execute(instance, 2 /* $leaf */, {}, ctx.depth + 1); }; const auto module = parse(wasm); @@ -253,9 +255,10 @@ TEST(execute_call_depth, call_host_function_calling_wasm_function_exclusive) "b"); static int recorded_depth; - constexpr auto host_f = [](std::any&, Instance& instance, const Value*, int depth) noexcept { - recorded_depth = depth; - return fizzy::execute(instance, 2, {}, depth); + constexpr auto host_f = [](std::any&, Instance& instance, const Value*, + ExecutionContext& ctx) noexcept { + recorded_depth = ctx.depth; + return fizzy::execute(instance, 2, {}, ctx.depth); }; const auto module = parse(wasm); @@ -297,10 +300,10 @@ TEST(execute_call_depth, call_host_function_calling_another_wasm_module) static int recorded_depth; constexpr auto host_f = [](std::any& host_context, Instance&, const Value*, - int depth) noexcept { - recorded_depth = depth; + ExecutionContext& ctx) noexcept { + recorded_depth = ctx.depth; auto instance = *std::any_cast(&host_context); - return fizzy::execute(*instance, 0, {}, depth + 1); + return fizzy::execute(*instance, 0, {}, ctx.depth + 1); }; auto another_instance = instantiate(parse(another_wasm)); @@ -458,9 +461,10 @@ TEST(execute_call_depth, execute_host_function_within_wasm_recursion_limit) static int max_recorded_wasm_recursion_depth; - constexpr auto host_f = [](std::any&, Instance& instance, const Value*, int depth) noexcept { - max_recorded_wasm_recursion_depth = std::max(max_recorded_wasm_recursion_depth, depth); - return fizzy::execute(instance, 0, {}, depth + 1); + constexpr auto host_f = [](std::any&, Instance& instance, const Value*, + ExecutionContext& ctx) noexcept { + max_recorded_wasm_recursion_depth = std::max(max_recorded_wasm_recursion_depth, ctx.depth); + return fizzy::execute(instance, 0, {}, ctx.depth + 1); }; const auto module = parse(wasm); @@ -485,16 +489,17 @@ TEST(execute_call_depth, execute_host_function_with_custom_recursion_limit) static int max_recorded_wasm_recursion_depth; static int max_recorded_host_recursion_depth; - constexpr auto host_f = [](std::any&, Instance& instance, const Value*, int depth) noexcept { + constexpr auto host_f = [](std::any&, Instance& instance, const Value*, + ExecutionContext& ctx) noexcept { ++host_recursion_depth; - assert(depth == 0); - max_recorded_wasm_recursion_depth = std::max(max_recorded_wasm_recursion_depth, depth); + assert(ctx.depth == 0); + max_recorded_wasm_recursion_depth = std::max(max_recorded_wasm_recursion_depth, ctx.depth); max_recorded_host_recursion_depth = std::max(max_recorded_host_recursion_depth, host_recursion_depth); const auto result = (host_recursion_depth < host_recursion_limit) ? - fizzy::execute(instance, 0, {}, depth) : + fizzy::execute(instance, 0, {}, ctx.depth) : ExecutionResult{Value{1}}; --host_recursion_depth; return result; @@ -525,10 +530,11 @@ TEST(execute_call, call_host_function_calling_wasm_interleaved_infinite_recursio from_hex("0061736d010000000105016000017f020a0104686f737401660000030201000a0601040010000b"); static int counter = 0; - constexpr auto host_f = [](std::any&, Instance& instance, const Value*, int depth) noexcept { - EXPECT_LT(depth, DepthLimit); + constexpr auto host_f = [](std::any&, Instance& instance, const Value*, + ExecutionContext& ctx) noexcept { + EXPECT_LT(ctx.depth, DepthLimit); ++counter; - return fizzy::execute(instance, 1, {}, depth + 1); + return fizzy::execute(instance, 1, {}, ctx.depth + 1); }; const auto module = parse(wasm); @@ -558,10 +564,11 @@ TEST(execute_call, call_host_function_calling_wasm_interleaved_infinite_recursio from_hex("0061736d010000000105016000017f020a0104686f737401660000030201000a0601040010000b"); static int counter = 0; - constexpr auto host_f = [](std::any&, Instance& instance, const Value*, int depth) noexcept { - EXPECT_LT(depth, DepthLimit); + constexpr auto host_f = [](std::any&, Instance& instance, const Value*, + ExecutionContext& ctx) noexcept { + EXPECT_LT(ctx.depth, DepthLimit); ++counter; - return fizzy::execute(instance, 1, {}, depth); + return fizzy::execute(instance, 1, {}, ctx.depth); }; const auto module = parse(wasm); diff --git a/test/unittests/execute_call_test.cpp b/test/unittests/execute_call_test.cpp index b758c0c31..05f9553e5 100644 --- a/test/unittests/execute_call_test.cpp +++ b/test/unittests/execute_call_test.cpp @@ -341,7 +341,9 @@ TEST(execute_call, imported_function_call) const auto module = parse(wasm); constexpr auto host_foo = [](std::any&, Instance&, const Value*, - int) noexcept -> ExecutionResult { return Value{42}; }; + ExecutionContext&) noexcept -> ExecutionResult { + return Value{42}; + }; const auto& host_foo_type = module->typesec[0]; auto instance = instantiate(*module, {{{host_foo}, host_foo_type}}); @@ -363,7 +365,7 @@ TEST(execute_call, imported_function_call_void) const auto module = parse(wasm); static bool called = false; - constexpr auto host_foo = [](std::any&, Instance&, const Value*, int) noexcept { + constexpr auto host_foo = [](std::any&, Instance&, const Value*, ExecutionContext&) noexcept { called = true; return Void; }; @@ -392,7 +394,7 @@ TEST(execute_call, imported_function_call_with_arguments) const auto module = parse(wasm); constexpr auto host_foo = [](std::any&, Instance&, const Value* args, - int) noexcept -> ExecutionResult { + ExecutionContext&) noexcept -> ExecutionResult { return Value{args[0].i32 * 2}; }; const auto host_foo_type = module->typesec[0]; @@ -437,12 +439,12 @@ TEST(execute_call, imported_functions_call_indirect) ASSERT_EQ(module->codesec.size(), 2); constexpr auto sqr = [](std::any&, Instance&, const Value* args, - int) noexcept -> ExecutionResult { + ExecutionContext&) noexcept -> ExecutionResult { const auto x = args[0].i32; return Value{uint64_t{x} * uint64_t{x}}; }; constexpr auto isqrt = [](std::any&, Instance&, const Value* args, - int) noexcept -> ExecutionResult { + ExecutionContext&) noexcept -> ExecutionResult { const auto x = args[0].i32; return Value{(11 + uint64_t{x} / 11) / 2}; }; @@ -566,9 +568,9 @@ TEST(execute_call, imported_function_from_another_module_via_host_function) ASSERT_TRUE(func_idx.has_value()); constexpr auto sub = [](std::any& host_context, Instance&, const Value* args, - int depth) noexcept { + ExecutionContext& ctx) noexcept { auto [inst1, idx] = *std::any_cast>(&host_context); - return fizzy::execute(*inst1, idx, args, depth); + return fizzy::execute(*inst1, idx, args, ctx.depth); }; auto host_context = std::make_any>(instance1.get(), *func_idx); @@ -593,7 +595,8 @@ TEST(execute_call, imported_function_with_context) const auto module = parse(wasm); - constexpr auto host_func = [](std::any& host_context, Instance&, const Value*, int) noexcept { + constexpr auto host_func = [](std::any& host_context, Instance&, const Value*, + ExecutionContext&) noexcept { ++(**std::any_cast(&host_context)); return Void; }; @@ -626,12 +629,13 @@ TEST(execute_call, imported_functions_with_shared_context) const auto module = parse(wasm); - constexpr auto host_func1 = [](std::any& host_context, Instance&, const Value*, int) noexcept { + constexpr auto host_func1 = [](std::any& host_context, Instance&, const Value*, + ExecutionContext&) noexcept { ++(**std::any_cast(&host_context)); return Void; }; constexpr auto host_func2 = [](std::any& host_context, Instance&, const Value*, - int) noexcept -> ExecutionResult { + ExecutionContext&) noexcept -> ExecutionResult { return Value{**std::any_cast(&host_context)}; }; @@ -666,12 +670,13 @@ TEST(execute_call, imported_functions_with_non_shared_context) const auto module = parse(wasm); - constexpr auto host_func1 = [](std::any& host_context, Instance&, const Value*, int) noexcept { + constexpr auto host_func1 = [](std::any& host_context, Instance&, const Value*, + ExecutionContext&) noexcept { ++(*std::any_cast(&host_context)); return Void; }; constexpr auto host_func2 = [](std::any& host_context, Instance&, const Value*, - int) noexcept -> ExecutionResult { + ExecutionContext&) noexcept -> ExecutionResult { return Value{*std::any_cast(&host_context)}; }; diff --git a/test/unittests/execute_test.cpp b/test/unittests/execute_test.cpp index b990d77b3..33e977a0a 100644 --- a/test/unittests/execute_test.cpp +++ b/test/unittests/execute_test.cpp @@ -829,7 +829,7 @@ TEST(execute, imported_function) ASSERT_EQ(module->typesec.size(), 1); constexpr auto host_foo = [](std::any&, Instance&, const Value* args, - int) noexcept -> ExecutionResult { + ExecutionContext&) noexcept -> ExecutionResult { return Value{args[0].i32 + args[1].i32}; }; @@ -850,11 +850,11 @@ TEST(execute, imported_two_functions) ASSERT_EQ(module->typesec.size(), 1); constexpr auto host_foo1 = [](std::any&, Instance&, const Value* args, - int) noexcept -> ExecutionResult { + ExecutionContext&) noexcept -> ExecutionResult { return Value{args[0].i32 + args[1].i32}; }; constexpr auto host_foo2 = [](std::any&, Instance&, const Value* args, - int) noexcept -> ExecutionResult { + ExecutionContext&) noexcept -> ExecutionResult { return Value{args[0].i32 * args[1].i32}; }; @@ -879,11 +879,11 @@ TEST(execute, imported_functions_and_regular_one) "000a0901070041aa80a8010b"); constexpr auto host_foo1 = [](std::any&, Instance&, const Value* args, - int) noexcept -> ExecutionResult { + ExecutionContext&) noexcept -> ExecutionResult { return Value{args[0].i32 + args[1].i32}; }; constexpr auto host_foo2 = [](std::any&, Instance&, const Value* args, - int) noexcept -> ExecutionResult { + ExecutionContext&) noexcept -> ExecutionResult { return Value{args[0].i32 * args[1].i32}; }; @@ -911,11 +911,11 @@ TEST(execute, imported_two_functions_different_type) "0001030201010a0901070042aa80a8010b"); constexpr auto host_foo1 = [](std::any&, Instance&, const Value* args, - int) noexcept -> ExecutionResult { + ExecutionContext&) noexcept -> ExecutionResult { return Value{args[0].i32 + args[1].i32}; }; constexpr auto host_foo2 = [](std::any&, Instance&, const Value* args, - int) noexcept -> ExecutionResult { + ExecutionContext&) noexcept -> ExecutionResult { return Value{args[0].i64 * args[0].i64}; }; @@ -936,7 +936,9 @@ TEST(execute, imported_function_traps) */ const auto wasm = from_hex("0061736d0100000001070160027f7f017f020b01036d6f6403666f6f0000"); - constexpr auto host_foo = [](std::any&, Instance&, const Value*, int) noexcept { return Trap; }; + constexpr auto host_foo = [](std::any&, Instance&, const Value*, ExecutionContext&) noexcept { + return Trap; + }; const auto module = parse(wasm); auto instance = instantiate(*module, {{{host_foo}, module->typesec[0]}}); diff --git a/test/unittests/instantiate_test.cpp b/test/unittests/instantiate_test.cpp index 8f341d04f..b9a9df514 100644 --- a/test/unittests/instantiate_test.cpp +++ b/test/unittests/instantiate_test.cpp @@ -15,12 +15,12 @@ using namespace fizzy::test; namespace { -ExecutionResult host_fn_1(std::any&, Instance&, const Value*, int) noexcept +ExecutionResult host_fn_1(std::any&, Instance&, const Value*, ExecutionContext&) noexcept { return Trap; } -ExecutionResult host_fn_2(std::any&, Instance&, const Value*, int) noexcept +ExecutionResult host_fn_2(std::any&, Instance&, const Value*, ExecutionContext&) noexcept { return Trap; } @@ -37,10 +37,10 @@ uint32_t call_table_func(Instance& instance, size_t idx) TEST(instantiate, check_test_host_functions) { Instance instance{{}, {nullptr, nullptr}, {}, {}, {nullptr, nullptr}, {}, {}, {}, {}}; - std::any context1; - EXPECT_THAT(host_fn_1(context1, instance, nullptr, 0), Traps()); - std::any context2; - EXPECT_THAT(host_fn_2(context2, instance, nullptr, 0), Traps()); + std::any host_ctx; + ExecutionContext execution_ctx; + EXPECT_THAT(host_fn_1(host_ctx, instance, nullptr, execution_ctx), Traps()); + EXPECT_THAT(host_fn_2(host_ctx, instance, nullptr, execution_ctx), Traps()); } TEST(instantiate, imported_functions) diff --git a/test/utils/fizzy_c_engine.cpp b/test/utils/fizzy_c_engine.cpp index e476550a3..cb3c523e2 100644 --- a/test/utils/fizzy_c_engine.cpp +++ b/test/utils/fizzy_c_engine.cpp @@ -28,7 +28,8 @@ class FizzyCEngine final : public WasmEngine namespace { -FizzyExecutionResult env_adler32(void*, FizzyInstance* instance, const FizzyValue* args, int) +FizzyExecutionResult env_adler32( + void*, FizzyInstance* instance, const FizzyValue* args, FizzyExecutionContext*) { auto* memory = fizzy_get_instance_memory_data(instance); assert(memory != nullptr); diff --git a/test/utils/fizzy_engine.cpp b/test/utils/fizzy_engine.cpp index a103f371a..c6f282ca1 100644 --- a/test/utils/fizzy_engine.cpp +++ b/test/utils/fizzy_engine.cpp @@ -28,8 +28,8 @@ class FizzyEngine final : public WasmEngine namespace { -fizzy::ExecutionResult env_adler32( - std::any&, fizzy::Instance& instance, const fizzy::Value* args, int) noexcept +fizzy::ExecutionResult env_adler32(std::any&, fizzy::Instance& instance, const fizzy::Value* args, + fizzy::ExecutionContext&) noexcept { assert(instance.memory != nullptr); const auto ret = fizzy::test::adler32( diff --git a/tools/wasi/wasi.cpp b/tools/wasi/wasi.cpp index 37d9a5bfb..a230bc402 100644 --- a/tools/wasi/wasi.cpp +++ b/tools/wasi/wasi.cpp @@ -21,19 +21,20 @@ namespace // and we are a single-run tool. This may change in the future and should reevaluate. uvwasi_t state; -ExecutionResult return_enosys(std::any&, Instance&, const Value*, int) noexcept +ExecutionResult return_enosys(std::any&, Instance&, const Value*, ExecutionContext&) noexcept { return Value{uint32_t{UVWASI_ENOSYS}}; } -ExecutionResult proc_exit(std::any&, Instance&, const Value* args, int) noexcept +ExecutionResult proc_exit(std::any&, Instance&, const Value* args, ExecutionContext&) noexcept { uvwasi_proc_exit(&state, static_cast(args[0].as())); // Should not reach this. return Trap; } -ExecutionResult fd_write(std::any&, Instance& instance, const Value* args, int) noexcept +ExecutionResult fd_write( + std::any&, Instance& instance, const Value* args, ExecutionContext&) noexcept { const auto fd = args[0].as(); const auto iov_ptr = args[1].as(); @@ -54,7 +55,8 @@ ExecutionResult fd_write(std::any&, Instance& instance, const Value* args, int) return Value{uint32_t{ret}}; } -ExecutionResult fd_read(std::any&, Instance& instance, const Value* args, int) noexcept +ExecutionResult fd_read( + std::any&, Instance& instance, const Value* args, ExecutionContext&) noexcept { const auto fd = args[0].as(); const auto iov_ptr = args[1].as(); @@ -75,7 +77,8 @@ ExecutionResult fd_read(std::any&, Instance& instance, const Value* args, int) n return Value{uint32_t{ret}}; } -ExecutionResult fd_prestat_get(std::any&, Instance& instance, const Value* args, int) noexcept +ExecutionResult fd_prestat_get( + std::any&, Instance& instance, const Value* args, ExecutionContext&) noexcept { const auto fd = args[0].as(); const auto prestat_ptr = args[1].as(); @@ -88,7 +91,8 @@ ExecutionResult fd_prestat_get(std::any&, Instance& instance, const Value* args, return Value{uint32_t{ret}}; } -ExecutionResult environ_sizes_get(std::any&, Instance& instance, const Value* args, int) noexcept +ExecutionResult environ_sizes_get( + std::any&, Instance& instance, const Value* args, ExecutionContext&) noexcept { const auto environc = args[0].as(); const auto environ_buf_size = args[1].as(); From e2af5e9428659cabf1beec6df4aa73559d23743b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 23 Mar 2021 14:01:44 +0100 Subject: [PATCH 3/5] Require ExecutionContext in execute() Change int depth param to ExecutionContext& in execute() --- lib/fizzy/execute.cpp | 19 +++++++++---------- lib/fizzy/execute.hpp | 10 +++++----- lib/fizzy/instantiate.cpp | 2 +- test/unittests/execute_call_depth_test.cpp | 20 ++++++++++++-------- test/unittests/execute_call_test.cpp | 2 +- test/unittests/instantiate_test.cpp | 2 +- test/utils/execute_helpers.hpp | 4 +++- 7 files changed, 32 insertions(+), 27 deletions(-) diff --git a/lib/fizzy/execute.cpp b/lib/fizzy/execute.cpp index 8e59f2fd4..577207e92 100644 --- a/lib/fizzy/execute.cpp +++ b/lib/fizzy/execute.cpp @@ -531,13 +531,14 @@ void branch(const Code& code, OperandStack& stack, const uint8_t*& pc, uint32_t } inline bool invoke_function(const FuncType& func_type, uint32_t func_idx, Instance& instance, - OperandStack& stack, int depth) noexcept + OperandStack& stack, ExecutionContext& ctx) noexcept { const auto num_args = func_type.inputs.size(); assert(stack.size() >= num_args); const auto call_args = stack.rend() - num_args; - const auto ret = execute(instance, func_idx, call_args, depth + 1); + const auto ctx_guard = ctx.increment_call_depth(); + const auto ret = execute(instance, func_idx, call_args, ctx); // Bubble up traps if (ret.trapped) return false; @@ -557,13 +558,11 @@ inline bool invoke_function(const FuncType& func_type, uint32_t func_idx, Instan } // namespace -ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args, int depth) noexcept +ExecutionResult execute( + Instance& instance, FuncIdx func_idx, const Value* args, ExecutionContext& ctx) noexcept { - ExecutionContext ctx; - ctx.depth = depth; - - assert(depth >= 0); - if (depth >= CallStackLimit) + assert(ctx.depth >= 0); + if (ctx.depth >= CallStackLimit) return Trap; const auto& func_type = instance.module->get_function_type(func_idx); @@ -653,7 +652,7 @@ ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args, const auto called_func_idx = read(pc); const auto& called_func_type = instance.module->get_function_type(called_func_idx); - if (!invoke_function(called_func_type, called_func_idx, instance, stack, depth)) + if (!invoke_function(called_func_type, called_func_idx, instance, stack, ctx)) goto trap; break; } @@ -680,7 +679,7 @@ ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args, goto trap; if (!invoke_function( - actual_type, called_func.func_idx, *called_func.instance, stack, depth)) + actual_type, called_func.func_idx, *called_func.instance, stack, ctx)) goto trap; break; } diff --git a/lib/fizzy/execute.hpp b/lib/fizzy/execute.hpp index 0a0b8a154..959cf68e0 100644 --- a/lib/fizzy/execute.hpp +++ b/lib/fizzy/execute.hpp @@ -77,16 +77,16 @@ class ExecutionContext /// @param args The pointer to the arguments. The number of items and their types must match /// the expected number of input parameters of the function, otherwise undefined /// behaviour (including crash) happens. -/// @param depth The call depth (indexing starts at 0). +/// @param ctx Execution context. /// @return The result of the execution. ExecutionResult execute( - Instance& instance, FuncIdx func_idx, const Value* args, int depth) noexcept; + Instance& instance, FuncIdx func_idx, const Value* args, ExecutionContext& ctx) noexcept; -/// Execute a function from an instance starting at default depth of 0. +/// Execute a function from an instance with execution context starting with default depth of 0. /// Arguments and behavior is the same as in the other execute(). inline ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args) noexcept { - constexpr int depth = 0; - return execute(instance, func_idx, args, depth); + ExecutionContext ctx; + return execute(instance, func_idx, args, ctx); } } // namespace fizzy diff --git a/lib/fizzy/instantiate.cpp b/lib/fizzy/instantiate.cpp index 7a0b3f27a..f3aa69194 100644 --- a/lib/fizzy/instantiate.cpp +++ b/lib/fizzy/instantiate.cpp @@ -316,7 +316,7 @@ ExecutionResult ExecuteFunction::operator()( Instance& instance, const Value* args, ExecutionContext& ctx) noexcept { if (m_instance) - return execute(*m_instance, m_func_idx, args, ctx.depth); + return execute(*m_instance, m_func_idx, args, ctx); else return m_host_function(m_host_context, instance, args, ctx); } diff --git a/test/unittests/execute_call_depth_test.cpp b/test/unittests/execute_call_depth_test.cpp index e50250091..aa59a4937 100644 --- a/test/unittests/execute_call_depth_test.cpp +++ b/test/unittests/execute_call_depth_test.cpp @@ -98,7 +98,7 @@ TEST(execute_call_depth, execute_imported_host_function_calling_wasm_function) constexpr auto host_f = [](std::any&, Instance& instance, const Value*, ExecutionContext& ctx) noexcept { recorded_depth = ctx.depth; - return fizzy::execute(instance, 1, {}, ctx.depth); + return fizzy::execute(instance, 1, {}, ctx); }; const auto module = parse(wasm); @@ -213,7 +213,8 @@ TEST(execute_call_depth, call_host_function_calling_wasm_function_inclusive) constexpr auto host_f = [](std::any&, Instance& instance, const Value*, ExecutionContext& ctx) noexcept { recorded_depth = ctx.depth; - return fizzy::execute(instance, 2 /* $leaf */, {}, ctx.depth + 1); + const auto ctx_guard = ctx.increment_call_depth(); + return fizzy::execute(instance, 2 /* $leaf */, {}, ctx); }; const auto module = parse(wasm); @@ -258,7 +259,7 @@ TEST(execute_call_depth, call_host_function_calling_wasm_function_exclusive) constexpr auto host_f = [](std::any&, Instance& instance, const Value*, ExecutionContext& ctx) noexcept { recorded_depth = ctx.depth; - return fizzy::execute(instance, 2, {}, ctx.depth); + return fizzy::execute(instance, 2, {}, ctx); }; const auto module = parse(wasm); @@ -303,7 +304,8 @@ TEST(execute_call_depth, call_host_function_calling_another_wasm_module) ExecutionContext& ctx) noexcept { recorded_depth = ctx.depth; auto instance = *std::any_cast(&host_context); - return fizzy::execute(*instance, 0, {}, ctx.depth + 1); + const auto ctx_guard = ctx.increment_call_depth(); + return fizzy::execute(*instance, 0, {}, ctx); }; auto another_instance = instantiate(parse(another_wasm)); @@ -464,7 +466,8 @@ TEST(execute_call_depth, execute_host_function_within_wasm_recursion_limit) constexpr auto host_f = [](std::any&, Instance& instance, const Value*, ExecutionContext& ctx) noexcept { max_recorded_wasm_recursion_depth = std::max(max_recorded_wasm_recursion_depth, ctx.depth); - return fizzy::execute(instance, 0, {}, ctx.depth + 1); + const auto ctx_guard = ctx.increment_call_depth(); + return fizzy::execute(instance, 0, {}, ctx); }; const auto module = parse(wasm); @@ -499,7 +502,7 @@ TEST(execute_call_depth, execute_host_function_with_custom_recursion_limit) std::max(max_recorded_host_recursion_depth, host_recursion_depth); const auto result = (host_recursion_depth < host_recursion_limit) ? - fizzy::execute(instance, 0, {}, ctx.depth) : + fizzy::execute(instance, 0, {}, ctx) : ExecutionResult{Value{1}}; --host_recursion_depth; return result; @@ -534,7 +537,8 @@ TEST(execute_call, call_host_function_calling_wasm_interleaved_infinite_recursio ExecutionContext& ctx) noexcept { EXPECT_LT(ctx.depth, DepthLimit); ++counter; - return fizzy::execute(instance, 1, {}, ctx.depth + 1); + const auto ctx_guard = ctx.increment_call_depth(); + return fizzy::execute(instance, 1, {}, ctx); }; const auto module = parse(wasm); @@ -568,7 +572,7 @@ TEST(execute_call, call_host_function_calling_wasm_interleaved_infinite_recursio ExecutionContext& ctx) noexcept { EXPECT_LT(ctx.depth, DepthLimit); ++counter; - return fizzy::execute(instance, 1, {}, ctx.depth); + return fizzy::execute(instance, 1, {}, ctx); }; const auto module = parse(wasm); diff --git a/test/unittests/execute_call_test.cpp b/test/unittests/execute_call_test.cpp index 05f9553e5..90710abad 100644 --- a/test/unittests/execute_call_test.cpp +++ b/test/unittests/execute_call_test.cpp @@ -570,7 +570,7 @@ TEST(execute_call, imported_function_from_another_module_via_host_function) constexpr auto sub = [](std::any& host_context, Instance&, const Value* args, ExecutionContext& ctx) noexcept { auto [inst1, idx] = *std::any_cast>(&host_context); - return fizzy::execute(*inst1, idx, args, ctx.depth); + return fizzy::execute(*inst1, idx, args, ctx); }; auto host_context = std::make_any>(instance1.get(), *func_idx); diff --git a/test/unittests/instantiate_test.cpp b/test/unittests/instantiate_test.cpp index b9a9df514..1082d7bb6 100644 --- a/test/unittests/instantiate_test.cpp +++ b/test/unittests/instantiate_test.cpp @@ -28,7 +28,7 @@ ExecutionResult host_fn_2(std::any&, Instance&, const Value*, ExecutionContext&) uint32_t call_table_func(Instance& instance, size_t idx) { const auto& elem = (*instance.table)[idx]; - const auto res = execute(*elem.instance, elem.func_idx, {}, 0); + const auto res = execute(*elem.instance, elem.func_idx, {}); EXPECT_TRUE(res.has_value); return res.value.i32; } diff --git a/test/utils/execute_helpers.hpp b/test/utils/execute_helpers.hpp index 51a43e60a..93eb52d28 100644 --- a/test/utils/execute_helpers.hpp +++ b/test/utils/execute_helpers.hpp @@ -31,7 +31,9 @@ inline TypedExecutionResult execute(Instance& instance, FuncIdx func_idx, std::transform(std::cbegin(typed_args), std::cend(typed_args), std::begin(args), [](const auto& typed_arg) { return typed_arg.value; }); - const auto result = fizzy::execute(instance, func_idx, args.data(), depth); + ExecutionContext ctx; + ctx.depth = depth; + const auto result = fizzy::execute(instance, func_idx, args.data(), ctx); assert(func_type.outputs.size() <= 1); const auto result_type = func_type.outputs.empty() ? ValType{} : func_type.outputs[0]; return {result, result_type}; From 60ee89ec607f17a9e8b83209288420867f35ae96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 23 Mar 2021 14:05:13 +0100 Subject: [PATCH 4/5] test: Drop depth param in execute(Module, ...) --- test/utils/execute_helpers.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/utils/execute_helpers.hpp b/test/utils/execute_helpers.hpp index 93eb52d28..11e3b4a62 100644 --- a/test/utils/execute_helpers.hpp +++ b/test/utils/execute_helpers.hpp @@ -40,9 +40,9 @@ inline TypedExecutionResult execute(Instance& instance, FuncIdx func_idx, } inline auto execute(const std::unique_ptr& module, FuncIdx func_idx, - std::initializer_list typed_args, int depth = 0) + std::initializer_list typed_args) { auto instance = instantiate(*module); - return test::execute(*instance, func_idx, typed_args, depth); + return test::execute(*instance, func_idx, typed_args); } } // namespace fizzy::test From 6e8a0a5fecb45bb50870ec20719ffdc271040305 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 26 Mar 2021 09:10:01 +0100 Subject: [PATCH 5/5] capi: Allocate new execution context if needed --- include/fizzy/fizzy.h | 3 ++- lib/fizzy/capi.cpp | 11 +++++++++-- test/unittests/capi_test.cpp | 5 ++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/include/fizzy/fizzy.h b/include/fizzy/fizzy.h index 71cdb87f1..ea1186b5f 100644 --- a/include/fizzy/fizzy.h +++ b/include/fizzy/fizzy.h @@ -55,7 +55,8 @@ typedef struct FizzyExecutionContext FizzyExecutionContext; /// @param host_ctx Opaque pointer to host context. /// @param instance Pointer to module instance. /// @param args Pointer to the argument array. Can be NULL iff function has no inputs. -/// @param ctx Opaque pointer to execution context. +/// @param ctx Opaque pointer to execution context. If NULL new execution context +/// will be allocated. /// @return Result of execution. /// /// @note diff --git a/lib/fizzy/capi.cpp b/lib/fizzy/capi.cpp index 245b52593..4bcba8cea 100644 --- a/lib/fizzy/capi.cpp +++ b/lib/fizzy/capi.cpp @@ -137,9 +137,16 @@ inline FizzyExternalFunction wrap(fizzy::ExternalFunction external_func) { static constexpr FizzyExternalFn c_function = [](void* host_ctx, FizzyInstance* instance, const FizzyValue* args, - FizzyExecutionContext* ctx) -> FizzyExecutionResult { + FizzyExecutionContext* c_ctx) -> FizzyExecutionResult { + // If execution context not provided, allocate new one. + // It must be allocated on heap otherwise the stack will explode in recursive calls. + std::unique_ptr new_ctx; + if (c_ctx == nullptr) + new_ctx = std::make_unique(); + auto& ctx = new_ctx ? *new_ctx : unwrap(c_ctx); + auto* func = static_cast(host_ctx); - return wrap((func->function)(*unwrap(instance), unwrap(args), unwrap(ctx))); + return wrap((func->function)(*unwrap(instance), unwrap(args), ctx)); }; auto host_ctx = std::make_unique(std::move(external_func)); diff --git a/test/unittests/capi_test.cpp b/test/unittests/capi_test.cpp index 982a8d182..bc3c8e348 100644 --- a/test/unittests/capi_test.cpp +++ b/test/unittests/capi_test.cpp @@ -330,9 +330,8 @@ TEST(capi, find_exported_function) EXPECT_EQ(function.type.output, FizzyValueTypeI32); EXPECT_NE(function.context, nullptr); ASSERT_NE(function.function, nullptr); - fizzy::ExecutionContext ctx; - auto* const c_ctx = reinterpret_cast(&ctx); - EXPECT_THAT(function.function(function.context, instance, nullptr, c_ctx), CResult(42_u32)); + + EXPECT_THAT(function.function(function.context, instance, nullptr, nullptr), CResult(42_u32)); fizzy_free_exported_function(&function);