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