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 /// the C API. Because we do not have a uniform representation for `funcref`s /// and `externref`s, a `*mut wasm_ref_t` is morally a /// `Option, Option>>>`. /// /// A null `*mut wasm_ref_t` is either a null `funcref` or a null `externref` /// depending on context (e.g. the table's element type that it is going into or /// coming out of). /// /// Note: this is not `#[repr(C)]` because it is an opaque type in the header, /// and only ever referenced as `*mut wasm_ref_t`. This also lets us use a /// regular, non-`repr(C)` `enum` to define `WasmRefInner`. #[derive(Clone)] pub struct wasm_ref_t { pub(crate) r: WasmRefInner, } #[derive(Clone)] pub(crate) enum WasmRefInner { ExternRef(Option), FuncRef(Option), } wasmtime_c_api_macros::declare_own!(wasm_ref_t); pub(crate) fn ref_into_val(r: Option>) -> Option { // Let callers decide whether to treat this as a null `funcref` or a // null `externref`. let r = r?; Some(match r.r { WasmRefInner::ExternRef(x) => Val::ExternRef(x), WasmRefInner::FuncRef(x) => Val::FuncRef(x), }) } pub(crate) fn ref_to_val(r: &wasm_ref_t) -> Val { match &r.r { WasmRefInner::ExternRef(x) => Val::ExternRef(x.clone()), WasmRefInner::FuncRef(x) => Val::FuncRef(x.clone()), } } pub(crate) fn val_into_ref(val: Val) -> Option> { match val { Val::ExternRef(x) => Some(Box::new(wasm_ref_t { r: WasmRefInner::ExternRef(x), })), Val::FuncRef(x) => Some(Box::new(wasm_ref_t { r: WasmRefInner::FuncRef(x), })), _ => None, } } #[no_mangle] pub extern "C" fn wasm_ref_copy(r: &wasm_ref_t) -> Box { Box::new(r.clone()) } #[no_mangle] pub extern "C" fn wasm_ref_same(a: &wasm_ref_t, b: &wasm_ref_t) -> bool { match (&a.r, &b.r) { (WasmRefInner::ExternRef(Some(a)), WasmRefInner::ExternRef(Some(b))) => a.ptr_eq(b), (WasmRefInner::ExternRef(None), WasmRefInner::ExternRef(None)) => true, // Note: we don't support equality for `Func`, so we always return // `false` for `funcref`s. _ => false, } } #[no_mangle] pub extern "C" fn wasm_ref_get_host_info(_ref: &wasm_ref_t) -> *mut c_void { std::ptr::null_mut() } #[no_mangle] pub extern "C" fn wasm_ref_set_host_info(_ref: &wasm_ref_t, _info: *mut c_void) { eprintln!("`wasm_ref_set_host_info` is not implemented"); std::process::abort(); } #[no_mangle] pub extern "C" fn wasm_ref_set_host_info_with_finalizer( _ref: &wasm_ref_t, _info: *mut c_void, _finalizer: Option, ) { 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 { wasmtime_externref_new_with_finalizer(data, 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, } }