1 use crate::wasm_trap_t; 2 use crate::{ 3 wasm_extern_t, wasm_functype_t, wasm_store_t, wasm_val_t, wasm_val_vec_t, wasmtime_error_t, 4 wasmtime_extern_t, wasmtime_val_t, wasmtime_val_union, CStoreContext, CStoreContextMut, 5 }; 6 use anyhow::anyhow; 7 use std::ffi::c_void; 8 use std::mem::MaybeUninit; 9 use std::panic::{self, AssertUnwindSafe}; 10 use std::ptr; 11 use std::str; 12 use wasmtime::{AsContextMut, Caller, Extern, Func, Trap, Val}; 13 14 #[derive(Clone)] 15 #[repr(transparent)] 16 pub struct wasm_func_t { 17 ext: wasm_extern_t, 18 } 19 20 wasmtime_c_api_macros::declare_ref!(wasm_func_t); 21 22 pub type wasm_func_callback_t = extern "C" fn( 23 args: *const wasm_val_vec_t, 24 results: *mut wasm_val_vec_t, 25 ) -> Option<Box<wasm_trap_t>>; 26 27 pub type wasm_func_callback_with_env_t = extern "C" fn( 28 env: *mut std::ffi::c_void, 29 args: *const wasm_val_vec_t, 30 results: *mut wasm_val_vec_t, 31 ) -> Option<Box<wasm_trap_t>>; 32 33 impl wasm_func_t { 34 pub(crate) fn try_from(e: &wasm_extern_t) -> Option<&wasm_func_t> { 35 match &e.which { 36 Extern::Func(_) => Some(unsafe { &*(e as *const _ as *const _) }), 37 _ => None, 38 } 39 } 40 41 pub(crate) fn func(&self) -> Func { 42 match self.ext.which { 43 Extern::Func(f) => f, 44 _ => unsafe { std::hint::unreachable_unchecked() }, 45 } 46 } 47 } 48 49 unsafe fn create_function( 50 store: &mut wasm_store_t, 51 ty: &wasm_functype_t, 52 func: impl Fn(*const wasm_val_vec_t, *mut wasm_val_vec_t) -> Option<Box<wasm_trap_t>> 53 + Send 54 + Sync 55 + 'static, 56 ) -> Box<wasm_func_t> { 57 let ty = ty.ty().ty.clone(); 58 let func = Func::new( 59 store.store.context_mut(), 60 ty, 61 move |_caller, params, results| { 62 let params: wasm_val_vec_t = params 63 .iter() 64 .cloned() 65 .map(|p| wasm_val_t::from_val(p)) 66 .collect::<Vec<_>>() 67 .into(); 68 let mut out_results: wasm_val_vec_t = vec![wasm_val_t::default(); results.len()].into(); 69 let out = func(¶ms, &mut out_results); 70 if let Some(trap) = out { 71 return Err(trap.trap.clone()); 72 } 73 74 let out_results = out_results.as_slice(); 75 for i in 0..results.len() { 76 results[i] = out_results[i].val(); 77 } 78 Ok(()) 79 }, 80 ); 81 Box::new(wasm_func_t { 82 ext: wasm_extern_t { 83 store: store.store.clone(), 84 which: func.into(), 85 }, 86 }) 87 } 88 89 #[no_mangle] 90 pub unsafe extern "C" fn wasm_func_new( 91 store: &mut wasm_store_t, 92 ty: &wasm_functype_t, 93 callback: wasm_func_callback_t, 94 ) -> Box<wasm_func_t> { 95 create_function(store, ty, move |params, results| callback(params, results)) 96 } 97 98 #[no_mangle] 99 pub unsafe extern "C" fn wasm_func_new_with_env( 100 store: &mut wasm_store_t, 101 ty: &wasm_functype_t, 102 callback: wasm_func_callback_with_env_t, 103 data: *mut c_void, 104 finalizer: Option<extern "C" fn(arg1: *mut std::ffi::c_void)>, 105 ) -> Box<wasm_func_t> { 106 let finalizer = crate::ForeignData { data, finalizer }; 107 create_function(store, ty, move |params, results| { 108 callback(finalizer.data, params, results) 109 }) 110 } 111 112 #[no_mangle] 113 pub unsafe extern "C" fn wasm_func_call( 114 func: &mut wasm_func_t, 115 args: *const wasm_val_vec_t, 116 results: *mut wasm_val_vec_t, 117 ) -> *mut wasm_trap_t { 118 let f = func.func(); 119 let results = (*results).as_uninit_slice(); 120 let args = (*args).as_slice(); 121 if results.len() != f.ty(func.ext.store.context()).results().len() { 122 return Box::into_raw(Box::new(wasm_trap_t::new( 123 anyhow!("wrong number of results provided").into(), 124 ))); 125 } 126 let params = args.iter().map(|i| i.val()).collect::<Vec<_>>(); 127 128 // We're calling arbitrary code here most of the time, and we in general 129 // want to try to insulate callers against bugs in wasmtime/wasi/etc if we 130 // can. As a result we catch panics here and transform them to traps to 131 // allow the caller to have any insulation possible against Rust panics. 132 let result = panic::catch_unwind(AssertUnwindSafe(|| { 133 f.call(func.ext.store.context_mut(), ¶ms) 134 })); 135 match result { 136 Ok(Ok(out)) => { 137 for (slot, val) in results.iter_mut().zip(out.into_vec().into_iter()) { 138 crate::initialize(slot, wasm_val_t::from_val(val)); 139 } 140 ptr::null_mut() 141 } 142 Ok(Err(trap)) => match trap.downcast::<Trap>() { 143 Ok(trap) => Box::into_raw(Box::new(wasm_trap_t::new(trap))), 144 Err(err) => Box::into_raw(Box::new(wasm_trap_t::new(err.into()))), 145 }, 146 Err(panic) => { 147 let trap = if let Some(msg) = panic.downcast_ref::<String>() { 148 Trap::new(msg) 149 } else if let Some(msg) = panic.downcast_ref::<&'static str>() { 150 Trap::new(*msg) 151 } else { 152 Trap::new("rust panic happened") 153 }; 154 let trap = Box::new(wasm_trap_t::new(trap)); 155 Box::into_raw(trap) 156 } 157 } 158 } 159 160 #[no_mangle] 161 pub unsafe extern "C" fn wasm_func_type(f: &wasm_func_t) -> Box<wasm_functype_t> { 162 Box::new(wasm_functype_t::new(f.func().ty(f.ext.store.context()))) 163 } 164 165 #[no_mangle] 166 pub unsafe extern "C" fn wasm_func_param_arity(f: &wasm_func_t) -> usize { 167 f.func().ty(f.ext.store.context()).params().len() 168 } 169 170 #[no_mangle] 171 pub unsafe extern "C" fn wasm_func_result_arity(f: &wasm_func_t) -> usize { 172 f.func().ty(f.ext.store.context()).results().len() 173 } 174 175 #[no_mangle] 176 pub extern "C" fn wasm_func_as_extern(f: &mut wasm_func_t) -> &mut wasm_extern_t { 177 &mut (*f).ext 178 } 179 180 #[no_mangle] 181 pub extern "C" fn wasm_func_as_extern_const(f: &wasm_func_t) -> &wasm_extern_t { 182 &(*f).ext 183 } 184 185 #[repr(C)] 186 pub struct wasmtime_caller_t<'a> { 187 caller: Caller<'a, crate::StoreData>, 188 } 189 190 pub type wasmtime_func_callback_t = extern "C" fn( 191 *mut c_void, 192 *mut wasmtime_caller_t, 193 *const wasmtime_val_t, 194 usize, 195 *mut wasmtime_val_t, 196 usize, 197 ) -> Option<Box<wasm_trap_t>>; 198 199 #[no_mangle] 200 pub unsafe extern "C" fn wasmtime_func_new( 201 store: CStoreContextMut<'_>, 202 ty: &wasm_functype_t, 203 callback: wasmtime_func_callback_t, 204 data: *mut c_void, 205 finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>, 206 func: &mut Func, 207 ) { 208 let ty = ty.ty().ty.clone(); 209 let cb = c_callback_to_rust_fn(callback, data, finalizer); 210 let f = Func::new(store, ty, cb); 211 *func = f; 212 } 213 214 pub(crate) unsafe fn c_callback_to_rust_fn( 215 callback: wasmtime_func_callback_t, 216 data: *mut c_void, 217 finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>, 218 ) -> impl Fn(Caller<'_, crate::StoreData>, &[Val], &mut [Val]) -> Result<(), Trap> { 219 let foreign = crate::ForeignData { data, finalizer }; 220 move |caller, params, results| { 221 let params = params 222 .iter() 223 .cloned() 224 .map(|p| wasmtime_val_t::from_val(p)) 225 .collect::<Vec<_>>(); 226 let mut out_results = (0..results.len()) 227 .map(|_| wasmtime_val_t { 228 kind: crate::WASMTIME_I32, 229 of: wasmtime_val_union { i32: 0 }, 230 }) 231 .collect::<Vec<_>>(); 232 let mut caller = wasmtime_caller_t { caller }; 233 let out = callback( 234 foreign.data, 235 &mut caller, 236 params.as_ptr(), 237 params.len(), 238 out_results.as_mut_ptr(), 239 out_results.len(), 240 ); 241 if let Some(trap) = out { 242 return Err(trap.trap); 243 } 244 245 for (i, result) in out_results.iter().enumerate() { 246 results[i] = unsafe { result.to_val() }; 247 } 248 Ok(()) 249 } 250 } 251 252 #[no_mangle] 253 pub unsafe extern "C" fn wasmtime_func_call( 254 store: CStoreContextMut<'_>, 255 func: &Func, 256 args: *const wasmtime_val_t, 257 nargs: usize, 258 results: *mut MaybeUninit<wasmtime_val_t>, 259 nresults: usize, 260 trap_ret: &mut *mut wasm_trap_t, 261 ) -> Option<Box<wasmtime_error_t>> { 262 if nresults != func.ty(&store).results().len() { 263 return Some(Box::new(wasmtime_error_t::from(anyhow!( 264 "wrong number of results provided" 265 )))); 266 } 267 let params = crate::slice_from_raw_parts(args, nargs) 268 .iter() 269 .map(|i| i.to_val()) 270 .collect::<Vec<_>>(); 271 272 // We're calling arbitrary code here most of the time, and we in general 273 // want to try to insulate callers against bugs in wasmtime/wasi/etc if we 274 // can. As a result we catch panics here and transform them to traps to 275 // allow the caller to have any insulation possible against Rust panics. 276 let result = panic::catch_unwind(AssertUnwindSafe(|| func.call(store, ¶ms))); 277 match result { 278 Ok(Ok(out)) => { 279 let results = crate::slice_from_raw_parts_mut(results, nresults); 280 for (slot, val) in results.iter_mut().zip(out.into_vec().into_iter()) { 281 crate::initialize(slot, wasmtime_val_t::from_val(val)); 282 } 283 None 284 } 285 Ok(Err(trap)) => match trap.downcast::<Trap>() { 286 Ok(trap) => { 287 *trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(trap))); 288 None 289 } 290 Err(err) => Some(Box::new(wasmtime_error_t::from(err))), 291 }, 292 Err(panic) => { 293 let trap = if let Some(msg) = panic.downcast_ref::<String>() { 294 Trap::new(msg) 295 } else if let Some(msg) = panic.downcast_ref::<&'static str>() { 296 Trap::new(*msg) 297 } else { 298 Trap::new("rust panic happened") 299 }; 300 *trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(trap))); 301 None 302 } 303 } 304 } 305 306 #[no_mangle] 307 pub extern "C" fn wasmtime_func_type( 308 store: CStoreContext<'_>, 309 func: &Func, 310 ) -> Box<wasm_functype_t> { 311 Box::new(wasm_functype_t::new(func.ty(store))) 312 } 313 314 #[no_mangle] 315 pub extern "C" fn wasmtime_caller_context<'a>( 316 caller: &'a mut wasmtime_caller_t, 317 ) -> CStoreContextMut<'a> { 318 caller.caller.as_context_mut() 319 } 320 321 #[no_mangle] 322 pub unsafe extern "C" fn wasmtime_caller_export_get( 323 caller: &mut wasmtime_caller_t, 324 name: *const u8, 325 name_len: usize, 326 item: &mut MaybeUninit<wasmtime_extern_t>, 327 ) -> bool { 328 let name = match str::from_utf8(crate::slice_from_raw_parts(name, name_len)) { 329 Ok(name) => name, 330 Err(_) => return false, 331 }; 332 let which = match caller.caller.get_export(name) { 333 Some(item) => item, 334 None => return false, 335 }; 336 crate::initialize(item, which.into()); 337 true 338 } 339