Skip to content

Commit 5896ba7

Browse files
authored
Merge pull request #576 from wasmx/capi-execute-dynamic-module
C API execute with dynamically allocated module
2 parents c00d177 + 59419bd commit 5896ba7

File tree

4 files changed

+464
-0
lines changed

4 files changed

+464
-0
lines changed

circle.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ jobs:
265265
command: |
266266
export PATH=$PATH:~/bin
267267
./wat2wasm4cpp.py test/unittests/api_test.cpp
268+
./wat2wasm4cpp.py test/unittests/capi_test.cpp
268269
./wat2wasm4cpp.py test/unittests/end_to_end_test.cpp
269270
./wat2wasm4cpp.py test/unittests/execute_call_test.cpp
270271
./wat2wasm4cpp.py test/unittests/execute_control_test.cpp

include/fizzy/fizzy.h

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,104 @@
1111
extern "C" {
1212
#endif
1313

14+
/// The opaque data type representing a module.
15+
typedef struct FizzyModule FizzyModule;
16+
17+
/// The opaque data type representing an instance (instantiated module).
18+
typedef struct FizzyInstance FizzyInstance;
19+
20+
/// The data type representing numeric values.
21+
///
22+
/// i64 member is used to represent values of both i32 and i64 type.
23+
union FizzyValue
24+
{
25+
uint64_t i64;
26+
float f32;
27+
double f64;
28+
};
29+
30+
/// Result of execution of a function.
31+
typedef struct FizzyExecutionResult
32+
{
33+
/// Whether execution ended with a trap.
34+
bool trapped;
35+
/// Whether function returned a value. Valid only if trapped equals false.
36+
bool has_value;
37+
/// Value returned from a function.
38+
/// Valid only if trapped equals false and has_value equals true.
39+
union FizzyValue value;
40+
} FizzyExecutionResult;
41+
42+
43+
/// Pointer to external function.
44+
///
45+
/// @param context Opaque pointer to execution context.
46+
/// @param instance Pointer to module instance.
47+
/// @param args Pointer to the argument array. Can be NULL iff args_size equals 0.
48+
/// @param args_size Size of the argument array.
49+
/// @param depth Call stack depth.
50+
typedef FizzyExecutionResult (*FizzyExternalFn)(void* context, FizzyInstance* instance,
51+
const union FizzyValue* args, size_t args_size, int depth);
52+
53+
/// External function.
54+
typedef struct FizzyExternalFunction
55+
{
56+
// TODO function type
57+
58+
/// Pointer to function.
59+
FizzyExternalFn function;
60+
/// Opaque pointer to execution context, that will be passed to function.
61+
void* context;
62+
} FizzyExternalFunction;
63+
64+
/// Validate binary module.
1465
bool fizzy_validate(const uint8_t* wasm_binary, size_t wasm_binary_size);
1566

67+
/// Parse binary module.
68+
///
69+
/// @returns non-NULL pointer to module in case of success, NULL otherwise.
70+
const FizzyModule* fizzy_parse(const uint8_t* wasm_binary, size_t wasm_binary_size);
71+
72+
/// Free resources associated with the module.
73+
///
74+
/// Should be called unless @p module was passed to fizzy_instantiate.
75+
/// If passed pointer is NULL, has no effect.
76+
void fizzy_free_module(const FizzyModule* module);
77+
78+
/// Instantiate a module.
79+
/// Takes ownership of module, i.e. @p module is invalidated after this call.
80+
///
81+
/// @param module Pointer to module.
82+
/// @param imported_functions Pointer to the imported function array. Can be NULL iff
83+
/// imported_functions_size equals 0.
84+
/// @param imported_functions_size Size of the imported function array. Can be zero.
85+
/// @returns non-NULL pointer to instance in case of success, NULL otherwise.
86+
///
87+
/// @note
88+
/// Function expects @a imported_functions to be in the order of imports defined in the module.
89+
/// No validation is done on the number of functions passed in, nor on their order.
90+
/// When number of passed functions or their order is different from the one defined by the
91+
/// module, behaviour is undefined.
92+
FizzyInstance* fizzy_instantiate(const FizzyModule* module,
93+
const FizzyExternalFunction* imported_functions, size_t imported_functions_size);
94+
95+
/// Free resources associated with the instance.
96+
/// If passed pointer is NULL, has no effect.
97+
void fizzy_free_instance(FizzyInstance* instance);
98+
99+
/// Execute module function.
100+
///
101+
/// @param instance Pointer to module instance.
102+
/// @param args Pointer to the argument array. Can be NULL if function has 0 inputs.
103+
/// @param depth Call stack depth.
104+
///
105+
/// @note
106+
/// No validation is done on the number of arguments passed in @p args, nor on their types.
107+
/// When number of passed arguments or their types are different from the ones defined by the
108+
/// function type, behaviour is undefined.
109+
FizzyExecutionResult fizzy_execute(
110+
FizzyInstance* instance, uint32_t func_idx, const union FizzyValue* args, int depth);
111+
16112
#ifdef __cplusplus
17113
}
18114
#endif

lib/fizzy/capi.cpp

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,79 @@
22
// Copyright 2020 The Fizzy Authors.
33
// SPDX-License-Identifier: Apache-2.0
44

