wasmtime-c-api: Add support for externref values
This required that `wasm_val_t` have a `Drop` implementation, an explicit `Clone` implementation, and no longer be `Copy`, which rippled out through the crate a bit. Additionally, `wasm_func_call` and friends were creating references to uninitialized data for its out pointers and assigning to them. As soon as `wasm_val_t` gained a `Drop` impl and tried to drop the old value of the assignment (which is uninitialized data), then things blew up. The fix is to properly represent the out pointers with `MaybeUninit`, and use `ptr::write` to initialize them without dropping the old data. Part of #929
This commit is contained in:
@@ -2,6 +2,7 @@ use crate::{wasm_extern_t, wasm_functype_t, wasm_store_t, wasm_val_t};
|
|||||||
use crate::{wasm_name_t, wasm_trap_t, wasmtime_error_t};
|
use crate::{wasm_name_t, wasm_trap_t, wasmtime_error_t};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
use std::panic::{self, AssertUnwindSafe};
|
use std::panic::{self, AssertUnwindSafe};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::str;
|
use std::str;
|
||||||
@@ -89,6 +90,7 @@ fn create_function(
|
|||||||
let func = Func::new(store, ty, move |caller, params, results| {
|
let func = Func::new(store, ty, move |caller, params, results| {
|
||||||
let params = params
|
let params = params
|
||||||
.iter()
|
.iter()
|
||||||
|
.cloned()
|
||||||
.map(|p| wasm_val_t::from_val(p))
|
.map(|p| wasm_val_t::from_val(p))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let mut out_results = vec![wasm_val_t::default(); results.len()];
|
let mut out_results = vec![wasm_val_t::default(); results.len()];
|
||||||
@@ -163,7 +165,7 @@ pub extern "C" fn wasmtime_func_new_with_env(
|
|||||||
pub unsafe extern "C" fn wasm_func_call(
|
pub unsafe extern "C" fn wasm_func_call(
|
||||||
wasm_func: &wasm_func_t,
|
wasm_func: &wasm_func_t,
|
||||||
args: *const wasm_val_t,
|
args: *const wasm_val_t,
|
||||||
results: *mut wasm_val_t,
|
results: *mut MaybeUninit<wasm_val_t>,
|
||||||
) -> *mut wasm_trap_t {
|
) -> *mut wasm_trap_t {
|
||||||
let func = wasm_func.func();
|
let func = wasm_func.func();
|
||||||
let mut trap = ptr::null_mut();
|
let mut trap = ptr::null_mut();
|
||||||
@@ -186,7 +188,7 @@ pub unsafe extern "C" fn wasmtime_func_call(
|
|||||||
func: &wasm_func_t,
|
func: &wasm_func_t,
|
||||||
args: *const wasm_val_t,
|
args: *const wasm_val_t,
|
||||||
num_args: usize,
|
num_args: usize,
|
||||||
results: *mut wasm_val_t,
|
results: *mut MaybeUninit<wasm_val_t>,
|
||||||
num_results: usize,
|
num_results: usize,
|
||||||
trap_ptr: &mut *mut wasm_trap_t,
|
trap_ptr: &mut *mut wasm_trap_t,
|
||||||
) -> Option<Box<wasmtime_error_t>> {
|
) -> Option<Box<wasmtime_error_t>> {
|
||||||
@@ -201,7 +203,7 @@ pub unsafe extern "C" fn wasmtime_func_call(
|
|||||||
fn _wasmtime_func_call(
|
fn _wasmtime_func_call(
|
||||||
func: &wasm_func_t,
|
func: &wasm_func_t,
|
||||||
args: &[wasm_val_t],
|
args: &[wasm_val_t],
|
||||||
results: &mut [wasm_val_t],
|
results: &mut [MaybeUninit<wasm_val_t>],
|
||||||
trap_ptr: &mut *mut wasm_trap_t,
|
trap_ptr: &mut *mut wasm_trap_t,
|
||||||
) -> Option<Box<wasmtime_error_t>> {
|
) -> Option<Box<wasmtime_error_t>> {
|
||||||
let func = func.func();
|
let func = func.func();
|
||||||
@@ -217,8 +219,13 @@ fn _wasmtime_func_call(
|
|||||||
let result = panic::catch_unwind(AssertUnwindSafe(|| func.call(¶ms)));
|
let result = panic::catch_unwind(AssertUnwindSafe(|| func.call(¶ms)));
|
||||||
match result {
|
match result {
|
||||||
Ok(Ok(out)) => {
|
Ok(Ok(out)) => {
|
||||||
for (slot, val) in results.iter_mut().zip(out.iter()) {
|
for (slot, val) in results.iter_mut().zip(out.into_vec().into_iter()) {
|
||||||
*slot = wasm_val_t::from_val(val);
|
unsafe {
|
||||||
|
// NB: The results array is likely uninitialized memory, so
|
||||||
|
// use `ptr::write` rather than assignment (which tries to
|
||||||
|
// run destructors).
|
||||||
|
ptr::write(slot.as_mut_ptr(), wasm_val_t::from_val(val));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use crate::{handle_result, wasmtime_error_t};
|
use crate::{handle_result, wasmtime_error_t};
|
||||||
use crate::{wasm_extern_t, wasm_globaltype_t, wasm_store_t, wasm_val_t};
|
use crate::{wasm_extern_t, wasm_globaltype_t, wasm_store_t, wasm_val_t};
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use wasmtime::{Extern, Global};
|
use wasmtime::{Extern, Global};
|
||||||
|
|
||||||
@@ -72,8 +73,10 @@ pub extern "C" fn wasm_global_type(g: &wasm_global_t) -> Box<wasm_globaltype_t>
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn wasm_global_get(g: &wasm_global_t, out: &mut wasm_val_t) {
|
pub extern "C" fn wasm_global_get(g: &wasm_global_t, out: &mut MaybeUninit<wasm_val_t>) {
|
||||||
out.set(g.global().get());
|
unsafe {
|
||||||
|
ptr::write(out.as_mut_ptr(), wasm_val_t::from_val(g.global().get()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
use crate::{from_valtype, into_valtype, wasm_ref_t, wasm_valkind_t, WASM_I32};
|
use crate::{from_valtype, into_valtype, wasm_ref_t, wasm_valkind_t, WASM_I32};
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
|
use std::ptr;
|
||||||
use wasmtime::{Val, ValType};
|
use wasmtime::{Val, ValType};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct wasm_val_t {
|
pub struct wasm_val_t {
|
||||||
pub kind: wasm_valkind_t,
|
pub kind: wasm_valkind_t,
|
||||||
pub of: wasm_val_union,
|
pub of: wasm_val_union,
|
||||||
@@ -20,6 +21,34 @@ pub union wasm_val_union {
|
|||||||
pub ref_: *mut wasm_ref_t,
|
pub ref_: *mut wasm_ref_t,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for wasm_val_t {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
match into_valtype(self.kind) {
|
||||||
|
ValType::ExternRef => unsafe {
|
||||||
|
drop(Box::from_raw(self.of.ref_));
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for wasm_val_t {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
match into_valtype(self.kind) {
|
||||||
|
ValType::ExternRef => wasm_val_t {
|
||||||
|
kind: self.kind,
|
||||||
|
of: wasm_val_union {
|
||||||
|
ref_: unsafe { Box::into_raw(Box::new((*self.of.ref_).clone())) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
_ => wasm_val_t {
|
||||||
|
kind: self.kind,
|
||||||
|
of: self.of,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for wasm_val_t {
|
impl Default for wasm_val_t {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
wasm_val_t {
|
wasm_val_t {
|
||||||
@@ -30,46 +59,30 @@ impl Default for wasm_val_t {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl wasm_val_t {
|
impl wasm_val_t {
|
||||||
pub fn from_val(val: &Val) -> wasm_val_t {
|
pub fn from_val(val: Val) -> wasm_val_t {
|
||||||
match val {
|
match val {
|
||||||
Val::I32(i) => wasm_val_t {
|
Val::I32(i) => wasm_val_t {
|
||||||
kind: from_valtype(&ValType::I32),
|
kind: from_valtype(&ValType::I32),
|
||||||
of: wasm_val_union { i32: *i },
|
of: wasm_val_union { i32: i },
|
||||||
},
|
},
|
||||||
Val::I64(i) => wasm_val_t {
|
Val::I64(i) => wasm_val_t {
|
||||||
kind: from_valtype(&ValType::I64),
|
kind: from_valtype(&ValType::I64),
|
||||||
of: wasm_val_union { i64: *i },
|
of: wasm_val_union { i64: i },
|
||||||
},
|
},
|
||||||
Val::F32(f) => wasm_val_t {
|
Val::F32(f) => wasm_val_t {
|
||||||
kind: from_valtype(&ValType::F32),
|
kind: from_valtype(&ValType::F32),
|
||||||
of: wasm_val_union { u32: *f },
|
of: wasm_val_union { u32: f },
|
||||||
},
|
},
|
||||||
Val::F64(f) => wasm_val_t {
|
Val::F64(f) => wasm_val_t {
|
||||||
kind: from_valtype(&ValType::F64),
|
kind: from_valtype(&ValType::F64),
|
||||||
of: wasm_val_union { u64: *f },
|
of: wasm_val_union { u64: f },
|
||||||
|
},
|
||||||
|
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 })),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
_ => unimplemented!("wasm_val_t::from_val {:?}", val),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set(&mut self, val: Val) {
|
|
||||||
match val {
|
|
||||||
Val::I32(i) => {
|
|
||||||
self.kind = from_valtype(&ValType::I32);
|
|
||||||
self.of = wasm_val_union { i32: i };
|
|
||||||
}
|
|
||||||
Val::I64(i) => {
|
|
||||||
self.kind = from_valtype(&ValType::I64);
|
|
||||||
self.of = wasm_val_union { i64: i };
|
|
||||||
}
|
|
||||||
Val::F32(f) => {
|
|
||||||
self.kind = from_valtype(&ValType::F32);
|
|
||||||
self.of = wasm_val_union { u32: f };
|
|
||||||
}
|
|
||||||
Val::F64(f) => {
|
|
||||||
self.kind = from_valtype(&ValType::F64);
|
|
||||||
self.of = wasm_val_union { u64: f };
|
|
||||||
}
|
|
||||||
_ => unimplemented!("wasm_val_t::from_val {:?}", val),
|
_ => unimplemented!("wasm_val_t::from_val {:?}", val),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,20 +93,26 @@ impl wasm_val_t {
|
|||||||
ValType::I64 => Val::from(unsafe { self.of.i64 }),
|
ValType::I64 => Val::from(unsafe { self.of.i64 }),
|
||||||
ValType::F32 => Val::from(unsafe { self.of.f32 }),
|
ValType::F32 => Val::from(unsafe { self.of.f32 }),
|
||||||
ValType::F64 => Val::from(unsafe { self.of.f64 }),
|
ValType::F64 => Val::from(unsafe { self.of.f64 }),
|
||||||
|
ValType::ExternRef => Val::ExternRef(unsafe { (*self.of.ref_).r.clone() }),
|
||||||
_ => unimplemented!("wasm_val_t::val {:?}", self.kind),
|
_ => unimplemented!("wasm_val_t::val {:?}", self.kind),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn wasm_val_copy(out: *mut wasm_val_t, source: &wasm_val_t) {
|
pub unsafe extern "C" fn wasm_val_copy(out: &mut MaybeUninit<wasm_val_t>, source: &wasm_val_t) {
|
||||||
*out = match into_valtype(source.kind) {
|
ptr::write(
|
||||||
ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 => *source,
|
out.as_mut_ptr(),
|
||||||
|
match into_valtype(source.kind) {
|
||||||
|
ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::ExternRef => {
|
||||||
|
source.clone()
|
||||||
|
}
|
||||||
_ => unimplemented!("wasm_val_copy arg"),
|
_ => unimplemented!("wasm_val_copy arg"),
|
||||||
};
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn wasm_val_delete(_val: &mut wasm_val_t) {
|
pub unsafe extern "C" fn wasm_val_delete(val: *mut wasm_val_t) {
|
||||||
// currently we only support integers/floats which need no deletion
|
ptr::drop_in_place(val);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user