Skip to content

Commit 5c6067d

Browse files
committed
capi: Instantiate with resolving host functions
1 parent e5d589a commit 5c6067d

3 files changed

Lines changed: 165 additions & 0 deletions

File tree

include/fizzy/fizzy.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,17 @@ typedef struct FizzyExternalFunction
8080
void* context;
8181
} FizzyExternalFunction;
8282

83+
/// Imported function.
84+
typedef struct FizzyImportedFunction
85+
{
86+
/// Module name. NULL-terminated string. Cannot be NULL.
87+
const char* module;
88+
/// Function name. NULL-terminated string. Cannot be NULL.
89+
const char* name;
90+
/// External function, defining its type, pointer to function and context for calling it.
91+
FizzyExternalFunction external_function;
92+
} FizzyImportedFunction;
93+
8394
/// Validate binary module.
8495
bool fizzy_validate(const uint8_t* wasm_binary, size_t wasm_binary_size);
8596

@@ -130,6 +141,22 @@ bool fizzy_find_exported_function(
130141
FizzyInstance* fizzy_instantiate(const FizzyModule* module,
131142
const FizzyExternalFunction* imported_functions, size_t imported_functions_size);
132143

144+
/// Instantiate a module resolving imported functions.
145+
/// Takes ownership of module, i.e. @p module is invalidated after this call.
146+
///
147+
/// @param module Pointer to module.
148+
/// @param imported_functions Pointer to the imported function array. Can be NULL iff
149+
/// imported_functions_size equals 0.
150+
/// @param imported_functions_size Size of the imported function array. Can be zero.
151+
/// @returns non-NULL pointer to instance in case of success, NULL otherwise.
152+
///
153+
/// @note
154+
/// Functions in @a imported_functions are allowed to be in any order and allowed to include some
155+
/// functions not required by instantiated module.
156+
/// Functions are matched to module's imports based on their module and name strings.
157+
FizzyInstance* fizzy_resolve_instantiate(const FizzyModule* module,
158+
const FizzyImportedFunction* imported_functions, size_t imported_functions_size);
159+
133160
/// Free resources associated with the instance.
134161
/// If passed pointer is NULL, has no effect.
135162
void fizzy_free_instance(FizzyInstance* instance);

lib/fizzy/capi.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,28 @@ inline fizzy::ExternalFunction unwrap(const FizzyExternalFunction& external_func
115115
return fizzy::ExternalFunction{
116116
unwrap(external_func.function, external_func.context), unwrap(external_func.type)};
117117
}
118+
119+
inline fizzy::ImportedFunction unwrap(const FizzyImportedFunction& c_imported_func)
120+
{
121+
fizzy::ImportedFunction imported_func;
122+
imported_func.module =
123+
c_imported_func.module ? std::string{c_imported_func.module} : std::string{};
124+
imported_func.name = c_imported_func.name ? std::string{c_imported_func.name} : std::string{};
125+
126+
const auto& c_type = c_imported_func.external_function.type;
127+
fizzy::ValType (*unwrap_valtype_fn)(FizzyValueType value) = &unwrap;
128+
std::transform(c_type.inputs, c_type.inputs + c_type.inputs_size, imported_func.inputs.begin(),
129+
unwrap_valtype_fn);
130+
imported_func.output = c_type.output == FizzyValueTypeVoid ?
131+
std::nullopt :
132+
std::make_optional(unwrap(c_type.output));
133+
134+
imported_func.function = unwrap(
135+
c_imported_func.external_function.function, c_imported_func.external_function.context);
136+
137+
return imported_func;
138+
}
139+
118140
} // namespace
119141

120142
extern "C" {
@@ -186,6 +208,29 @@ FizzyInstance* fizzy_instantiate(const FizzyModule* module,
186208
}
187209
}
188210

211+
FizzyInstance* fizzy_resolve_instantiate(const FizzyModule* c_module,
212+
const FizzyImportedFunction* c_imported_functions, size_t imported_functions_size)
213+
{
214+
try
215+
{
216+
std::vector<fizzy::ImportedFunction> imported_functions(imported_functions_size);
217+
fizzy::ImportedFunction (*unwrap_imported_func_fn)(const FizzyImportedFunction&) = &unwrap;
218+
std::transform(c_imported_functions, c_imported_functions + imported_functions_size,
219+
imported_functions.begin(), unwrap_imported_func_fn);
220+
221+
std::unique_ptr<const fizzy::Module> module{unwrap(c_module)};
222+
auto resolved_imports = fizzy::resolve_imported_functions(*module, imported_functions);
223+
224+
auto instance = fizzy::instantiate(std::move(module), std::move(resolved_imports));
225+
226+
return wrap(instance.release());
227+
}
228+
catch (...)
229+
{
230+
return nullptr;
231+
}
232+
}
233+
189234
void fizzy_free_instance(FizzyInstance* instance)
190235
{
191236
delete unwrap(instance);

test/unittests/capi_test.cpp

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,99 @@ TEST(capi, instantiate_imported_function)
136136
fizzy_free_instance(instance);
137137
}
138138

