From 267bf16683bb1f23bc3e23fa16886109985e725f Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Wed, 8 Jul 2020 12:20:49 -0700 Subject: [PATCH] wasmtime-c-api: Add Wasmtime-specific APIs for `externref`s This commit adds APIs to create new `externref` values (with and without finalizers) and to get an `externref`'s wrapped data. --- crates/c-api/include/wasmtime.h | 54 +++++++++++++++++++++++++++++ crates/c-api/src/ref.rs | 60 +++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h index f024ac4e93..d83546c4a0 100644 --- a/crates/c-api/include/wasmtime.h +++ b/crates/c-api/include/wasmtime.h @@ -836,6 +836,60 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_funcref_table_grow( wasm_table_size_t *prev_size ); +/** + * \brief Create a new `externref` value. + * + * Creates a new `externref` value wrapping the provided data. + * + * This function does not take an associated finalizer to clean up the data when + * the reference is reclaimed. If you need a finalizer to clean up the data, + * then use #wasmtime_externref_new_with_finalizer. + */ +WASM_API_EXTERN wasm_val_t wasmtime_externref_new(void *data); + +/** + * \brief A finalizer for an `externref`'s wrapped data. + * + * A finalizer callback to clean up an `externref`'s wrapped data after the + * `externref` has been reclaimed. This is an opportunity to run destructors, + * free dynamically allocated memory, close file handles, etc. + */ +typedef void (*wasmtime_externref_finalizer_t)(void*); + +/** + * \brief Create a new `externref` value with a finalizer. + * + * Creates a new `externref` value wrapping the provided data. + * + * When the reference is reclaimed, the wrapped data is cleaned up with the + * provided finalizer. If you do not need to clean up the wrapped data, then use + * #wasmtime_externref_new. + */ +WASM_API_EXTERN wasm_val_t wasmtime_externref_new_with_finalizer( + void *data, + wasmtime_externref_finalizer_t finalizer +); + +/** + * \brief Get an `externref`'s wrapped data + * + * If the given value is a reference to a non-null `externref`, writes the + * wrapped data that was passed into #wasmtime_externref_new or + * #wasmtime_externref_new_with_finalizer when creating the given `externref` to + * `datap`, and returns `true`. + * + * If the value is a reference to a null `externref`, writes `NULL` to `datap` + * and returns `true`. + * + * If the given value is not an `externref`, returns `false` and leaves `datap` + * unmodified. + * + * Does not take ownership of `val`. + * + * Both `val` and `datap` must not be `NULL`. + */ +WASM_API_EXTERN bool wasmtime_externref_data(wasm_val_t* val, void** datap); + #undef own #ifdef __cplusplus diff --git a/crates/c-api/src/ref.rs b/crates/c-api/src/ref.rs index 52f46040d8..7fca245c2d 100644 --- a/crates/c-api/src/ref.rs +++ b/crates/c-api/src/ref.rs @@ -1,4 +1,7 @@ +use crate::wasm_val_t; +use std::any::Any; use std::os::raw::c_void; +use std::ptr; use wasmtime::{ExternRef, Func, Val}; /// `*mut wasm_ref_t` is a reference type (`externref` or `funcref`), as seen by @@ -92,3 +95,60 @@ pub extern "C" fn wasm_ref_set_host_info_with_finalizer( eprintln!("`wasm_ref_set_host_info_with_finalizer` is not implemented"); std::process::abort(); } + +type wasmtime_externref_finalizer_t = extern "C" fn(*mut c_void); + +struct CExternRef { + data: *mut c_void, + finalizer: Option, +} + +impl Drop for CExternRef { + fn drop(&mut self) { + if let Some(f) = self.finalizer { + f(self.data); + } + } +} + +#[no_mangle] +pub extern "C" fn wasmtime_externref_new(data: *mut c_void) -> wasm_val_t { + wasm_val_t::from_val(Val::ExternRef(Some(ExternRef::new(CExternRef { + data, + finalizer: None, + })))) +} + +#[no_mangle] +pub extern "C" fn wasmtime_externref_new_with_finalizer( + data: *mut c_void, + finalizer: Option, +) -> wasm_val_t { + wasm_val_t::from_val(Val::ExternRef(Some(ExternRef::new(CExternRef { + data, + finalizer, + })))) +} + +#[no_mangle] +pub extern "C" fn wasmtime_externref_data(val: &wasm_val_t, datap: *mut *mut c_void) -> bool { + match val.val() { + Val::ExternRef(None) => { + unsafe { + ptr::write(datap, ptr::null_mut()); + } + true + } + Val::ExternRef(Some(x)) => { + let data = match x.data().downcast_ref::() { + Some(r) => r.data, + None => x.data() as *const dyn Any as *mut c_void, + }; + unsafe { + ptr::write(datap, data); + } + true + } + _ => false, + } +}