Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C API instantiate with resolving imported functions #608

Merged
merged 1 commit into from
Oct 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions include/fizzy/fizzy.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,17 @@ typedef struct FizzyExternalFunction
void* context;
} FizzyExternalFunction;

/// Imported function.
typedef struct FizzyImportedFunction
{
/// Module name. NULL-terminated string. Cannot be NULL.
const char* module;
/// Function name. NULL-terminated string. Cannot be NULL.
const char* name;
/// External function, defining its type, pointer to function and context for calling it.
FizzyExternalFunction external_function;
} FizzyImportedFunction;

/// Validate binary module.
bool fizzy_validate(const uint8_t* wasm_binary, size_t wasm_binary_size);

Expand Down Expand Up @@ -130,6 +141,22 @@ bool fizzy_find_exported_function(
FizzyInstance* fizzy_instantiate(const FizzyModule* module,
const FizzyExternalFunction* imported_functions, size_t imported_functions_size);

/// Instantiate a module resolving imported functions.
/// Takes ownership of module, i.e. @p module is invalidated after this call.
///
/// @param module Pointer to module.
/// @param imported_functions Pointer to the imported function array. Can be NULL iff
/// imported_functions_size equals 0.
/// @param imported_functions_size Size of the imported function array. Can be zero.
/// @returns non-NULL pointer to instance in case of success, NULL otherwise.
///
/// @note
/// Functions in @a imported_functions are allowed to be in any order and allowed to include some
/// functions not required by the module.
/// Functions are matched to module's imports based on their module and name strings.
FizzyInstance* fizzy_resolve_instantiate(const FizzyModule* module,
const FizzyImportedFunction* imported_functions, size_t imported_functions_size);

/// Free resources associated with the instance.
/// If passed pointer is NULL, has no effect.
void fizzy_free_instance(FizzyInstance* instance);
Expand Down
45 changes: 45 additions & 0 deletions lib/fizzy/capi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,28 @@ inline fizzy::ExternalFunction unwrap(const FizzyExternalFunction& external_func
return fizzy::ExternalFunction{
unwrap(external_func.function, external_func.context), unwrap(external_func.type)};
}

inline fizzy::ImportedFunction unwrap(const FizzyImportedFunction& c_imported_func)
{
fizzy::ImportedFunction imported_func;
imported_func.module =
c_imported_func.module ? std::string{c_imported_func.module} : std::string{};
imported_func.name = c_imported_func.name ? std::string{c_imported_func.name} : std::string{};

const auto& c_type = c_imported_func.external_function.type;
fizzy::ValType (*unwrap_valtype_fn)(FizzyValueType value) = &unwrap;
std::transform(c_type.inputs, c_type.inputs + c_type.inputs_size, imported_func.inputs.begin(),
unwrap_valtype_fn);
imported_func.output = c_type.output == FizzyValueTypeVoid ?
std::nullopt :
std::make_optional(unwrap(c_type.output));

imported_func.function = unwrap(
c_imported_func.external_function.function, c_imported_func.external_function.context);

return imported_func;
}

} // namespace

extern "C" {
Expand Down Expand Up @@ -186,6 +208,29 @@ FizzyInstance* fizzy_instantiate(const FizzyModule* module,
}
}

FizzyInstance* fizzy_resolve_instantiate(const FizzyModule* c_module,
const FizzyImportedFunction* c_imported_functions, size_t imported_functions_size)
{
try
{
std::vector<fizzy::ImportedFunction> imported_functions(imported_functions_size);
fizzy::ImportedFunction (*unwrap_imported_func_fn)(const FizzyImportedFunction&) = &unwrap;
std::transform(c_imported_functions, c_imported_functions + imported_functions_size,
imported_functions.begin(), unwrap_imported_func_fn);

std::unique_ptr<const fizzy::Module> module{unwrap(c_module)};
auto resolved_imports = fizzy::resolve_imported_functions(*module, imported_functions);

auto instance = fizzy::instantiate(std::move(module), std::move(resolved_imports));

return wrap(instance.release());
}
catch (...)
{
return nullptr;
}
}

void fizzy_free_instance(FizzyInstance* instance)
{
delete unwrap(instance);
Expand Down
93 changes: 93 additions & 0 deletions test/unittests/capi_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,99 @@ TEST(capi, instantiate_imported_function)
fizzy_free_instance(instance);
}

