Skip to content

Commit

Permalink
Merge pull request #576 from wasmx/capi-execute-dynamic-module
Browse files Browse the repository at this point in the history
C API execute with dynamically allocated module
  • Loading branch information
axic authored Oct 12, 2020
2 parents c00d177 + 59419bd commit 5896ba7
Show file tree
Hide file tree
Showing 4 changed files with 464 additions and 0 deletions.
1 change: 1 addition & 0 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ jobs:
command: |
export PATH=$PATH:~/bin
./wat2wasm4cpp.py test/unittests/api_test.cpp
./wat2wasm4cpp.py test/unittests/capi_test.cpp
./wat2wasm4cpp.py test/unittests/end_to_end_test.cpp
./wat2wasm4cpp.py test/unittests/execute_call_test.cpp
./wat2wasm4cpp.py test/unittests/execute_control_test.cpp
Expand Down
96 changes: 96 additions & 0 deletions include/fizzy/fizzy.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,104 @@
extern "C" {
#endif

/// The opaque data type representing a module.
typedef struct FizzyModule FizzyModule;

/// The opaque data type representing an instance (instantiated module).
typedef struct FizzyInstance FizzyInstance;

/// The data type representing numeric values.
///
/// i64 member is used to represent values of both i32 and i64 type.
union FizzyValue
{
uint64_t i64;
float f32;
double f64;
};

/// Result of execution of a function.
typedef struct FizzyExecutionResult
{
/// Whether execution ended with a trap.
bool trapped;
/// Whether function returned a value. Valid only if trapped equals false.
bool has_value;
/// Value returned from a function.
/// Valid only if trapped equals false and has_value equals true.
union FizzyValue value;
} FizzyExecutionResult;


/// Pointer to external function.
///
/// @param context Opaque pointer to execution context.
/// @param instance Pointer to module instance.
/// @param args Pointer to the argument array. Can be NULL iff args_size equals 0.
/// @param args_size Size of the argument array.
/// @param depth Call stack depth.
typedef FizzyExecutionResult (*FizzyExternalFn)(void* context, FizzyInstance* instance,
const union FizzyValue* args, size_t args_size, int depth);

/// External function.
typedef struct FizzyExternalFunction
{
// TODO function type

/// Pointer to function.
FizzyExternalFn function;
/// Opaque pointer to execution context, that will be passed to function.
void* context;
} FizzyExternalFunction;

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

/// Parse binary module.
///
/// @returns non-NULL pointer to module in case of success, NULL otherwise.
const FizzyModule* fizzy_parse(const uint8_t* wasm_binary, size_t wasm_binary_size);

/// Free resources associated with the module.
///
/// Should be called unless @p module was passed to fizzy_instantiate.
/// If passed pointer is NULL, has no effect.
void fizzy_free_module(const FizzyModule* module);

/// Instantiate a module.
/// 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
/// Function expects @a imported_functions to be in the order of imports defined in the module.
/// No validation is done on the number of functions passed in, nor on their order.
/// When number of passed functions or their order is different from the one defined by the
/// module, behaviour is undefined.
FizzyInstance* fizzy_instantiate(const FizzyModule* module,
const FizzyExternalFunction* 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);

/// Execute module function.
///
/// @param instance Pointer to module instance.
/// @param args Pointer to the argument array. Can be NULL if function has 0 inputs.
/// @param depth Call stack depth.
///
/// @note
/// No validation is done on the number of arguments passed in @p args, nor on their types.
/// When number of passed arguments or their types are different from the ones defined by the
/// function type, behaviour is undefined.
FizzyExecutionResult fizzy_execute(
FizzyInstance* instance, uint32_t func_idx, const union FizzyValue* args, int depth);

#ifdef __cplusplus
}
#endif
132 changes: 132 additions & 0 deletions lib/fizzy/capi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,79 @@
// Copyright 2020 The Fizzy Authors.
// SPDX-License-Identifier: Apache-2.0