5+
#include "cxx20/bit.hpp"
6+
#include "execute.hpp"
7+
#include "instantiate.hpp"
58
#include "parser.hpp"
69
#include <fizzy/fizzy.h>
10+
#include <memory>
11+
12+
namespace
13+
{
14+
inline const FizzyModule* wrap(const fizzy::Module* module) noexcept
15+
{
16+
return reinterpret_cast<const FizzyModule*>(module);
17+
}
18+
19+
inline const fizzy::Module* unwrap(const FizzyModule* module) noexcept
20+
{
21+
return reinterpret_cast<const fizzy::Module*>(module);
22+
}
23+
24+
inline FizzyValue wrap(fizzy::Value value) noexcept
25+
{
26+
return fizzy::bit_cast<FizzyValue>(value);
27+
}
28+
29+
inline fizzy::Value unwrap(FizzyValue value) noexcept
30+
{
31+
return fizzy::bit_cast<fizzy::Value>(value);
32+
}
33+
34+
inline const FizzyValue* wrap(const fizzy::Value* values) noexcept
35+
{
36+
return reinterpret_cast<const FizzyValue*>(values);
37+
}
38+
39+
inline const fizzy::Value* unwrap(const FizzyValue* values) noexcept
40+
{
41+
return reinterpret_cast<const fizzy::Value*>(values);
42+
}
43+
44+
inline FizzyInstance* wrap(fizzy::Instance* instance) noexcept
45+
{
46+
return reinterpret_cast<FizzyInstance*>(instance);
47+
}
48+
49+
inline fizzy::Instance* unwrap(FizzyInstance* instance) noexcept
50+
{
51+
return reinterpret_cast<fizzy::Instance*>(instance);
52+
}
53+
54+
inline FizzyExecutionResult wrap(const fizzy::ExecutionResult& result) noexcept
55+
{
56+
return {result.trapped, result.has_value, wrap(result.value)};
57+
}
58+
59+
inline fizzy::ExecutionResult unwrap(const FizzyExecutionResult& result) noexcept
60+
{
61+
if (result.trapped)
62+
return fizzy::Trap;
63+
else if (!result.has_value)
64+
return fizzy::Void;
65+
else
66+
return unwrap(result.value);
67+
}
68+
69+
inline auto unwrap(FizzyExternalFn func, void* context) noexcept
70+
{
71+
return [func, context](fizzy::Instance& instance, fizzy::span<const fizzy::Value> args,
72+
int depth) noexcept -> fizzy::ExecutionResult {
73+
const auto result = func(context, wrap(&instance), wrap(args.data()), args.size(), depth);
74+
return unwrap(result);
75+
};
76+
}
77+
} // namespace
778

879
extern "C" {
980
bool fizzy_validate(const uint8_t* wasm_binary, size_t wasm_binary_size)
@@ -18,4 +89,65 @@ bool fizzy_validate(const uint8_t* wasm_binary, size_t wasm_binary_size)
1889
return false;
1990
}
2091
}
92+
93+
const FizzyModule* fizzy_parse(const uint8_t* wasm_binary, size_t wasm_binary_size)
94+
{
95+
try
96+
{
97+
auto module = fizzy::parse({wasm_binary, wasm_binary_size});
98+
return wrap(module.release());
99+
}
100+
catch (...)
101+
{
102+
return nullptr;
103+
}
104+
}
105+
106+
void fizzy_free_module(const FizzyModule* module)
107+
{
108+
delete unwrap(module);
109+
}
110+
111+
FizzyInstance* fizzy_instantiate(const FizzyModule* module,
112+
const FizzyExternalFunction* imported_functions, size_t imported_functions_size)
113+
{
114+
try
115+
{
116+
// Convert fizzy_external_function to fizzy::ExternalFunction
117+
std::vector<fizzy::ExternalFunction> functions(imported_functions_size);
118+
for (size_t imported_func_idx = 0; imported_func_idx < imported_functions_size;
119+
++imported_func_idx)
120+
{
121+
const auto& cfunc = imported_functions[imported_func_idx];
122+
123+
auto func = unwrap(cfunc.function, cfunc.context);
124+
// TODO get type from input array
125+
auto func_type = unwrap(module)->imported_function_types[imported_func_idx];
126+
127+
functions[imported_func_idx] =
128+
fizzy::ExternalFunction{std::move(func), std::move(func_type)};
129+
}
130+
131+
auto instance = fizzy::instantiate(
132+
std::unique_ptr<const fizzy::Module>(unwrap(module)), std::move(functions));
133+
134+
return wrap(instance.release());
135+
}
136+
catch (...)
137+
{
138+
return nullptr;
139+
}
140+
}
141+
142+
void fizzy_free_instance(FizzyInstance* instance)
143+
{
144+
delete unwrap(instance);
145+
}
146+
147+
FizzyExecutionResult fizzy_execute(
148+
FizzyInstance* instance, uint32_t func_idx, const FizzyValue* args, int depth)
149+
{
150+
const auto result = fizzy::execute(*unwrap(instance), func_idx, unwrap(args), depth);
151+
return wrap(result);
152+
}
21153
}

0 commit comments

Comments
 (0)