TEST(capi, resolve_instantiate_no_imports)
{
/* wat2wasm
(module)
*/
const auto wasm = from_hex("0061736d01000000");
auto module = fizzy_parse(wasm.data(), wasm.size());
ASSERT_NE(module, nullptr);

auto instance = fizzy_resolve_instantiate(module, nullptr, 0);
EXPECT_NE(instance, nullptr);

fizzy_free_instance(instance);

module = fizzy_parse(wasm.data(), wasm.size());
ASSERT_NE(module, nullptr);

FizzyImportedFunction host_funcs[] = {{"mod", "foo",
{{FizzyValueTypeVoid, nullptr, 0},
[](void*, FizzyInstance*, const FizzyValue*, int) { return FizzyExecutionResult{}; },
nullptr}}};

instance = fizzy_resolve_instantiate(module, host_funcs, 1);
EXPECT_NE(instance, nullptr);

fizzy_free_instance(instance);
}

TEST(capi, resolve_instantiate)
{
/* wat2wasm
(func (import "mod1" "foo1") (result i32))
(func (import "mod1" "foo2") (result i64))
(func (import "mod2" "foo1") (result f32))
(func (import "mod2" "foo2") (result f64))
*/
const auto wasm = from_hex(
"0061736d010000000111046000017f6000017e6000017d6000017c023104046d6f643104666f6f310000046d6f"
"643104666f6f320001046d6f643204666f6f310002046d6f643204666f6f320003");
auto module = fizzy_parse(wasm.data(), wasm.size());
ASSERT_NE(module, nullptr);

EXPECT_EQ(fizzy_instantiate(module, nullptr, 0), nullptr);

module = fizzy_parse(wasm.data(), wasm.size());
ASSERT_NE(module, nullptr);

FizzyExternalFn host_fn = [](void* context, FizzyInstance*, const FizzyValue*, int) {
return FizzyExecutionResult{true, false, *static_cast<FizzyValue*>(context)};
};

FizzyValue result_int{42};
FizzyExternalFunction mod1foo1 = {{FizzyValueTypeI32, nullptr, 0}, host_fn, &result_int};
FizzyExternalFunction mod1foo2 = {{FizzyValueTypeI64, nullptr, 0}, host_fn, &result_int};
FizzyValue result_f32;
result_f32.f32 = 42;
FizzyExternalFunction mod2foo1 = {{FizzyValueTypeF32, nullptr, 0}, host_fn, &result_f32};
FizzyValue result_f64;
result_f64.f64 = 42;
FizzyExternalFunction mod2foo2 = {{FizzyValueTypeF64, nullptr, 0}, host_fn, &result_f64};

FizzyImportedFunction host_funcs[] = {{"mod1", "foo1", mod1foo1}, {"mod1", "foo2", mod1foo2},
{"mod2", "foo1", mod2foo1}, {"mod2", "foo2", mod2foo2}};

auto instance = fizzy_resolve_instantiate(module, host_funcs, 4);
EXPECT_NE(instance, nullptr);
fizzy_free_instance(instance);

// reordered functions
module = fizzy_parse(wasm.data(), wasm.size());
ASSERT_NE(module, nullptr);
FizzyImportedFunction host_funcs_reordered[] = {{"mod1", "foo2", mod1foo2},
{"mod2", "foo1", mod2foo1}, {"mod2", "foo2", mod2foo2}, {"mod1", "foo1", mod1foo1}};
instance = fizzy_resolve_instantiate(module, host_funcs_reordered, 4);
EXPECT_NE(instance, nullptr);
fizzy_free_instance(instance);

// extra functions
module = fizzy_parse(wasm.data(), wasm.size());
ASSERT_NE(module, nullptr);
FizzyImportedFunction host_funcs_extra[] = {{"mod1", "foo1", mod1foo1},
{"mod1", "foo2", mod1foo2}, {"mod2", "foo1", mod2foo1}, {"mod2", "foo2", mod2foo2},
{"mod3", "foo1", mod1foo1}};
instance = fizzy_resolve_instantiate(module, host_funcs_extra, 4);
EXPECT_NE(instance, nullptr);
fizzy_free_instance(instance);

// not enough functions
module = fizzy_parse(wasm.data(), wasm.size());
ASSERT_NE(module, nullptr);
EXPECT_EQ(fizzy_resolve_instantiate(module, host_funcs, 3), nullptr);
}

TEST(capi, free_instance_null)
{
fizzy_free_instance(nullptr);
Expand Down