diff --git a/crates/c-api/src/ref.rs b/crates/c-api/src/ref.rs index 5ddc437836..52f46040d8 100644 --- a/crates/c-api/src/ref.rs +++ b/crates/c-api/src/ref.rs @@ -1,14 +1,61 @@ use std::os::raw::c_void; -use wasmtime::ExternRef; +use wasmtime::{ExternRef, Func, Val}; -#[repr(C)] +/// `*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: Option, + 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()) @@ -16,9 +63,11 @@ pub extern "C" fn wasm_ref_copy(r: &wasm_ref_t) -> Box { #[no_mangle] pub extern "C" fn wasm_ref_same(a: &wasm_ref_t, b: &wasm_ref_t) -> bool { - match (a.r.as_ref(), b.r.as_ref()) { - (Some(a), Some(b)) => a.ptr_eq(b), - (None, None) => true, + 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, } } diff --git a/crates/c-api/src/table.rs b/crates/c-api/src/table.rs index c9833702a9..29a675ba53 100644 --- a/crates/c-api/src/table.rs +++ b/crates/c-api/src/table.rs @@ -1,7 +1,8 @@ +use crate::r#ref::{ref_into_val, val_into_ref}; use crate::{handle_result, wasm_func_t, wasm_ref_t, wasmtime_error_t}; use crate::{wasm_extern_t, wasm_store_t, wasm_tabletype_t}; use std::ptr; -use wasmtime::{Extern, Table, Val}; +use wasmtime::{Extern, Table, TableType, Val, ValType}; #[derive(Clone)] #[repr(transparent)] @@ -29,16 +30,21 @@ impl wasm_table_t { } } +fn ref_into_val_for_table(r: Option>, table_ty: &TableType) -> Val { + ref_into_val(r).unwrap_or_else(|| match table_ty.element() { + ValType::FuncRef => Val::FuncRef(None), + ValType::ExternRef => Val::ExternRef(None), + ty => panic!("unsupported table element type: {:?}", ty), + }) +} + #[no_mangle] pub extern "C" fn wasm_table_new( store: &wasm_store_t, tt: &wasm_tabletype_t, init: Option>, ) -> Option> { - let init: Val = match init { - Some(init) => init.r.into(), - None => Val::FuncRef(None), - }; + let init = ref_into_val_for_table(init, &tt.ty().ty); let table = Table::new(&store.store, tt.ty().ty.clone(), init).ok()?; Some(Box::new(wasm_table_t { ext: wasm_extern_t { @@ -77,11 +83,12 @@ pub extern "C" fn wasm_table_type(t: &wasm_table_t) -> Box { } #[no_mangle] -pub extern "C" fn wasm_table_get(t: &wasm_table_t, index: wasm_table_size_t) -> *mut wasm_ref_t { - match t.table().get(index) { - Some(val) => into_funcref(val), - None => into_funcref(Val::FuncRef(None)), - } +pub extern "C" fn wasm_table_get( + t: &wasm_table_t, + index: wasm_table_size_t, +) -> Option> { + let val = t.table().get(index)?; + Some(val_into_ref(val).unwrap()) } #[no_mangle] @@ -108,9 +115,9 @@ pub extern "C" fn wasmtime_funcref_table_get( pub unsafe extern "C" fn wasm_table_set( t: &wasm_table_t, index: wasm_table_size_t, - r: *mut wasm_ref_t, + r: Option>, ) -> bool { - let val = from_funcref(r); + let val = ref_into_val_for_table(r, &t.table().ty()); t.table().set(index, val).is_ok() } @@ -127,26 +134,6 @@ pub extern "C" fn wasmtime_funcref_table_set( handle_result(t.table().set(index, val), |()| {}) } -fn into_funcref(val: Val) -> *mut wasm_ref_t { - if let Val::FuncRef(None) = val { - return ptr::null_mut(); - } - let externref = match val.externref() { - Some(externref) => externref, - None => return ptr::null_mut(), - }; - let r = Box::new(wasm_ref_t { r: externref }); - Box::into_raw(r) -} - -unsafe fn from_funcref(r: *mut wasm_ref_t) -> Val { - if !r.is_null() { - Box::from_raw(r).r.into() - } else { - Val::FuncRef(None) - } -} - #[no_mangle] pub extern "C" fn wasm_table_size(t: &wasm_table_t) -> wasm_table_size_t { t.table().size() @@ -156,9 +143,9 @@ pub extern "C" fn wasm_table_size(t: &wasm_table_t) -> wasm_table_size_t { pub unsafe extern "C" fn wasm_table_grow( t: &wasm_table_t, delta: wasm_table_size_t, - init: *mut wasm_ref_t, + init: Option>, ) -> bool { - let init = from_funcref(init); + let init = ref_into_val_for_table(init, &t.table().ty()); t.table().grow(delta, init).is_ok() } diff --git a/crates/c-api/src/val.rs b/crates/c-api/src/val.rs index befb22f03d..7d567bc993 100644 --- a/crates/c-api/src/val.rs +++ b/crates/c-api/src/val.rs @@ -1,3 +1,4 @@ +use crate::r#ref::{ref_to_val, WasmRefInner}; use crate::{from_valtype, into_valtype, wasm_ref_t, wasm_valkind_t, WASM_I32}; use std::mem::MaybeUninit; use std::ptr; @@ -80,7 +81,17 @@ impl wasm_val_t { Val::ExternRef(r) => wasm_val_t { kind: from_valtype(&ValType::ExternRef), of: wasm_val_union { - ref_: Box::into_raw(Box::new(wasm_ref_t { r })), + ref_: Box::into_raw(Box::new(wasm_ref_t { + r: WasmRefInner::ExternRef(r), + })), + }, + }, + Val::FuncRef(f) => wasm_val_t { + kind: from_valtype(&ValType::FuncRef), + of: wasm_val_union { + ref_: Box::into_raw(Box::new(wasm_ref_t { + r: WasmRefInner::FuncRef(f), + })), }, }, _ => unimplemented!("wasm_val_t::from_val {:?}", val), @@ -93,7 +104,7 @@ impl wasm_val_t { ValType::I64 => Val::from(unsafe { self.of.i64 }), ValType::F32 => Val::from(unsafe { self.of.f32 }), ValType::F64 => Val::from(unsafe { self.of.f64 }), - ValType::ExternRef => Val::ExternRef(unsafe { (*self.of.ref_).r.clone() }), + ValType::ExternRef | ValType::FuncRef => ref_to_val(unsafe { &*self.of.ref_ }), _ => unimplemented!("wasm_val_t::val {:?}", self.kind), } } @@ -104,9 +115,12 @@ pub unsafe extern "C" fn wasm_val_copy(out: &mut MaybeUninit, source ptr::write( out.as_mut_ptr(), match into_valtype(source.kind) { - ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::ExternRef => { - source.clone() - } + ValType::I32 + | ValType::I64 + | ValType::F32 + | ValType::F64 + | ValType::ExternRef + | ValType::FuncRef => source.clone(), _ => unimplemented!("wasm_val_copy arg"), }, );