diff --git a/.gitignore b/.gitignore index fd33a2b..9cd27b3 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ examples/*/out/* examples/*/target examples/*/Cargo.lock examples/doom/upstream/ +crates/c-api/dist *.wad *.WAD examples/wast/* diff --git a/Cargo.lock b/Cargo.lock index af39bbb..70967e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -916,6 +916,10 @@ dependencies = [ "wat", ] +[[package]] +name = "tinywasm-c-api" +version = "0.9.0-alpha.1" + [[package]] name = "tinywasm-cli" version = "0.9.0-alpha.1" diff --git a/Cargo.toml b/Cargo.toml index 444bd71..6b21a6d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ tinywasm-parser={version="0.9.0-alpha.1", path="crates/parser", default-features tinywasm-types={version="0.9.0-alpha.1", path="crates/types", default-features=false} tinywasm-cli={version="0.9.0-alpha.1", path="crates/cli", default-features=false} tinywasm={version="0.9.0-alpha.1", path="crates/tinywasm", default-features=false} +tinywasm-c-api={version="0.9.0-alpha.1", path="crates/c-api", default-features=false} criterion={version="0.8", default-features=false, features=["cargo_bench_support", "rayon"]} eyre="0.6" diff --git a/crates/c-api/Cargo.toml b/crates/c-api/Cargo.toml new file mode 100644 index 0000000..716d520 --- /dev/null +++ b/crates/c-api/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name="tinywasm-c-api" +version.workspace=true +description="C API bindings for TinyWasm" +edition.workspace=true +license.workspace=true +authors.workspace=true +repository.workspace=true +rust-version.workspace=true +keywords.workspace=true +categories.workspace=true +readme="README.md" + +[lib] +name="tinywasm_c_api" +path="src/lib.rs" +crate-type=["rlib", "staticlib", "cdylib"] diff --git a/crates/c-api/Makefile b/crates/c-api/Makefile new file mode 100644 index 0000000..e72a2fd --- /dev/null +++ b/crates/c-api/Makefile @@ -0,0 +1,47 @@ +PACKAGE := tinywasm-c-api +LIB_BASENAME := tinywasm_c_api +RUST_TARGET ?= $(shell rustc -vV | sed -n 's/^host: //p') + +DIST_DIR := dist/$(RUST_TARGET) +DIST_INCLUDE_DIR := $(DIST_DIR)/include +DIST_LIB_DIR := $(DIST_DIR)/lib +CARGO_BUILD_ARGS := -p $(PACKAGE) --release --target $(RUST_TARGET) +TARGET_DIR := ../../target/$(RUST_TARGET)/release + +ifneq (,$(findstring windows,$(RUST_TARGET))) +STATIC_SRC := $(LIB_BASENAME).lib +STATIC_DST := tinywasm.lib +DYNAMIC_SRC := $(LIB_BASENAME).dll +DYNAMIC_DST := tinywasm.dll +else ifneq (,$(findstring apple,$(RUST_TARGET))) +STATIC_SRC := lib$(LIB_BASENAME).a +STATIC_DST := libtinywasm.a +DYNAMIC_SRC := lib$(LIB_BASENAME).dylib +DYNAMIC_DST := libtinywasm.dylib +else +STATIC_SRC := lib$(LIB_BASENAME).a +STATIC_DST := libtinywasm.a +DYNAMIC_SRC := lib$(LIB_BASENAME).so +DYNAMIC_DST := libtinywasm.so +endif + +.PHONY: all header static dynamic clean update-header + +all: header static dynamic + +header: + @mkdir -p "$(DIST_INCLUDE_DIR)" + cp include/wasm.h "$(DIST_INCLUDE_DIR)/wasm.h" + +static: + cargo build $(CARGO_BUILD_ARGS) + @mkdir -p "$(DIST_LIB_DIR)" + cp "$(TARGET_DIR)/$(STATIC_SRC)" "$(DIST_LIB_DIR)/$(STATIC_DST)" + +dynamic: + cargo build $(CARGO_BUILD_ARGS) + @mkdir -p "$(DIST_LIB_DIR)" + cp "$(TARGET_DIR)/$(DYNAMIC_SRC)" "$(DIST_LIB_DIR)/$(DYNAMIC_DST)" + +clean: + rm -rf "$(DIST_DIR)" diff --git a/crates/c-api/README.md b/crates/c-api/README.md new file mode 100644 index 0000000..0f9c882 --- /dev/null +++ b/crates/c-api/README.md @@ -0,0 +1,26 @@ +# `tinywasm-c-api` + +This crate will expose the C API surface for TinyWasm. + +It is currently a placeholder crate that exists to reserve the package name and workspace layout. + +## Artifacts + +For now, this crate only provides boilerplate packaging for future C consumers: + +- a vendored upstream-compatible public header at `include/wasm.h` +- a `Makefile` that builds the Rust crate and stages C-facing artifacts in `dist/` + +Build the staged artifacts with the host Rust target: + +```sh +make -C crates/c-api +``` + +Artifacts are staged under `dist//`. + +Override the target triple when needed: + +```sh +make -C crates/c-api RUST_TARGET=aarch64-apple-darwin +``` diff --git a/crates/c-api/include/wasm.h b/crates/c-api/include/wasm.h new file mode 100644 index 0000000..41f7fa3 --- /dev/null +++ b/crates/c-api/include/wasm.h @@ -0,0 +1,737 @@ +// WebAssembly C API + +#ifndef WASM_H +#define WASM_H + +#include +#include +#include +#include +#include + +#ifndef WASM_API_EXTERN +#if defined(_WIN32) && !defined(__MINGW32__) && !defined(LIBWASM_STATIC) +#define WASM_API_EXTERN __declspec(dllimport) +#else +#define WASM_API_EXTERN +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Auxiliaries + +// Machine types + +inline void assertions(void) { + static_assert(sizeof(float) == sizeof(uint32_t), "incompatible float type"); + static_assert(sizeof(double) == sizeof(uint64_t), "incompatible double type"); + static_assert(sizeof(intptr_t) == sizeof(uint32_t) || + sizeof(intptr_t) == sizeof(uint64_t), + "incompatible pointer type"); +} + +typedef char byte_t; +typedef float float32_t; +typedef double float64_t; + + +// Ownership + +#define own + +// The qualifier `own` is used to indicate ownership of data in this API. +// It is intended to be interpreted similar to a `const` qualifier: +// +// - `own wasm_xxx_t*` owns the pointed-to data +// - `own wasm_xxx_t` distributes to all fields of a struct or union `xxx` +// - `own wasm_xxx_vec_t` owns the vector as well as its elements(!) +// - an `own` function parameter passes ownership from caller to callee +// - an `own` function result passes ownership from callee to caller +// - an exception are `own` pointer parameters named `out`, which are copy-back +// output parameters passing back ownership from callee to caller +// +// Own data is created by `wasm_xxx_new` functions and some others. +// It must be released with the corresponding `wasm_xxx_delete` function. +// +// Deleting a reference does not necessarily delete the underlying object, +// it merely indicates that this owner no longer uses it. +// +// For vectors, `const wasm_xxx_vec_t` is used informally to indicate that +// neither the vector nor its elements should be modified. +// TODO: introduce proper `wasm_xxx_const_vec_t`? + + +#define WASM_DECLARE_OWN(name) \ + typedef struct wasm_##name##_t wasm_##name##_t; \ + \ + WASM_API_EXTERN void wasm_##name##_delete(own wasm_##name##_t*); + + +// Vectors + +#define WASM_DECLARE_VEC(name, ptr_or_none) \ + typedef struct wasm_##name##_vec_t { \ + size_t size; \ + wasm_##name##_t ptr_or_none* data; \ + } wasm_##name##_vec_t; \ + \ + WASM_API_EXTERN void wasm_##name##_vec_new_empty(own wasm_##name##_vec_t* out); \ + WASM_API_EXTERN void wasm_##name##_vec_new_uninitialized( \ + own wasm_##name##_vec_t* out, size_t); \ + WASM_API_EXTERN void wasm_##name##_vec_new( \ + own wasm_##name##_vec_t* out, \ + size_t, own wasm_##name##_t ptr_or_none const[]); \ + WASM_API_EXTERN void wasm_##name##_vec_copy( \ + own wasm_##name##_vec_t* out, const wasm_##name##_vec_t*); \ + WASM_API_EXTERN void wasm_##name##_vec_delete(own wasm_##name##_vec_t*); + + +// Byte vectors + +typedef byte_t wasm_byte_t; +WASM_DECLARE_VEC(byte, ) + +typedef wasm_byte_vec_t wasm_name_t; + +#define wasm_name wasm_byte_vec +#define wasm_name_new wasm_byte_vec_new +#define wasm_name_new_empty wasm_byte_vec_new_empty +#define wasm_name_new_uninitialized wasm_byte_vec_new_uninitialized +#define wasm_name_copy wasm_byte_vec_copy +#define wasm_name_delete wasm_byte_vec_delete + +static inline void wasm_name_new_from_string( + own wasm_name_t* out, const char* s +) { + wasm_name_new(out, strlen(s), s); +} + +static inline void wasm_name_new_from_string_nt( + own wasm_name_t* out, const char* s +) { + wasm_name_new(out, strlen(s) + 1, s); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Runtime Environment + +// Configuration + +WASM_DECLARE_OWN(config) + +WASM_API_EXTERN own wasm_config_t* wasm_config_new(void); + +// Embedders may provide custom functions for manipulating configs. + + +// Engine + +WASM_DECLARE_OWN(engine) + +WASM_API_EXTERN own wasm_engine_t* wasm_engine_new(void); +WASM_API_EXTERN own wasm_engine_t* wasm_engine_new_with_config(own wasm_config_t*); + + +// Store + +WASM_DECLARE_OWN(store) + +WASM_API_EXTERN own wasm_store_t* wasm_store_new(wasm_engine_t*); + + +/////////////////////////////////////////////////////////////////////////////// +// Type Representations + +// Type attributes + +typedef uint8_t wasm_mutability_t; +enum wasm_mutability_enum { + WASM_CONST, + WASM_VAR, +}; + +typedef struct wasm_limits_t { + uint32_t min; + uint32_t max; +} wasm_limits_t; + +static const uint32_t wasm_limits_max_default = 0xffffffff; + + +// Generic + +#define WASM_DECLARE_TYPE(name) \ + WASM_DECLARE_OWN(name) \ + WASM_DECLARE_VEC(name, *) \ + \ + WASM_API_EXTERN own wasm_##name##_t* wasm_##name##_copy(const wasm_##name##_t*); + + +// Value Types + +WASM_DECLARE_TYPE(valtype) + +typedef uint8_t wasm_valkind_t; +enum wasm_valkind_enum { + WASM_I32, + WASM_I64, + WASM_F32, + WASM_F64, + WASM_EXTERNREF = 128, + WASM_FUNCREF, +}; + +WASM_API_EXTERN own wasm_valtype_t* wasm_valtype_new(wasm_valkind_t); + +WASM_API_EXTERN wasm_valkind_t wasm_valtype_kind(const wasm_valtype_t*); + +static inline bool wasm_valkind_is_num(wasm_valkind_t k) { + return k < WASM_EXTERNREF; +} +static inline bool wasm_valkind_is_ref(wasm_valkind_t k) { + return k >= WASM_EXTERNREF; +} + +static inline bool wasm_valtype_is_num(const wasm_valtype_t* t) { + return wasm_valkind_is_num(wasm_valtype_kind(t)); +} +static inline bool wasm_valtype_is_ref(const wasm_valtype_t* t) { + return wasm_valkind_is_ref(wasm_valtype_kind(t)); +} + + +// Function Types + +WASM_DECLARE_TYPE(functype) + +WASM_API_EXTERN own wasm_functype_t* wasm_functype_new( + own wasm_valtype_vec_t* params, own wasm_valtype_vec_t* results); + +WASM_API_EXTERN const wasm_valtype_vec_t* wasm_functype_params(const wasm_functype_t*); +WASM_API_EXTERN const wasm_valtype_vec_t* wasm_functype_results(const wasm_functype_t*); + + +// Global Types + +WASM_DECLARE_TYPE(globaltype) + +WASM_API_EXTERN own wasm_globaltype_t* wasm_globaltype_new( + own wasm_valtype_t*, wasm_mutability_t); + +WASM_API_EXTERN const wasm_valtype_t* wasm_globaltype_content(const wasm_globaltype_t*); +WASM_API_EXTERN wasm_mutability_t wasm_globaltype_mutability(const wasm_globaltype_t*); + + +// Table Types + +WASM_DECLARE_TYPE(tabletype) + +WASM_API_EXTERN own wasm_tabletype_t* wasm_tabletype_new( + own wasm_valtype_t*, const wasm_limits_t*); + +WASM_API_EXTERN const wasm_valtype_t* wasm_tabletype_element(const wasm_tabletype_t*); +WASM_API_EXTERN const wasm_limits_t* wasm_tabletype_limits(const wasm_tabletype_t*); + + +// Memory Types + +WASM_DECLARE_TYPE(memorytype) + +WASM_API_EXTERN own wasm_memorytype_t* wasm_memorytype_new(const wasm_limits_t*); + +WASM_API_EXTERN const wasm_limits_t* wasm_memorytype_limits(const wasm_memorytype_t*); + + +// Tag Types + +WASM_DECLARE_TYPE(tagtype) + +WASM_API_EXTERN own wasm_tagtype_t* wasm_tagtype_new(own wasm_functype_t*); + +WASM_API_EXTERN const wasm_functype_t* wasm_tagtype_functype(const wasm_tagtype_t*); + + +// Extern Types + +WASM_DECLARE_TYPE(externtype) + +typedef uint8_t wasm_externkind_t; +enum wasm_externkind_enum { + WASM_EXTERN_FUNC, + WASM_EXTERN_GLOBAL, + WASM_EXTERN_TABLE, + WASM_EXTERN_MEMORY, + WASM_EXTERN_TAG, +}; + +WASM_API_EXTERN wasm_externkind_t wasm_externtype_kind(const wasm_externtype_t*); + +WASM_API_EXTERN wasm_externtype_t* wasm_functype_as_externtype(wasm_functype_t*); +WASM_API_EXTERN wasm_externtype_t* wasm_globaltype_as_externtype(wasm_globaltype_t*); +WASM_API_EXTERN wasm_externtype_t* wasm_tabletype_as_externtype(wasm_tabletype_t*); +WASM_API_EXTERN wasm_externtype_t* wasm_memorytype_as_externtype(wasm_memorytype_t*); +WASM_API_EXTERN wasm_externtype_t* wasm_tagtype_as_externtype(wasm_tagtype_t*); + +WASM_API_EXTERN wasm_functype_t* wasm_externtype_as_functype(wasm_externtype_t*); +WASM_API_EXTERN wasm_globaltype_t* wasm_externtype_as_globaltype(wasm_externtype_t*); +WASM_API_EXTERN wasm_tabletype_t* wasm_externtype_as_tabletype(wasm_externtype_t*); +WASM_API_EXTERN wasm_memorytype_t* wasm_externtype_as_memorytype(wasm_externtype_t*); +WASM_API_EXTERN wasm_tagtype_t* wasm_externtype_as_tagtype(wasm_externtype_t*); + +WASM_API_EXTERN const wasm_externtype_t* wasm_functype_as_externtype_const(const wasm_functype_t*); +WASM_API_EXTERN const wasm_externtype_t* wasm_globaltype_as_externtype_const(const wasm_globaltype_t*); +WASM_API_EXTERN const wasm_externtype_t* wasm_tabletype_as_externtype_const(const wasm_tabletype_t*); +WASM_API_EXTERN const wasm_externtype_t* wasm_memorytype_as_externtype_const(const wasm_memorytype_t*); +WASM_API_EXTERN const wasm_externtype_t* wasm_tagtype_as_externtype_const(const wasm_tagtype_t*); + +WASM_API_EXTERN const wasm_functype_t* wasm_externtype_as_functype_const(const wasm_externtype_t*); +WASM_API_EXTERN const wasm_globaltype_t* wasm_externtype_as_globaltype_const(const wasm_externtype_t*); +WASM_API_EXTERN const wasm_tabletype_t* wasm_externtype_as_tabletype_const(const wasm_externtype_t*); +WASM_API_EXTERN const wasm_memorytype_t* wasm_externtype_as_memorytype_const(const wasm_externtype_t*); +WASM_API_EXTERN const wasm_tagtype_t* wasm_externtype_as_tagtype_const(const wasm_externtype_t*); + + +// Import Types + +WASM_DECLARE_TYPE(importtype) + +WASM_API_EXTERN own wasm_importtype_t* wasm_importtype_new( + own wasm_name_t* module, own wasm_name_t* name, own wasm_externtype_t*); + +WASM_API_EXTERN const wasm_name_t* wasm_importtype_module(const wasm_importtype_t*); +WASM_API_EXTERN const wasm_name_t* wasm_importtype_name(const wasm_importtype_t*); +WASM_API_EXTERN const wasm_externtype_t* wasm_importtype_type(const wasm_importtype_t*); + + +// Export Types + +WASM_DECLARE_TYPE(exporttype) + +WASM_API_EXTERN own wasm_exporttype_t* wasm_exporttype_new( + own wasm_name_t*, own wasm_externtype_t*); + +WASM_API_EXTERN const wasm_name_t* wasm_exporttype_name(const wasm_exporttype_t*); +WASM_API_EXTERN const wasm_externtype_t* wasm_exporttype_type(const wasm_exporttype_t*); + + +/////////////////////////////////////////////////////////////////////////////// +// Runtime Objects + +// Values + +struct wasm_ref_t; + +typedef struct wasm_val_t { + wasm_valkind_t kind; + union { + int32_t i32; + int64_t i64; + float32_t f32; + float64_t f64; + struct wasm_ref_t* ref; + } of; +} wasm_val_t; + +WASM_API_EXTERN void wasm_val_delete(own wasm_val_t* v); +WASM_API_EXTERN void wasm_val_copy(own wasm_val_t* out, const wasm_val_t*); + +WASM_DECLARE_VEC(val, ) + + +// References + +#define WASM_DECLARE_REF_BASE(name) \ + WASM_DECLARE_OWN(name) \ + \ + WASM_API_EXTERN own wasm_##name##_t* wasm_##name##_copy(const wasm_##name##_t*); \ + WASM_API_EXTERN bool wasm_##name##_same(const wasm_##name##_t*, const wasm_##name##_t*); \ + \ + WASM_API_EXTERN void* wasm_##name##_get_host_info(const wasm_##name##_t*); \ + WASM_API_EXTERN void wasm_##name##_set_host_info(wasm_##name##_t*, void*); \ + WASM_API_EXTERN void wasm_##name##_set_host_info_with_finalizer( \ + wasm_##name##_t*, void*, void (*)(void*)); + +#define WASM_DECLARE_REF(name) \ + WASM_DECLARE_REF_BASE(name) \ + \ + WASM_API_EXTERN wasm_ref_t* wasm_##name##_as_ref(wasm_##name##_t*); \ + WASM_API_EXTERN wasm_##name##_t* wasm_ref_as_##name(wasm_ref_t*); \ + WASM_API_EXTERN const wasm_ref_t* wasm_##name##_as_ref_const(const wasm_##name##_t*); \ + WASM_API_EXTERN const wasm_##name##_t* wasm_ref_as_##name##_const(const wasm_ref_t*); + +#define WASM_DECLARE_SHARABLE_REF(name) \ + WASM_DECLARE_REF(name) \ + WASM_DECLARE_OWN(shared_##name) \ + \ + WASM_API_EXTERN own wasm_shared_##name##_t* wasm_##name##_share(const wasm_##name##_t*); \ + WASM_API_EXTERN own wasm_##name##_t* wasm_##name##_obtain(wasm_store_t*, const wasm_shared_##name##_t*); + + +WASM_DECLARE_REF_BASE(ref) + + +// Frames + +WASM_DECLARE_OWN(frame) +WASM_DECLARE_VEC(frame, *) +WASM_API_EXTERN own wasm_frame_t* wasm_frame_copy(const wasm_frame_t*); + +WASM_API_EXTERN struct wasm_instance_t* wasm_frame_instance(const wasm_frame_t*); +WASM_API_EXTERN uint32_t wasm_frame_func_index(const wasm_frame_t*); +WASM_API_EXTERN size_t wasm_frame_func_offset(const wasm_frame_t*); +WASM_API_EXTERN size_t wasm_frame_module_offset(const wasm_frame_t*); + + +// Traps + +typedef wasm_name_t wasm_message_t; // null terminated + +WASM_DECLARE_REF(trap) + +WASM_API_EXTERN own wasm_trap_t* wasm_trap_new(wasm_store_t* store, const wasm_message_t*); + +WASM_API_EXTERN void wasm_trap_message(const wasm_trap_t*, own wasm_message_t* out); +WASM_API_EXTERN own wasm_frame_t* wasm_trap_origin(const wasm_trap_t*); +WASM_API_EXTERN void wasm_trap_trace(const wasm_trap_t*, own wasm_frame_vec_t* out); + + +// Foreign Objects + +WASM_DECLARE_REF(foreign) + +WASM_API_EXTERN own wasm_foreign_t* wasm_foreign_new(wasm_store_t*); + + +// Modules + +WASM_DECLARE_SHARABLE_REF(module) + +WASM_API_EXTERN own wasm_module_t* wasm_module_new( + wasm_store_t*, const wasm_byte_vec_t* binary); + +WASM_API_EXTERN bool wasm_module_validate(wasm_store_t*, const wasm_byte_vec_t* binary); + +WASM_API_EXTERN void wasm_module_imports(const wasm_module_t*, own wasm_importtype_vec_t* out); +WASM_API_EXTERN void wasm_module_exports(const wasm_module_t*, own wasm_exporttype_vec_t* out); + +WASM_API_EXTERN void wasm_module_serialize(const wasm_module_t*, own wasm_byte_vec_t* out); +WASM_API_EXTERN own wasm_module_t* wasm_module_deserialize(wasm_store_t*, const wasm_byte_vec_t*); + + +// Function Instances + +WASM_DECLARE_REF(func) + +typedef own wasm_trap_t* (*wasm_func_callback_t)( + const wasm_val_vec_t* args, own wasm_val_vec_t* results); +typedef own wasm_trap_t* (*wasm_func_callback_with_env_t)( + void* env, const wasm_val_vec_t* args, wasm_val_vec_t* results); + +WASM_API_EXTERN own wasm_func_t* wasm_func_new( + wasm_store_t*, const wasm_functype_t*, wasm_func_callback_t); +WASM_API_EXTERN own wasm_func_t* wasm_func_new_with_env( + wasm_store_t*, const wasm_functype_t* type, wasm_func_callback_with_env_t, + void* env, void (*finalizer)(void*)); + +WASM_API_EXTERN own wasm_functype_t* wasm_func_type(const wasm_func_t*); +WASM_API_EXTERN size_t wasm_func_param_arity(const wasm_func_t*); +WASM_API_EXTERN size_t wasm_func_result_arity(const wasm_func_t*); + +WASM_API_EXTERN own wasm_trap_t* wasm_func_call( + const wasm_func_t*, const wasm_val_vec_t* args, wasm_val_vec_t* results); + + +// Global Instances + +WASM_DECLARE_REF(global) + +WASM_API_EXTERN own wasm_global_t* wasm_global_new( + wasm_store_t*, const wasm_globaltype_t*, const wasm_val_t*); + +WASM_API_EXTERN own wasm_globaltype_t* wasm_global_type(const wasm_global_t*); + +WASM_API_EXTERN void wasm_global_get(const wasm_global_t*, own wasm_val_t* out); +WASM_API_EXTERN void wasm_global_set(wasm_global_t*, const wasm_val_t*); + + +// Table Instances + +WASM_DECLARE_REF(table) + +typedef uint32_t wasm_table_size_t; + +WASM_API_EXTERN own wasm_table_t* wasm_table_new( + wasm_store_t*, const wasm_tabletype_t*, wasm_ref_t* init); + +WASM_API_EXTERN own wasm_tabletype_t* wasm_table_type(const wasm_table_t*); + +WASM_API_EXTERN own wasm_ref_t* wasm_table_get(const wasm_table_t*, wasm_table_size_t index); +WASM_API_EXTERN bool wasm_table_set(wasm_table_t*, wasm_table_size_t index, wasm_ref_t*); + +WASM_API_EXTERN wasm_table_size_t wasm_table_size(const wasm_table_t*); +WASM_API_EXTERN bool wasm_table_grow(wasm_table_t*, wasm_table_size_t delta, wasm_ref_t* init); + + +// Memory Instances + +WASM_DECLARE_REF(memory) + +typedef uint32_t wasm_memory_pages_t; + +static const size_t MEMORY_PAGE_SIZE = 0x10000; + +WASM_API_EXTERN own wasm_memory_t* wasm_memory_new(wasm_store_t*, const wasm_memorytype_t*); + +WASM_API_EXTERN own wasm_memorytype_t* wasm_memory_type(const wasm_memory_t*); + +WASM_API_EXTERN byte_t* wasm_memory_data(wasm_memory_t*); +WASM_API_EXTERN size_t wasm_memory_data_size(const wasm_memory_t*); + +WASM_API_EXTERN wasm_memory_pages_t wasm_memory_size(const wasm_memory_t*); +WASM_API_EXTERN bool wasm_memory_grow(wasm_memory_t*, wasm_memory_pages_t delta); + + +// Externals + +WASM_DECLARE_REF(extern) +WASM_DECLARE_VEC(extern, *) + +WASM_API_EXTERN wasm_externkind_t wasm_extern_kind(const wasm_extern_t*); +WASM_API_EXTERN own wasm_externtype_t* wasm_extern_type(const wasm_extern_t*); + +WASM_API_EXTERN wasm_extern_t* wasm_func_as_extern(wasm_func_t*); +WASM_API_EXTERN wasm_extern_t* wasm_global_as_extern(wasm_global_t*); +WASM_API_EXTERN wasm_extern_t* wasm_table_as_extern(wasm_table_t*); +WASM_API_EXTERN wasm_extern_t* wasm_memory_as_extern(wasm_memory_t*); + +WASM_API_EXTERN wasm_func_t* wasm_extern_as_func(wasm_extern_t*); +WASM_API_EXTERN wasm_global_t* wasm_extern_as_global(wasm_extern_t*); +WASM_API_EXTERN wasm_table_t* wasm_extern_as_table(wasm_extern_t*); +WASM_API_EXTERN wasm_memory_t* wasm_extern_as_memory(wasm_extern_t*); + +WASM_API_EXTERN const wasm_extern_t* wasm_func_as_extern_const(const wasm_func_t*); +WASM_API_EXTERN const wasm_extern_t* wasm_global_as_extern_const(const wasm_global_t*); +WASM_API_EXTERN const wasm_extern_t* wasm_table_as_extern_const(const wasm_table_t*); +WASM_API_EXTERN const wasm_extern_t* wasm_memory_as_extern_const(const wasm_memory_t*); + +WASM_API_EXTERN const wasm_func_t* wasm_extern_as_func_const(const wasm_extern_t*); +WASM_API_EXTERN const wasm_global_t* wasm_extern_as_global_const(const wasm_extern_t*); +WASM_API_EXTERN const wasm_table_t* wasm_extern_as_table_const(const wasm_extern_t*); +WASM_API_EXTERN const wasm_memory_t* wasm_extern_as_memory_const(const wasm_extern_t*); + + +// Module Instances + +WASM_DECLARE_REF(instance) + +WASM_API_EXTERN own wasm_instance_t* wasm_instance_new( + wasm_store_t*, const wasm_module_t*, const wasm_extern_vec_t* imports, + own wasm_trap_t** +); + +WASM_API_EXTERN void wasm_instance_exports(const wasm_instance_t*, own wasm_extern_vec_t* out); + + +/////////////////////////////////////////////////////////////////////////////// +// Convenience + +// Vectors + +#define WASM_EMPTY_VEC {0, NULL} +#define WASM_ARRAY_VEC(array) {sizeof(array)/sizeof(*(array)), array} + + +// Value Type construction short-hands + +static inline own wasm_valtype_t* wasm_valtype_new_i32(void) { + return wasm_valtype_new(WASM_I32); +} +static inline own wasm_valtype_t* wasm_valtype_new_i64(void) { + return wasm_valtype_new(WASM_I64); +} +static inline own wasm_valtype_t* wasm_valtype_new_f32(void) { + return wasm_valtype_new(WASM_F32); +} +static inline own wasm_valtype_t* wasm_valtype_new_f64(void) { + return wasm_valtype_new(WASM_F64); +} + +static inline own wasm_valtype_t* wasm_valtype_new_externref(void) { + return wasm_valtype_new(WASM_EXTERNREF); +} +static inline own wasm_valtype_t* wasm_valtype_new_funcref(void) { + return wasm_valtype_new(WASM_FUNCREF); +} + + +// Function Types construction short-hands + +static inline own wasm_functype_t* wasm_functype_new_0_0(void) { + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new_empty(¶ms); + wasm_valtype_vec_new_empty(&results); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_1_0( + own wasm_valtype_t* p +) { + wasm_valtype_t* ps[1] = {p}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 1, ps); + wasm_valtype_vec_new_empty(&results); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_2_0( + own wasm_valtype_t* p1, own wasm_valtype_t* p2 +) { + wasm_valtype_t* ps[2] = {p1, p2}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 2, ps); + wasm_valtype_vec_new_empty(&results); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_3_0( + own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* p3 +) { + wasm_valtype_t* ps[3] = {p1, p2, p3}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 3, ps); + wasm_valtype_vec_new_empty(&results); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_0_1( + own wasm_valtype_t* r +) { + wasm_valtype_t* rs[1] = {r}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new_empty(¶ms); + wasm_valtype_vec_new(&results, 1, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_1_1( + own wasm_valtype_t* p, own wasm_valtype_t* r +) { + wasm_valtype_t* ps[1] = {p}; + wasm_valtype_t* rs[1] = {r}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 1, ps); + wasm_valtype_vec_new(&results, 1, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_2_1( + own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* r +) { + wasm_valtype_t* ps[2] = {p1, p2}; + wasm_valtype_t* rs[1] = {r}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 2, ps); + wasm_valtype_vec_new(&results, 1, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_3_1( + own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* p3, + own wasm_valtype_t* r +) { + wasm_valtype_t* ps[3] = {p1, p2, p3}; + wasm_valtype_t* rs[1] = {r}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 3, ps); + wasm_valtype_vec_new(&results, 1, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_0_2( + own wasm_valtype_t* r1, own wasm_valtype_t* r2 +) { + wasm_valtype_t* rs[2] = {r1, r2}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new_empty(¶ms); + wasm_valtype_vec_new(&results, 2, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_1_2( + own wasm_valtype_t* p, own wasm_valtype_t* r1, own wasm_valtype_t* r2 +) { + wasm_valtype_t* ps[1] = {p}; + wasm_valtype_t* rs[2] = {r1, r2}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 1, ps); + wasm_valtype_vec_new(&results, 2, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_2_2( + own wasm_valtype_t* p1, own wasm_valtype_t* p2, + own wasm_valtype_t* r1, own wasm_valtype_t* r2 +) { + wasm_valtype_t* ps[2] = {p1, p2}; + wasm_valtype_t* rs[2] = {r1, r2}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 2, ps); + wasm_valtype_vec_new(&results, 2, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_3_2( + own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* p3, + own wasm_valtype_t* r1, own wasm_valtype_t* r2 +) { + wasm_valtype_t* ps[3] = {p1, p2, p3}; + wasm_valtype_t* rs[2] = {r1, r2}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 3, ps); + wasm_valtype_vec_new(&results, 2, rs); + return wasm_functype_new(¶ms, &results); +} + + +// Value construction short-hands + +static inline void wasm_val_init_ptr(own wasm_val_t* out, void* p) { +#if UINTPTR_MAX == UINT32_MAX + out->kind = WASM_I32; + out->of.i32 = (intptr_t)p; +#elif UINTPTR_MAX == UINT64_MAX + out->kind = WASM_I64; + out->of.i64 = (intptr_t)p; +#endif +} + +static inline void* wasm_val_ptr(const wasm_val_t* val) { +#if UINTPTR_MAX == UINT32_MAX + return (void*)(intptr_t)val->of.i32; +#elif UINTPTR_MAX == UINT64_MAX + return (void*)(intptr_t)val->of.i64; +#endif +} + +#define WASM_I32_VAL(i) {.kind = WASM_I32, .of = {.i32 = i}} +#define WASM_I64_VAL(i) {.kind = WASM_I64, .of = {.i64 = i}} +#define WASM_F32_VAL(z) {.kind = WASM_F32, .of = {.f32 = z}} +#define WASM_F64_VAL(z) {.kind = WASM_F64, .of = {.f64 = z}} +#define WASM_REF_VAL(r) {.kind = WASM_EXTERNREF, .of = {.ref = r}} +#define WASM_INIT_VAL {.kind = WASM_EXTERNREF, .of = {.ref = NULL}} + + +/////////////////////////////////////////////////////////////////////////////// + +#undef own + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // #ifdef WASM_H diff --git a/crates/c-api/src/lib.rs b/crates/c-api/src/lib.rs new file mode 100644 index 0000000..64d4fb7 --- /dev/null +++ b/crates/c-api/src/lib.rs @@ -0,0 +1,233 @@ +#![doc(test( + no_crate_inject, + attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_assignments, unused_variables)) +))] +#![warn(missing_docs, rust_2018_idioms, unreachable_pub)] +#![deny(unsafe_op_in_unsafe_fn)] + +//! `wasm-c-api` for TinyWasm. +//! +//! The canonical public API lives in the vendored `include/wasm.h` header. + +use core::ffi::c_char; +use core::ptr; + +#[allow(non_camel_case_types, missing_docs, unreachable_pub)] +mod abi { + use super::{c_char, ptr}; + + pub type wasm_byte_t = c_char; + pub type wasm_valkind_t = u8; + + pub const WASM_EXTERNREF: wasm_valkind_t = 128; + + #[repr(C)] + pub struct wasm_byte_vec_t { + pub size: usize, + pub data: *mut wasm_byte_t, + } + + #[repr(C)] + pub union wasm_val_inner_t { + pub i32: i32, + pub i64: i64, + pub f32: f32, + pub f64: f64, + pub ref_: *mut wasm_ref_t, + } + + #[repr(C)] + pub struct wasm_val_t { + pub kind: wasm_valkind_t, + pub of: wasm_val_inner_t, + } + + #[repr(C)] + pub struct wasm_ref_t { + _private: u8, + } + + #[repr(C)] + pub struct wasm_config_t { + _private: u8, + } + + #[repr(C)] + pub struct wasm_engine_t { + _private: u8, + } + + #[repr(C)] + pub struct wasm_store_t { + _private: u8, + } + + fn into_owned_ptr(value: T) -> *mut T { + Box::into_raw(Box::new(value)) + } + + unsafe fn drop_owned_ptr(ptr: *mut T) { + if !ptr.is_null() { + unsafe { + drop(Box::from_raw(ptr)); + } + } + } + + unsafe fn byte_vec_from_parts(data: *const wasm_byte_t, size: usize) -> wasm_byte_vec_t { + if size == 0 { + return wasm_byte_vec_t { size: 0, data: ptr::null_mut() }; + } + + let mut bytes = vec![0 as wasm_byte_t; size]; + if !data.is_null() { + unsafe { + ptr::copy_nonoverlapping(data, bytes.as_mut_ptr(), size); + } + } + + let leaked = bytes.leak(); + wasm_byte_vec_t { size, data: leaked.as_mut_ptr() } + } + + unsafe fn byte_vec_delete_inner(vec: *mut wasm_byte_vec_t) { + if vec.is_null() { + return; + } + + let byte_vec = unsafe { &mut *vec }; + if !byte_vec.data.is_null() { + unsafe { + drop(Vec::from_raw_parts(byte_vec.data, byte_vec.size, byte_vec.size)); + } + } + byte_vec.size = 0; + byte_vec.data = ptr::null_mut(); + } + + #[unsafe(no_mangle)] + pub extern "C" fn wasm_config_new() -> *mut wasm_config_t { + into_owned_ptr(wasm_config_t { _private: 0 }) + } + + #[unsafe(no_mangle)] + pub unsafe extern "C" fn wasm_config_delete(config: *mut wasm_config_t) { + unsafe { drop_owned_ptr(config) } + } + + #[unsafe(no_mangle)] + pub extern "C" fn wasm_engine_new() -> *mut wasm_engine_t { + into_owned_ptr(wasm_engine_t { _private: 0 }) + } + + #[unsafe(no_mangle)] + pub unsafe extern "C" fn wasm_engine_new_with_config(config: *mut wasm_config_t) -> *mut wasm_engine_t { + unsafe { wasm_config_delete(config) }; + wasm_engine_new() + } + + #[unsafe(no_mangle)] + pub unsafe extern "C" fn wasm_engine_delete(engine: *mut wasm_engine_t) { + unsafe { drop_owned_ptr(engine) } + } + + #[unsafe(no_mangle)] + pub unsafe extern "C" fn wasm_store_new(_engine: *mut wasm_engine_t) -> *mut wasm_store_t { + into_owned_ptr(wasm_store_t { _private: 0 }) + } + + #[unsafe(no_mangle)] + pub unsafe extern "C" fn wasm_store_delete(store: *mut wasm_store_t) { + unsafe { drop_owned_ptr(store) } + } + + #[unsafe(no_mangle)] + pub unsafe extern "C" fn wasm_byte_vec_new_empty(out: *mut wasm_byte_vec_t) { + if out.is_null() { + return; + } + + unsafe { + *out = wasm_byte_vec_t { size: 0, data: ptr::null_mut() }; + } + } + + #[unsafe(no_mangle)] + pub unsafe extern "C" fn wasm_byte_vec_new_uninitialized(out: *mut wasm_byte_vec_t, size: usize) { + if out.is_null() { + return; + } + + unsafe { + *out = byte_vec_from_parts(ptr::null(), size); + } + } + + #[unsafe(no_mangle)] + pub unsafe extern "C" fn wasm_byte_vec_new(out: *mut wasm_byte_vec_t, size: usize, data: *const wasm_byte_t) { + if out.is_null() { + return; + } + + unsafe { + *out = byte_vec_from_parts(data, size); + } + } + + #[unsafe(no_mangle)] + pub unsafe extern "C" fn wasm_byte_vec_copy(out: *mut wasm_byte_vec_t, src: *const wasm_byte_vec_t) { + if out.is_null() { + return; + } + + if src.is_null() { + unsafe { wasm_byte_vec_new_empty(out) }; + return; + } + + let src = unsafe { &*src }; + unsafe { + *out = byte_vec_from_parts(src.data, src.size); + } + } + + #[unsafe(no_mangle)] + pub unsafe extern "C" fn wasm_byte_vec_delete(vec: *mut wasm_byte_vec_t) { + unsafe { byte_vec_delete_inner(vec) } + } + + #[unsafe(no_mangle)] + pub unsafe extern "C" fn wasm_val_delete(val: *mut wasm_val_t) { + if val.is_null() { + return; + } + + let val = unsafe { &mut *val }; + val.kind = WASM_EXTERNREF; + val.of = wasm_val_inner_t { ref_: ptr::null_mut() }; + } + + #[unsafe(no_mangle)] + pub unsafe extern "C" fn wasm_val_copy(out: *mut wasm_val_t, src: *const wasm_val_t) { + if out.is_null() { + return; + } + + if src.is_null() { + unsafe { + (*out).kind = WASM_EXTERNREF; + (*out).of = wasm_val_inner_t { ref_: ptr::null_mut() }; + } + return; + } + + unsafe { + ptr::copy_nonoverlapping(src, out, 1); + } + } + + #[unsafe(no_mangle)] + pub unsafe extern "C" fn wasm_ref_delete(reference: *mut wasm_ref_t) { + unsafe { drop_owned_ptr(reference) } + } +}