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::{Error, Result}; 7 use std::any::Any; 8 use std::ffi::c_void; 9 use std::mem::{self, MaybeUninit}; 10 use std::panic::{self, AssertUnwindSafe}; 11 use std::ptr; 12 use std::str; 13 use wasmtime::{AsContextMut, Caller, Extern, Func, Trap, Val, ValRaw}; 14 15 #[derive(Clone)] 16 #[repr(transparent)] 17 pub struct wasm_func_t { 18 ext: wasm_extern_t, 19 } 20 21 wasmtime_c_api_macros::declare_ref!(wasm_func_t); 22 23 pub type wasm_func_callback_t = extern "C" fn( 24 args: *const wasm_val_vec_t, 25 results: *mut wasm_val_vec_t, 26 ) -> Option<Box<wasm_trap_t>>; 27 28 pub type wasm_func_callback_with_env_t = extern "C" fn( 29 env: *mut std::ffi::c_void, 30 args: *const wasm_val_vec_t, 31 results: *mut wasm_val_vec_t, 32 ) -> Option<Box<wasm_trap_t>>; 33 34 impl wasm_func_t { 35 pub(crate) fn try_from(e: &wasm_extern_t) -> Option<&wasm_func_t> { 36 match &e.which { 37 Extern::Func(_) => Some(unsafe { &*(e as *const _ as *const _) }), 38 _ => None, 39 } 40 } 41 42 pub(crate) fn func(&self) -> Func { 43 match self.ext.which { 44 Extern::Func(f) => f, 45 _ => unsafe { std::hint::unreachable_unchecked() }, 46 } 47 } 48 } 49 50 unsafe fn create_function( 51 store: &mut wasm_store_t, 52 ty: &wasm_functype_t, 53 func: impl Fn(*const wasm_val_vec_t, *mut wasm_val_vec_t) -> Option<Box<wasm_trap_t>> 54 + Send 55 + Sync 56 + 'static, 57 ) -> Box<wasm_func_t> { 58 let ty = ty.ty().ty(store.store.context().engine()); 59 let func = Func::new( 60 store.store.context_mut(), 61 ty, 62 move |_caller, params, results| { 63 let params: wasm_val_vec_t = params 64 .iter() 65 .cloned() 66 .map(|p| wasm_val_t::from_val(p)) 67 .collect::<Vec<_>>() 68 .into(); 69 let mut out_results: wasm_val_vec_t = vec![wasm_val_t::default(); results.len()].into(); 70 let out = func(¶ms, &mut out_results); 71 if let Some(trap) = out { 72 return Err(trap.error); 73 } 74 75 let out_results = out_results.as_slice(); 76 for i in 0..results.len() { 77 results[i] = out_results[i].val(); 78 } 79 Ok(()) 80 }, 81 ); 82 Box::new(wasm_func_t { 83 ext: wasm_extern_t { 84 store: store.store.clone(), 85 which: func.into(), 86 }, 87 }) 88 } 89 90 #[no_mangle] 91 pub unsafe extern "C" fn wasm_func_new( 92 store: &mut wasm_store_t, 93 ty: &wasm_functype_t, 94 callback: wasm_func_callback_t, 95 ) -> Box<wasm_func_t> { 96 create_function(store, ty, move |params, results| callback(params, results)) 97 } 98 99 #[no_mangle] 100 pub unsafe extern "C" fn wasm_func_new_with_env( 101 store: &mut wasm_store_t, 102 ty: &wasm_functype_t, 103 callback: wasm_func_callback_with_env_t, 104 data: *mut c_void, 105 finalizer: Option<extern "C" fn(arg1: *mut std::ffi::c_void)>, 106 ) -> Box<wasm_func_t> { 107 let finalizer = crate::ForeignData { data, finalizer }; 108 create_function(store, ty, move |params, results| { 109 let _ = &finalizer; // move entire finalizer into this closure 110 callback(finalizer.data, params, results) 111 }) 112 } 113 114 /// Places the `args` into `dst` and additionally reserves space in `dst` for `results_size` 115 /// returns. The params/results slices are then returned separately. 116 pub(crate) fn translate_args<'a>( 117 dst: &'a mut Vec<Val>, 118 args: impl ExactSizeIterator<Item = Val>, 119 results_size: usize, 120 ) -> (&'a [Val], &'a mut [Val]) { 121 debug_assert!(dst.is_empty()); 122 let num_args = args.len(); 123 dst.reserve(args.len() + results_size); 124 dst.extend(args); 125 dst.extend((0..results_size).map(|_| Val::null_func_ref())); 126 let (a, b) = dst.split_at_mut(num_args); 127 (a, b) 128 } 129 130 #[no_mangle] 131 pub unsafe extern "C" fn wasm_func_call( 132 func: &mut wasm_func_t, 133 args: *const wasm_val_vec_t, 134 results: *mut wasm_val_vec_t, 135 ) -> *mut wasm_trap_t { 136 let f = func.func(); 137 let results = (*results).as_uninit_slice(); 138 let args = (*args).as_slice(); 139 let mut dst = Vec::new(); 140 let (wt_params, wt_results) = 141 translate_args(&mut dst, args.iter().map(|i| i.val()), results.len()); 142 143 // We're calling arbitrary code here most of the time, and we in general 144 // want to try to insulate callers against bugs in wasmtime/wasi/etc if we 145 // can. As a result we catch panics here and transform them to traps to 146 // allow the caller to have any insulation possible against Rust panics. 147 let result = panic::catch_unwind(AssertUnwindSafe(|| { 148 f.call(func.ext.store.context_mut(), wt_params, wt_results) 149 })); 150 match result { 151 Ok(Ok(())) => { 152 for (slot, val) in results.iter_mut().zip(wt_results.iter().cloned()) { 153 crate::initialize(slot, wasm_val_t::from_val(val)); 154 } 155 ptr::null_mut() 156 } 157 Ok(Err(err)) => Box::into_raw(Box::new(wasm_trap_t::new(err))), 158 Err(panic) => { 159 let err = error_from_panic(panic); 160 let trap = Box::new(wasm_trap_t::new(err)); 161 Box::into_raw(trap) 162 } 163 } 164 } 165 166 fn error_from_panic(panic: Box<dyn Any + Send>) -> Error { 167 if let Some(msg) = panic.downcast_ref::<String>() { 168 Error::msg(msg.clone()) 169 } else if let Some(msg) = panic.downcast_ref::<&'static str>() { 170 Error::msg(*msg) 171 } else { 172 Error::msg("rust panic happened") 173 } 174 } 175 176 #[no_mangle] 177 pub unsafe extern "C" fn wasm_func_type(f: &wasm_func_t) -> Box<wasm_functype_t> { 178 Box::new(wasm_functype_t::new(f.func().ty(f.ext.store.context()))) 179 } 180 181 #[no_mangle] 182 pub unsafe extern "C" fn wasm_func_param_arity(f: &wasm_func_t) -> usize { 183 f.func().ty(f.ext.store.context()).params().len() 184 } 185 186 #[no_mangle] 187 pub unsafe extern "C" fn wasm_func_result_arity(f: &wasm_func_t) -> usize { 188 f.func().ty(f.ext.store.context()).results().len() 189 } 190 191 #[no_mangle] 192 pub extern "C" fn wasm_func_as_extern(f: &mut wasm_func_t) -> &mut wasm_extern_t { 193 &mut (*f).ext 194 } 195 196 #[no_mangle] 197 pub extern "C" fn wasm_func_as_extern_const(f: &wasm_func_t) -> &wasm_extern_t { 198 &(*f).ext 199 } 200 201 #[repr(C)] 202 pub struct wasmtime_caller_t<'a> { 203 pub(crate) caller: Caller<'a, crate::StoreData>, 204 } 205 206 pub type wasmtime_func_callback_t = extern "C" fn( 207 *mut c_void, 208 *mut wasmtime_caller_t, 209 *const wasmtime_val_t, 210 usize, 211 *mut wasmtime_val_t, 212 usize, 213 ) -> Option<Box<wasm_trap_t>>; 214 215 pub type wasmtime_func_unchecked_callback_t = extern "C" fn( 216 *mut c_void, 217 *mut wasmtime_caller_t, 218 *mut ValRaw, 219 usize, 220 ) -> Option<Box<wasm_trap_t>>; 221 222 #[no_mangle] 223 pub unsafe extern "C" fn wasmtime_func_new( 224 store: CStoreContextMut<'_>, 225 ty: &wasm_functype_t, 226 callback: wasmtime_func_callback_t, 227 data: *mut c_void, 228 finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>, 229 func: &mut Func, 230 ) { 231 let ty = ty.ty().ty(store.engine()); 232 let cb = c_callback_to_rust_fn(callback, data, finalizer); 233 let f = Func::new(store, ty, cb); 234 *func = f; 235 } 236 237 pub(crate) unsafe fn c_callback_to_rust_fn( 238 callback: wasmtime_func_callback_t, 239 data: *mut c_void, 240 finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>, 241 ) -> impl Fn(Caller<'_, crate::StoreData>, &[Val], &mut [Val]) -> Result<()> { 242 let foreign = crate::ForeignData { data, finalizer }; 243 move |mut caller, params, results| { 244 let _ = &foreign; // move entire foreign into this closure 245 246 // Convert `params/results` to `wasmtime_val_t`. Use the previous 247 // storage in `hostcall_val_storage` to help avoid allocations all the 248 // time. 249 let mut vals = mem::take(&mut caller.data_mut().hostcall_val_storage); 250 debug_assert!(vals.is_empty()); 251 vals.reserve(params.len() + results.len()); 252 vals.extend( 253 params 254 .iter() 255 .cloned() 256 .map(|p| wasmtime_val_t::from_val(&mut caller, p)), 257 ); 258 vals.extend((0..results.len()).map(|_| wasmtime_val_t { 259 kind: crate::WASMTIME_I32, 260 of: wasmtime_val_union { i32: 0 }, 261 })); 262 let (params, out_results) = vals.split_at_mut(params.len()); 263 264 // Invoke the C function pointer, getting the results. 265 let mut caller = wasmtime_caller_t { caller }; 266 let out = callback( 267 foreign.data, 268 &mut caller, 269 params.as_ptr(), 270 params.len(), 271 out_results.as_mut_ptr(), 272 out_results.len(), 273 ); 274 if let Some(trap) = out { 275 return Err(trap.error); 276 } 277 278 // Translate the `wasmtime_val_t` results into the `results` space 279 for (i, result) in out_results.iter().enumerate() { 280 results[i] = result.to_val(&mut caller.caller); 281 } 282 283 // Move our `vals` storage back into the store now that we no longer 284 // need it. This'll get picked up by the next hostcall and reuse our 285 // same storage. 286 vals.truncate(0); 287 caller.caller.data_mut().hostcall_val_storage = vals; 288 Ok(()) 289 } 290 } 291 292 #[no_mangle] 293 pub unsafe extern "C" fn wasmtime_func_new_unchecked( 294 store: CStoreContextMut<'_>, 295 ty: &wasm_functype_t, 296 callback: wasmtime_func_unchecked_callback_t, 297 data: *mut c_void, 298 finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>, 299 func: &mut Func, 300 ) { 301 let ty = ty.ty().ty(store.engine()); 302 let cb = c_unchecked_callback_to_rust_fn(callback, data, finalizer); 303 *func = Func::new_unchecked(store, ty, cb); 304 } 305 306 pub(crate) unsafe fn c_unchecked_callback_to_rust_fn( 307 callback: wasmtime_func_unchecked_callback_t, 308 data: *mut c_void, 309 finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>, 310 ) -> impl Fn(Caller<'_, crate::StoreData>, &mut [ValRaw]) -> Result<()> { 311 let foreign = crate::ForeignData { data, finalizer }; 312 move |caller, values| { 313 let _ = &foreign; // move entire foreign into this closure 314 let mut caller = wasmtime_caller_t { caller }; 315 match callback(foreign.data, &mut caller, values.as_mut_ptr(), values.len()) { 316 None => Ok(()), 317 Some(trap) => Err(trap.error), 318 } 319 } 320 } 321 322 #[no_mangle] 323 pub unsafe extern "C" fn wasmtime_func_call( 324 mut store: CStoreContextMut<'_>, 325 func: &Func, 326 args: *const wasmtime_val_t, 327 nargs: usize, 328 results: *mut MaybeUninit<wasmtime_val_t>, 329 nresults: usize, 330 trap_ret: &mut *mut wasm_trap_t, 331 ) -> Option<Box<wasmtime_error_t>> { 332 let mut store = store.as_context_mut(); 333 let mut params = mem::take(&mut store.data_mut().wasm_val_storage); 334 let (wt_params, wt_results) = translate_args( 335 &mut params, 336 crate::slice_from_raw_parts(args, nargs) 337 .iter() 338 .map(|i| i.to_val(&mut store)), 339 nresults, 340 ); 341 342 // We're calling arbitrary code here most of the time, and we in general 343 // want to try to insulate callers against bugs in wasmtime/wasi/etc if we 344 // can. As a result we catch panics here and transform them to traps to 345 // allow the caller to have any insulation possible against Rust panics. 346 let result = panic::catch_unwind(AssertUnwindSafe(|| { 347 func.call(&mut store, wt_params, wt_results) 348 })); 349 match result { 350 Ok(Ok(())) => { 351 let results = crate::slice_from_raw_parts_mut(results, nresults); 352 for (slot, val) in results.iter_mut().zip(wt_results.iter()) { 353 crate::initialize(slot, wasmtime_val_t::from_val(&mut store, val.clone())); 354 } 355 params.truncate(0); 356 store.data_mut().wasm_val_storage = params; 357 None 358 } 359 Ok(Err(trap)) => store_err(trap, trap_ret), 360 Err(panic) => { 361 let err = error_from_panic(panic); 362 *trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(err))); 363 None 364 } 365 } 366 } 367 368 #[no_mangle] 369 pub unsafe extern "C" fn wasmtime_func_call_unchecked( 370 store: CStoreContextMut<'_>, 371 func: &Func, 372 args_and_results: *mut ValRaw, 373 args_and_results_len: usize, 374 trap_ret: &mut *mut wasm_trap_t, 375 ) -> Option<Box<wasmtime_error_t>> { 376 match func.call_unchecked(store, args_and_results, args_and_results_len) { 377 Ok(()) => None, 378 Err(trap) => store_err(trap, trap_ret), 379 } 380 } 381 382 fn store_err(err: Error, trap_ret: &mut *mut wasm_trap_t) -> Option<Box<wasmtime_error_t>> { 383 if err.is::<Trap>() { 384 *trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(err))); 385 None 386 } else { 387 Some(Box::new(wasmtime_error_t::from(err))) 388 } 389 } 390 391 #[no_mangle] 392 pub extern "C" fn wasmtime_func_type( 393 store: CStoreContext<'_>, 394 func: &Func, 395 ) -> Box<wasm_functype_t> { 396 Box::new(wasm_functype_t::new(func.ty(store))) 397 } 398 399 #[no_mangle] 400 pub extern "C" fn wasmtime_caller_context<'a>( 401 caller: &'a mut wasmtime_caller_t, 402 ) -> CStoreContextMut<'a> { 403 caller.caller.as_context_mut() 404 } 405 406 #[no_mangle] 407 pub unsafe extern "C" fn wasmtime_caller_export_get( 408 caller: &mut wasmtime_caller_t, 409 name: *const u8, 410 name_len: usize, 411 item: &mut MaybeUninit<wasmtime_extern_t>, 412 ) -> bool { 413 let name = match str::from_utf8(crate::slice_from_raw_parts(name, name_len)) { 414 Ok(name) => name, 415 Err(_) => return false, 416 }; 417 let which = match caller.caller.get_export(name) { 418 Some(item) => item, 419 None => return false, 420 }; 421 crate::initialize(item, which.into()); 422 true 423 } 424 425 #[no_mangle] 426 pub unsafe extern "C" fn wasmtime_func_from_raw( 427 store: CStoreContextMut<'_>, 428 raw: *mut c_void, 429 func: &mut Func, 430 ) { 431 *func = Func::from_raw(store, raw).unwrap(); 432 } 433 434 #[no_mangle] 435 pub unsafe extern "C" fn wasmtime_func_to_raw( 436 store: CStoreContextMut<'_>, 437 func: &Func, 438 ) -> *mut c_void { 439 func.to_raw(store) 440 } 441