Skip to content
Closed
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
78 changes: 78 additions & 0 deletions include/fizzy/fizzy.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,86 @@
extern "C" {
#endif

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

/// The opaque data type representing an instance (instantiated module).
struct 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 == false
bool has_value;
/// Value returned from a function. Valid only if has_value == true
union FizzyValue value;
} FizzyExecutionResult;


/// Pointer to external function.
///
/// @param context Opaque pointer to execution context.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
/// @param context Opaque pointer to execution context.
/// @param context Opaque pointer to execution context.

/// @param instance Pointer to module instance.
/// @param args Pointer to the argument array.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can args be null?

/// @param args_size Size of the argument array.
Copy link
Copy Markdown
Member

@axic axic Oct 1, 2020

Choose a reason for hiding this comment

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

Suggested change
/// @param args_size Size of the argument array.
/// @param args_size Size of the argument array.

/// @param depth Call stack depth.
typedef struct FizzyExecutionResult (*FizzyExternalFn)(void* context,
struct 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.
struct 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.
void fizzy_free_module(struct 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.
/// @param imported_functions_size Size of the imported function array.
struct FizzyInstance* fizzy_instantiate(struct FizzyModule* module,
const struct FizzyExternalFunction* imported_functions, size_t imported_functions_size);

/// Free resources associated with the instance.
void fizzy_free_instance(struct FizzyInstance* instance);

/// Execute module function.
///
/// @param instance Pointer to module instance.
/// @param args Pointer to the argument array.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can args be null?

/// @param depth Call stack depth.
FizzyExecutionResult fizzy_execute(
struct FizzyInstance* instance, uint32_t func_idx, const union FizzyValue* args, int depth);

#ifdef __cplusplus
}
#endif
129 changes: 129 additions & 0 deletions lib/fizzy/capi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,76 @@
// 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 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(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
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Not sure this is a good idea, because 1. corresponding wrap would not be possible or useful; 2. implemetation actualy wraps C function with lambda.

Maybe another name for this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

encapsulate?

{
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" {
struct FizzyModule
{
fizzy::Module module;
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I realize now, that this is not going to work for the case when we want to access already instantiated module, i.e. get FizzyModule* from FizzyInstance* which owns it.

Module has to be dynamically allocated on the C++ side, then on C side module and instance will be represented similarly.

};

bool fizzy_validate(const uint8_t* wasm_binary, size_t wasm_binary_size)
{
try
Expand All @@ -18,4 +84,67 @@ bool fizzy_validate(const uint8_t* wasm_binary, size_t wasm_binary_size)
return false;
}
}

FizzyModule* fizzy_parse(const uint8_t* wasm_binary, size_t wasm_binary_size)
{
try
{
auto cmodule = std::make_unique<FizzyModule>();
cmodule->module = fizzy::parse({wasm_binary, wasm_binary_size});
return cmodule.release();
}
catch (...)
{
return nullptr;
}
}

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

FizzyInstance* fizzy_instantiate(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 = module->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::move(module->module), std::move(functions));

fizzy_free_module(module);
return wrap(instance.release());
}
catch (...)
{
fizzy_free_module(module);
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);
}
}
83 changes: 80 additions & 3 deletions test/compilation/compilation_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,86 @@

#include <fizzy/fizzy.h>

bool dummy(void);
bool validate(const uint8_t* binary, size_t size);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

For the future, the "compilation test" is to check if the code in the header is valid C99, C11. The is no need to add code to it.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I know, but I can imagine the case when the header compiles, but then calling the function doesn't compile in C like you imagine it would.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

There are unittests for that.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The ones compiled in C99 and C11?

bool parse(const uint8_t* binary, size_t size);
bool instantiate(const uint8_t* binary, size_t size);
struct FizzyExecutionResult dummy_host_func(void* context, struct FizzyInstance* instance,
const union FizzyValue* args, size_t args_size, int depth);
bool instantiate_with_host_func(const uint8_t* binary, size_t size);
bool execute(const uint8_t* binary, size_t size);

bool dummy()
bool validate(const uint8_t* binary, size_t size)
{
return fizzy_validate(NULL, 0);
return fizzy_validate(binary, size);
}

bool parse(const uint8_t* binary, size_t size)
{
struct FizzyModule* module = fizzy_parse(binary, size);
if (!module)
return false;

fizzy_free_module(module);
return true;
}

bool instantiate(const uint8_t* binary, size_t size)
{
struct FizzyModule* module = fizzy_parse(binary, size);
if (!module)
return false;

struct FizzyInstance* instance = fizzy_instantiate(module, NULL, 0);
if (!instance)
return false;

fizzy_free_instance(instance);
return true;
}

struct FizzyExecutionResult dummy_host_func(void* context, struct FizzyInstance* instance,
const union FizzyValue* args, size_t args_size, int depth)
{
(void)context;
(void)instance;
(void)args;
(void)args_size;
(void)depth;
struct FizzyExecutionResult res = {true, false, {0}};
return res;
}

bool instantiate_with_host_func(const uint8_t* binary, size_t size)
{
struct FizzyModule* module = fizzy_parse(binary, size);
if (!module)
return false;

FizzyExternalFunction host_funcs[] = {{dummy_host_func, NULL}};

struct FizzyInstance* instance = fizzy_instantiate(module, host_funcs, 1);
if (!instance)
return false;

fizzy_free_instance(instance);
return true;
}

bool execute(const uint8_t* binary, size_t size)
{
struct FizzyModule* module = fizzy_parse(binary, size);
if (!module)
return false;

struct FizzyInstance* instance = fizzy_instantiate(module, NULL, 0);
if (!instance)
return false;

fizzy_execute(instance, 0, NULL, 0);

union FizzyValue args[] = {{1}, {2}};
fizzy_execute(instance, 1, args, 0);

fizzy_free_instance(instance);
return true;
}
Loading