Skip to content

Commit 21bc72a

Browse files
chore: cleanup
Signed-off-by: Henry <mail@henrygressmann.de>
1 parent 9922409 commit 21bc72a

10 files changed

Lines changed: 113 additions & 16 deletions

File tree

‎crates/c-api/README.md‎

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,26 @@ This builds `libtinywasm.so` (or `.dylib` on macOS) and `libtinywasm.a` in
1515
`pkg-config` file, run `make -C crates/c-api install`. Run `make -C crates/c-api example` to
1616
build and run the C example.
1717

18+
## Symbol prefix
19+
20+
By default, the library exports the names in `wasm.h`. To avoid symbol
21+
collisions with another WebAssembly C API implementation, build with a prefix:
22+
23+
```sh
24+
make -C crates/c-api TINYWASM_C_API_PREFIX=my_
25+
```
26+
27+
Define the same prefix before including `tinywasm.h` in C or C++:
28+
29+
```c
30+
#define TINYWASM_C_API_PREFIX my_
31+
#include "tinywasm.h"
32+
```
33+
34+
When building with Cargo directly, enable `custom-prefix` and set the
35+
`TINYWASM_C_API_PREFIX` environment variable. Include `tinywasm.h` before
36+
`wasm.h` so the symbol aliases take effect.
37+
1838
## Notes
1939
2040
- Follow the ownership annotations in `wasm.h` and use the matching delete
@@ -28,5 +48,5 @@ build and run the C example.
2848
the module boundary.
2949
3050
`wasm.h` is vendored from WebAssembly/wasm-c-api commit
31-
`9d6b93764ac96cdd9db51081c363e09d2d488b4d` under
51+
`9d6b93764ac96cdd9db51081c363e09d2d488b4d`, under
3252
[`include/LICENSE-wasm-c-api`](include/LICENSE-wasm-c-api).

‎crates/c-api/examples/add.c‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include <assert.h>
12
#include <stdio.h>
23
#include "tinywasm.h"
34

@@ -30,8 +31,10 @@ int main(void) {
3031
assert(instance && !trap);
3132
wasm_extern_vec_t exports;
3233
wasm_instance_exports(instance, &exports);
34+
/* The export vector owns its handles. wasm_extern_as_func borrows one. */
3335
wasm_val_t arguments[] = { WASM_I32_VAL(20), WASM_I32_VAL(22) };
3436
wasm_val_t result[1];
37+
/* Stack-backed vectors need no vector delete. The call writes the result slot. */
3538
wasm_val_vec_t args = WASM_ARRAY_VEC(arguments);
3639
wasm_val_vec_t results = WASM_ARRAY_VEC(result);
3740
trap = wasm_func_call(wasm_extern_as_func(exports.data[0]), &args, &results);

‎crates/c-api/src/externals.rs‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ export! { pub unsafe extern "C" fn wasm_table_new(store: *mut wasm_store_t, ty:
4848
}).map_or_else(failure, |object| boxed(wasm_ref_t(object)))
4949
}}
5050

