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
100 changes: 2 additions & 98 deletions crates/c-api/include/wasmtime.hh
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
#endif
#endif

#include "wasmtime.h"
#include <wasmtime.h>
#include <wasmtime/error.hh>

namespace wasmtime {

Expand Down Expand Up @@ -130,103 +131,6 @@ private:

#endif

class Trace;

/**
* \brief Errors coming from Wasmtime
*
* This class represents an error that came from Wasmtime and contains a textual
* description of the error that occurred.
*/
class Error {
struct deleter {
void operator()(wasmtime_error_t *p) const { wasmtime_error_delete(p); }
};

std::unique_ptr<wasmtime_error_t, deleter> ptr;

public:
/// \brief Creates an error from the raw C API representation
///
/// Takes ownership of the provided `error`.
Error(wasmtime_error_t *error) : ptr(error) {}

/// \brief Returns the error message associated with this error.
std::string message() const {
wasm_byte_vec_t msg_bytes;
wasmtime_error_message(ptr.get(), &msg_bytes);
auto ret = std::string(msg_bytes.data, msg_bytes.size);
wasm_byte_vec_delete(&msg_bytes);
return ret;
}

/// If this trap represents a call to `exit` for WASI, this will return the
/// optional error code associated with the exit trap.
std::optional<int32_t> i32_exit() const {
int32_t status = 0;
if (wasmtime_error_exit_status(ptr.get(), &status)) {
return status;
}
return std::nullopt;
}

/// Returns the trace of WebAssembly frames associated with this error.
///
/// Note that the `trace` cannot outlive this error object.
Trace trace() const;
};

/// \brief Used to print an error.
inline std::ostream &operator<<(std::ostream &os, const Error &e) {
os << e.message();
return os;
}

/**
* \brief Fallible result type used for Wasmtime.
*
* This type is used as the return value of many methods in the Wasmtime API.
* This behaves similarly to Rust's `Result<T, E>` and will be replaced with a
* C++ standard when it exists.
*/
template <typename T, typename E = Error> class [[nodiscard]] Result {
std::variant<T, E> data;

public:
/// \brief Creates a `Result` from its successful value.
Result(T t) : data(std::move(t)) {}
/// \brief Creates a `Result` from an error value.
Result(E e) : data(std::move(e)) {}

/// \brief Returns `true` if this result is a success, `false` if it's an
/// error
explicit operator bool() const { return data.index() == 0; }

/// \brief Returns the error, if present, aborts if this is not an error.
E &&err() { return std::get<E>(std::move(data)); }
/// \brief Returns the error, if present, aborts if this is not an error.
const E &&err() const { return std::get<E>(std::move(data)); }

/// \brief Returns the success, if present, aborts if this is an error.
T &&ok() { return std::get<T>(std::move(data)); }
/// \brief Returns the success, if present, aborts if this is an error.
const T &&ok() const { return std::get<T>(std::move(data)); }

/// \brief Returns the success, if present, aborts if this is an error.
T unwrap() {
if (*this) {
return this->ok();
}
unwrap_failed();
}

private:
[[noreturn]] void unwrap_failed() {
fprintf(stderr, "error: %s\n", this->err().message().c_str()); // NOLINT
std::abort();
}
};

/// \brief Strategies passed to `Config::strategy`
enum class Strategy {
/// Automatically selects the compilation strategy
Expand Down
119 changes: 119 additions & 0 deletions crates/c-api/include/wasmtime/error.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/**
* \file wasmtime/error.hh
*/

#ifndef WASMTIME_ERROR_HH
#define WASMTIME_ERROR_HH

#include <memory>
#include <optional>
#include <string>
#include <variant>
#include <wasmtime/error.h>

namespace wasmtime {

class Trace;

/**
* \brief Errors coming from Wasmtime
*
* This class represents an error that came from Wasmtime and contains a textual
* description of the error that occurred.
*/
class Error {
struct deleter {
void operator()(wasmtime_error_t *p) const { wasmtime_error_delete(p); }
};

std::unique_ptr<wasmtime_error_t, deleter> ptr;

public:
/// \brief Creates an error from the raw C API representation
///
/// Takes ownership of the provided `error`.
Error(wasmtime_error_t *error) : ptr(error) {}

/// \brief Creates an error with the provided message.
Error(const std::string &s) : ptr(wasmtime_error_new(s.c_str())) {}

/// \brief Returns the error message associated with this error.
std::string message() const {
wasm_byte_vec_t msg_bytes;
wasmtime_error_message(ptr.get(), &msg_bytes);
auto ret = std::string(msg_bytes.data, msg_bytes.size);
wasm_byte_vec_delete(&msg_bytes);
return ret;
}

/// If this trap represents a call to `exit` for WASI, this will return the
/// optional error code associated with the exit trap.
std::optional<int32_t> i32_exit() const {
int32_t status = 0;
if (wasmtime_error_exit_status(ptr.get(), &status)) {
return status;
}
return std::nullopt;
}

/// Returns the trace of WebAssembly frames associated with this error.
///
/// Note that the `trace` cannot outlive this error object.
Trace trace() const;
};

/// \brief Used to print an error.
inline std::ostream &operator<<(std::ostream &os, const Error &e) {
os << e.message();
return os;
}

/**
* \brief Fallible result type used for Wasmtime.
*
* This type is used as the return value of many methods in the Wasmtime API.
* This behaves similarly to Rust's `Result<T, E>` and will be replaced with a
* C++ standard when it exists.
*/
template <typename T, typename E = Error> class [[nodiscard]] Result {
std::variant<T, E> data;

public:
/// \brief Creates a `Result` from its successful value.
Result(T t) : data(std::move(t)) {}
/// \brief Creates a `Result` from an error value.
Result(E e) : data(std::move(e)) {}

/// \brief Returns `true` if this result is a success, `false` if it's an
/// error
explicit operator bool() const { return data.index() == 0; }

/// \brief Returns the error, if present, aborts if this is not an error.
E &&err() { return std::get<E>(std::move(data)); }
/// \brief Returns the error, if present, aborts if this is not an error.
const E &&err() const { return std::get<E>(std::move(data)); }

/// \brief Returns the success, if present, aborts if this is an error.
T &&ok() { return std::get<T>(std::move(data)); }
/// \brief Returns the success, if present, aborts if this is an error.
const T &&ok() const { return std::get<T>(std::move(data)); }

/// \brief Returns the success, if present, aborts if this is an error.
T unwrap() {
if (*this) {
return this->ok();
}
unwrap_failed();
}

private:
[[noreturn]] void unwrap_failed() {
fprintf(stderr, "error: %s\n", this->err().message().c_str()); // NOLINT
std::abort();
}
};

} // namespace wasmtime

#endif // WASMTIME_ERROR_HH

18 changes: 9 additions & 9 deletions crates/c-api/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@ FetchContent_MakeAvailable(googletest)

include(GoogleTest)

function(add_capi_test name path)
add_executable(test-${name} ${path})
function(add_capi_test name)
cmake_parse_arguments(PARSE_ARGV 1 arg "" "" "FILES")
add_executable(test-${name} ${arg_FILES})
target_link_libraries(test-${name} PRIVATE wasmtime-cpp gtest_main)
gtest_discover_tests(test-${name})
endfunction()

add_capi_test(simple simple.cc)
add_capi_test(types types.cc)
add_capi_test(func func.cc)
add_capi_test(component-instantiate component/instantiate.cc)
add_capi_test(simple FILES simple.cc)
add_capi_test(types FILES types.cc)
add_capi_test(func FILES func.cc)
add_capi_test(component-instantiate FILES component/instantiate.cc)
add_capi_test(error FILES error.cc)

# Add a custom test where two files include `wasmtime.hh` and are compiled into
# the same executable (basically makes sure any defined functions in the header
# are tagged with `inline`).
add_executable(test-double-include double-include-a.cc double-include-b.cc)
target_link_libraries(test-double-include PRIVATE wasmtime-cpp gtest_main)
gtest_discover_tests(test-double-include)
add_capi_test(test-double-include FILES double-include-a.cc double-include-b.cc)
21 changes: 21 additions & 0 deletions crates/c-api/tests/error.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include <wasmtime/error.hh>
#include <gtest/gtest.h>

using namespace wasmtime;

TEST(Result, Simple) {
Result<int> ok_result(1);
EXPECT_TRUE(ok_result);
EXPECT_EQ(ok_result.ok(), 1);
EXPECT_EQ(ok_result.unwrap(), 1);

Result<int, std::string> err_result("x");
EXPECT_FALSE(err_result);
EXPECT_EQ(err_result.err(), "x");
}

TEST(Error, Simple) {
Error err("hello");
EXPECT_EQ(err.message(), "hello");
EXPECT_FALSE(err.i32_exit());
}