#include "cxx20/bit.hpp"
#include "execute.hpp"
#include "instantiate.hpp"
#include "parser.hpp"
#include <fizzy/fizzy.h>
#include <memory>

namespace
{
inline const FizzyModule* wrap(const fizzy::Module* module) noexcept
{
return reinterpret_cast<const FizzyModule*>(module);
}

inline const fizzy::Module* unwrap(const FizzyModule* module) noexcept
{
return reinterpret_cast<const fizzy::Module*>(module);
}

inline FizzyValue wrap(fizzy::Value value) noexcept
{
return fizzy::bit_cast<FizzyValue>(value);
}

inline fizzy::Value unwrap(FizzyValue value) noexcept
{
return fizzy::bit_cast<fizzy::Value>(value);
}

inline const FizzyValue* wrap(const fizzy::Value* values) noexcept
{
return reinterpret_cast<const FizzyValue*>(values);
}

inline const fizzy::Value* unwrap(const FizzyValue* values) noexcept
{
return reinterpret_cast<const fizzy::Value*>(values);
}

inline FizzyInstance* wrap(fizzy::Instance* instance) noexcept
{
return reinterpret_cast<FizzyInstance*>(instance);
}

inline fizzy::Instance* unwrap(FizzyInstance* instance) noexcept
{
return reinterpret_cast<fizzy::Instance*>(instance);
}

inline FizzyExecutionResult wrap(const fizzy::ExecutionResult& result) noexcept
{
return {result.trapped, result.has_value, wrap(result.value)};
}

inline fizzy::ExecutionResult unwrap(const FizzyExecutionResult& result) noexcept
{
if (result.trapped)
return fizzy::Trap;
else if (!result.has_value)
return fizzy::Void;
else
return unwrap(result.value);
}

inline auto unwrap(FizzyExternalFn func, void* context) noexcept
{
return [func, context](fizzy::Instance& instance, fizzy::span<const fizzy::Value> args,
int depth) noexcept -> fizzy::ExecutionResult {
const auto result = func(context, wrap(&instance), wrap(args.data()), args.size(), depth);
return unwrap(result);
};
}
} // namespace

extern "C" {
bool fizzy_validate(const uint8_t* wasm_binary, size_t wasm_binary_size)
Expand All @@ -18,4 +89,65 @@ bool fizzy_validate(const uint8_t* wasm_binary, size_t wasm_binary_size)
return false;
}
}

const FizzyModule* fizzy_parse(const uint8_t* wasm_binary, size_t wasm_binary_size)
{
try
{
auto module = fizzy::parse({wasm_binary, wasm_binary_size});
return wrap(module.release());
}
catch (...)
{
return nullptr;
}
}

void fizzy_free_module(const FizzyModule* module)
{
delete unwrap(module);
}

FizzyInstance* fizzy_instantiate(const FizzyModule* module,
const FizzyExternalFunction* imported_functions, size_t imported_functions_size)
{
try
{
// Convert fizzy_external_function to fizzy::ExternalFunction
std::vector<fizzy::ExternalFunction> functions(imported_functions_size);
for (size_t imported_func_idx = 0; imported_func_idx < imported_functions_size;
++imported_func_idx)
{
const auto& cfunc = imported_functions[imported_func_idx];

auto func = unwrap(cfunc.function, cfunc.context);
// TODO get type from input array
auto func_type = unwrap(module)->imported_function_types[imported_func_idx];

functions[imported_func_idx] =
fizzy::ExternalFunction{std::move(func), std::move(func_type)};
}

auto instance = fizzy::instantiate(
std::unique_ptr<const fizzy::Module>(unwrap(module)), std::move(functions));

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

void fizzy_free_instance(FizzyInstance* instance)
{
delete unwrap(instance);
}

FizzyExecutionResult fizzy_execute(
FizzyInstance* instance, uint32_t func_idx, const FizzyValue* args, int depth)
{
const auto result = fizzy::execute(*unwrap(instance), func_idx, unwrap(args), depth);
return wrap(result);
}
}
Loading

0 comments on commit 5896ba7

Please sign in to comment.