51-
/// Runs a checked store operation on an opaque object.
5251
fn with_object<T: Default>(
5352
object: &Object,
5453
action: impl FnOnce(&Rc<StoreState>, &mut Access<'_>) -> tinywasm::Result<T>,

‎crates/c-api/src/function.rs‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ impl<T> ThreadConfined<T> {
3939
}
4040

4141
impl CallbackData {
42+
/// # Safety
43+
/// The C callback and its environment must remain valid for this call, and
44+
/// `args` and `results` must point to valid callback vectors.
4245
unsafe fn call(&self, args: *const wasm_val_vec_t, results: *mut wasm_val_vec_t) -> *mut wasm_trap_t {
4346
unsafe {
4447
match self.function {
@@ -123,7 +126,10 @@ export! { pub unsafe extern "C" fn wasm_func_result_arity(value: *const wasm_fun
123126

124127
export! { pub unsafe extern "C" fn wasm_func_call(value: *const wasm_func_t, args: *const wasm_val_vec_t, results: *mut wasm_val_vec_t) -> *mut wasm_trap_t {
125128
let object = unsafe { (*value).0.clone() };
126-
let state = object.state().expect("function store must be alive");
129+
let state = match object.state() {
130+
Ok(state) => state,
131+
Err(error) => return failure(error),
132+
};
127133
let result = state.access(|access| {
128134
let ObjectKind::Func(function, _) = &object.kind else { return Err(tinywasm::Error::Other("expected function".into())); };
129135
let ty = function.ty(access.store())?.clone();

‎crates/c-api/src/lib.rs‎

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,27 @@
1-
//! Cargo-built implementation of the WebAssembly C API.
1+
//! C API implementation for TinyWasm.
22
//!
3-
//! The public contract is in `include/wasm.h` and `include/tinywasm.h`.
4-
//! All pointer arguments follow those headers' ownership and lifetime rules.
5-
//! Stores and their objects are confined to the creating thread.
3+
//! See `include/wasm.h` and `include/tinywasm.h` for the C interface and ownership rules.
4+
//! Use a store and its objects only on the thread that created the store.
5+
//!
6+
//! # Symbol prefix
7+
//!
8+
//! By default, the library exports the names in `wasm.h`. To avoid collisions
9+
//! with another WebAssembly C API implementation, build with a prefix:
10+
//!
11+
//! ```sh
12+
//! make -C crates/c-api TINYWASM_C_API_PREFIX=my_
13+
//! ```
14+
//!
15+
//! Define the same prefix before including `tinywasm.h` in C or C++:
16+
//!
17+
//! ```c
18+
//! #define TINYWASM_C_API_PREFIX my_
19+
//! #include "tinywasm.h"
20+
//! ```
21+
//!
22+
//! When building with Cargo directly, enable `custom-prefix` and set
23+
//! `TINYWASM_C_API_PREFIX`. Include `tinywasm.h` before `wasm.h` so the aliases
24+
//! take effect.
625
#![allow(non_camel_case_types)]
726
#![deny(unsafe_op_in_unsafe_fn)]
827

@@ -38,9 +57,12 @@ fn boxed<T>(value: T) -> *mut T {
3857
}
3958

4059
/// Deletes a nullable owned opaque handle.
60+
///
61+
/// # Safety
62+
/// `value` must be null or a live handle returned by `boxed` whose ownership
63+
/// has been transferred to this call exactly once.
4164
unsafe fn delete<T>(value: *mut T) {
4265
if !value.is_null() {
43-
// SAFETY: the caller transfers a handle allocated by `boxed` exactly once.
4466
unsafe { drop(Box::from_raw(value)) };
4567
}
4668
}

‎crates/c-api/src/module.rs‎

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,17 +122,15 @@ export! { pub unsafe extern "C" fn wasm_instance_exports(value: *const wasm_inst
122122
let state = object.state()?;
123123
let ObjectKind::Instance(instance) = &object.kind else { return Err(tinywasm::Error::Other("expected instance".into())); };
124124
state.access(|access| {
125-
// Build owned handles only after all fallible conversions complete.
126-
let objects = instance.exports().map(|(_, item)| {
127-
let kind = match item {
125+
let kinds = instance.exports().map(|(_, item)| {
126+
Ok(match item {
128127
ExternItem::Func(func) => { let reference = func.as_func_ref(access.store())?; ObjectKind::Func(func, reference) },
129128
ExternItem::Global(global) => ObjectKind::Global(global), ExternItem::Memory(memory) => ObjectKind::Memory(memory),
130129
ExternItem::Table(table) => ObjectKind::Table(table), ExternItem::Tag(_) => return Err(tinywasm::Error::Other("tag exports are unsupported".into())),
131130
ExternItem::MemoryShared(_) => return Err(tinywasm::Error::Other("shared memory exports are unsupported".into())),
132-
};
133-
Ok(state.intern(kind))
131+
})
134132
}).collect::<tinywasm::Result<Vec<_>>>()?;
135-
Ok(Vector::from_vec(objects.into_iter().map(|object| boxed(wasm_ref_t(object))).collect()))
133+
Ok(Vector::from_vec(kinds.into_iter().map(|kind| boxed(wasm_ref_t(state.intern(kind)))).collect()))
136134
})
137135
})();
138136
unsafe { out.write(result.unwrap_or_else(failure)) };

‎crates/c-api/src/objects.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use std::{
1111
};
1212
use tinywasm::{FuncRef, Function, Global, Memory, Module, ModuleInstance, Table};
1313

14-
/// A finalizer owns exactly one C environment or host-info value.
14+
/// Calls the optional finalizer when replaced or dropped.
1515
pub(crate) struct HostInfo {
1616
pub(crate) data: *mut c_void,
1717
pub(crate) finalizer: Option<unsafe extern "C" fn(*mut c_void)>,

‎crates/c-api/src/tests.rs‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,3 +150,32 @@ fn memory_pointer_lifetime() {
150150
wasm_engine_delete(engine);
151151
}
152152
}
153+
154+
#[test]
155+
fn function_call_after_store_delete() {
156+
let binary = Vector::from_vec(wat::parse_str(r#"(module (func (export "noop")))"#).unwrap());
157+
unsafe {
158+
let engine = wasm_engine_new();
159+
let store = wasm_store_new(engine);
160+
let module = wasm_module_new(store, &binary);
161+
let imports = Vector::default();
162+
let instance = wasm_instance_new(store, module, &imports, ptr::null_mut());
163+
let mut exports = Vector::default();
164+
wasm_instance_exports(instance, &mut exports);
165+
let function = wasm_func_copy(wasm_extern_as_func(exports.as_slice()[0]));
166+
drop(exports);
167+
wasm_instance_delete(instance);
168+
wasm_module_delete(module);
169+
wasm_store_delete(store);
170+
171+
let empty = Vector::default();
172+
let mut results = Vector::default();
173+
assert!(wasm_func_call(function, &empty, &mut results).is_null());
174+
let mut message = Vector::default();
175+
crate::tinywasm_last_error_message(&mut message);
176+
assert!(message.as_slice().ends_with(b"store has been deleted\0"));
177+
178+
wasm_func_delete(function);
179+
wasm_engine_delete(engine);
180+
}
181+
}

‎crates/c-api/src/values.rs‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ impl Element for wasm_val_t {
4545

4646
impl wasm_val_t {
4747
/// Reads a valid C value and roots any reference in the owning runtime store.
48+
///
49+
/// # Safety
50+
/// The value must be initialized, including the union field selected by `kind`.
4851
pub(crate) unsafe fn to_runtime(&self, state: &Rc<StoreState>, store: &mut Store) -> tinywasm::Result<WasmValue> {
4952
Ok(unsafe {
5053
match self.kind {
@@ -92,6 +95,9 @@ impl wasm_val_t {
9295
}
9396

9497
/// Converts a borrowed C reference, preserving store identity.
98+
///
99+
/// # Safety
100+
/// `value` must be null or a live C handle for the duration of this call.
95101
pub(crate) unsafe fn reference_to_runtime(
96102
value: *const wasm_ref_t,
97103
kind: u8,

‎crates/c-api/src/vectors.rs‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,14 @@ use crate::{objects::*, types::*, values::wasm_val_t};
55
/// An element owned by a C API vector.
66
pub trait Element: Default {
77
/// Copies the element and any owned handle.
8+
///
9+
/// # Safety
10+
/// The element and any handle it contains must be initialized and live.
811
unsafe fn copy(&self) -> Self;
912
/// Releases any owned handle.
13+
///
14+
/// # Safety
15+
/// The element must own its handle and must not have been destroyed already.
1016
unsafe fn destroy(&mut self);
1117
}
1218

@@ -51,19 +57,27 @@ impl<T: Element> Vector<T> {
5157
}
5258

5359
/// Borrows initialized elements. Zero-length vectors may have null data.
60+
///
61+
/// # Safety
62+
/// For nonempty vectors, `data` must point to `size` live, initialized
63+
/// elements that are not mutated for the duration of the borrow.
5464
pub(crate) unsafe fn as_slice(&self) -> &[T] {
5565
if self.size == 0 { &[] } else { unsafe { slice::from_raw_parts(self.data, self.size) } }
5666
}
5767

5868
/// Borrows initialized elements exclusively.
69+
///
70+
/// # Safety
71+
/// For nonempty vectors, `data` must point to `size` live, initialized
72+
/// elements exclusively accessible for the duration of the borrow.
5973
pub(crate) unsafe fn as_mut_slice(&mut self) -> &mut [T] {
6074
if self.size == 0 { &mut [] } else { unsafe { slice::from_raw_parts_mut(self.data, self.size) } }
6175
}
6276
}
6377

6478
impl<T: Element> Clone for Vector<T> {
6579
fn clone(&self) -> Self {
66-
// SAFETY: Rust-owned vectors always contain initialized, live elements.
80+
// SAFETY: the source vector must contain initialized, live elements.
6781
unsafe { Self::from_vec(self.as_slice().iter().map(|value| value.copy()).collect()) }
6882
}
6983
}

0 commit comments

Comments
 (0)