Skip to content
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
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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps mention this returns a non-null module pointer on success.

///
/// @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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps mention module must be non-null.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the same comments apply to instantiate.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This calles delete, so is guaranteed to be no-op for null pointer. I'll mention this and add a test.

///
/// 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);
Copy link
Member

@axic axic Oct 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This takes result as a reference. Will that reference be valid after exiting this lambda? Is the wrap(result.value) doing a copy?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both wrap and unwrap for result are doing a copy

};
}
} // 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