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