139+
TEST(capi, resolve_instantiate_no_imports)
140+
{
141+
/* wat2wasm
142+
(module)
143+
*/
144+
const auto wasm = from_hex("0061736d01000000");
145+
auto module = fizzy_parse(wasm.data(), wasm.size());
146+
ASSERT_NE(module, nullptr);
147+
148+
auto instance = fizzy_resolve_instantiate(module, nullptr, 0);
149+
EXPECT_NE(instance, nullptr);
150+
151+
fizzy_free_instance(instance);
152+
153+
module = fizzy_parse(wasm.data(), wasm.size());
154+
ASSERT_NE(module, nullptr);
155+
156+
FizzyImportedFunction host_funcs[] = {{"mod", "foo",
157+
{{FizzyValueTypeVoid, nullptr, 0},
158+
[](void*, FizzyInstance*, const FizzyValue*, int) { return FizzyExecutionResult{}; },
159+
nullptr}}};
160+
161+
instance = fizzy_resolve_instantiate(module, host_funcs, 1);
162+
EXPECT_NE(instance, nullptr);
163+
164+
fizzy_free_instance(instance);
165+
}
166+
167+
TEST(capi, resolve_instantiate)
168+
{
169+
/* wat2wasm
170+
(func (import "mod1" "foo1") (result i32))
171+
(func (import "mod1" "foo2") (result i64))
172+
(func (import "mod2" "foo1") (result f32))
173+
(func (import "mod2" "foo2") (result f64))
174+
*/
175+
const auto wasm = from_hex(
176+
"0061736d010000000111046000017f6000017e6000017d6000017c023104046d6f643104666f6f310000046d6f"
177+
"643104666f6f320001046d6f643204666f6f310002046d6f643204666f6f320003");
178+
auto module = fizzy_parse(wasm.data(), wasm.size());
179+
ASSERT_NE(module, nullptr);
180+
181+
EXPECT_EQ(fizzy_instantiate(module, nullptr, 0), nullptr);
182+
183+
module = fizzy_parse(wasm.data(), wasm.size());
184+
ASSERT_NE(module, nullptr);
185+
186+
FizzyExternalFn host_fn = [](void* context, FizzyInstance*, const FizzyValue*, int) {
187+
return FizzyExecutionResult{true, false, *static_cast<FizzyValue*>(context)};
188+
};
189+
190+
FizzyValue result_int{42};
191+
FizzyExternalFunction mod1foo1 = {{FizzyValueTypeI32, nullptr, 0}, host_fn, &result_int};
192+
FizzyExternalFunction mod1foo2 = {{FizzyValueTypeI64, nullptr, 0}, host_fn, &result_int};
193+
FizzyValue result_f32;
194+
result_f32.f32 = 42;
195+
FizzyExternalFunction mod2foo1 = {{FizzyValueTypeF32, nullptr, 0}, host_fn, &result_f32};
196+
FizzyValue result_f64;
197+
result_f64.f64 = 42;
198+
FizzyExternalFunction mod2foo2 = {{FizzyValueTypeF64, nullptr, 0}, host_fn, &result_f64};
199+
200+
FizzyImportedFunction host_funcs[] = {{"mod1", "foo1", mod1foo1}, {"mod1", "foo2", mod1foo2},
201+
{"mod2", "foo1", mod2foo1}, {"mod2", "foo2", mod2foo2}};
202+
203+
auto instance = fizzy_resolve_instantiate(module, host_funcs, 4);
204+
EXPECT_NE(instance, nullptr);
205+
fizzy_free_instance(instance);
206+
207+
// reordered functions
208+
module = fizzy_parse(wasm.data(), wasm.size());
209+
ASSERT_NE(module, nullptr);
210+
FizzyImportedFunction host_funcs_reordered[] = {{"mod1", "foo2", mod1foo2},
211+
{"mod2", "foo1", mod2foo1}, {"mod2", "foo2", mod2foo2}, {"mod1", "foo1", mod1foo1}};
212+
instance = fizzy_resolve_instantiate(module, host_funcs_reordered, 4);
213+
EXPECT_NE(instance, nullptr);
214+
fizzy_free_instance(instance);
215+
216+
// extra functions
217+
module = fizzy_parse(wasm.data(), wasm.size());
218+
ASSERT_NE(module, nullptr);
219+
FizzyImportedFunction host_funcs_extra[] = {{"mod1", "foo1", mod1foo1},
220+
{"mod1", "foo2", mod1foo2}, {"mod2", "foo1", mod2foo1}, {"mod2", "foo2", mod2foo2},
221+
{"mod3", "foo1", mod1foo1}};
222+
instance = fizzy_resolve_instantiate(module, host_funcs_extra, 4);
223+
EXPECT_NE(instance, nullptr);
224+
fizzy_free_instance(instance);
225+
226+
// not enough functions
227+
module = fizzy_parse(wasm.data(), wasm.size());
228+
ASSERT_NE(module, nullptr);
229+
EXPECT_EQ(fizzy_resolve_instantiate(module, host_funcs, 3), nullptr);
230+
}
231+
139232
TEST(capi, free_instance_null)
140233
{
141234
fizzy_free_instance(nullptr);

0 commit comments

Comments
 (0)