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