1 use crate::{wasm_engine_t, wasmtime_error_t, wasmtime_val_t, ForeignData}; 2 use std::cell::UnsafeCell; 3 use std::ffi::c_void; 4 use std::sync::Arc; 5 use wasmtime::{ 6 AsContext, AsContextMut, Store, StoreContext, StoreContextMut, StoreLimits, StoreLimitsBuilder, 7 UpdateDeadline, Val, 8 }; 9 10 /// This representation of a `Store` is used to implement the `wasm.h` API. 11 /// 12 /// This is stored alongside `Func` and such for `wasm.h` so each object is 13 /// independently owned. The usage of `Arc` here is mostly to just get it to be 14 /// safe to drop across multiple threads, but otherwise acquiring the `context` 15 /// values from this struct is considered unsafe due to it being unknown how the 16 /// aliasing is working on the C side of things. 17 /// 18 /// The aliasing requirements are documented in the C API `wasm.h` itself (at 19 /// least Wasmtime's implementation). 20 #[derive(Clone)] 21 pub struct StoreRef { 22 store: Arc<UnsafeCell<Store<()>>>, 23 } 24 25 impl StoreRef { 26 pub unsafe fn context(&self) -> StoreContext<'_, ()> { 27 (*self.store.get()).as_context() 28 } 29 30 pub unsafe fn context_mut(&mut self) -> StoreContextMut<'_, ()> { 31 (*self.store.get()).as_context_mut() 32 } 33 } 34 35 #[repr(C)] 36 #[derive(Clone)] 37 pub struct wasm_store_t { 38 pub(crate) store: StoreRef, 39 } 40 41 wasmtime_c_api_macros::declare_own!(wasm_store_t); 42 43 #[no_mangle] 44 pub extern "C" fn wasm_store_new(engine: &wasm_engine_t) -> Box<wasm_store_t> { 45 let engine = &engine.engine; 46 let store = Store::new(engine, ()); 47 Box::new(wasm_store_t { 48 store: StoreRef { 49 store: Arc::new(UnsafeCell::new(store)), 50 }, 51 }) 52 } 53 54 /// Representation of a `Store` for `wasmtime.h` This notably tries to move more 55 /// burden of aliasing on the caller rather than internally, allowing for a more 56 /// raw representation of contexts and such that requires less `unsafe` in the 57 /// implementation. 58 /// 59 /// Note that this notably carries `StoreData` as a payload which allows storing 60 /// foreign data and configuring WASI as well. 61 #[repr(C)] 62 pub struct wasmtime_store_t { 63 pub(crate) store: Store<StoreData>, 64 } 65 66 wasmtime_c_api_macros::declare_own!(wasmtime_store_t); 67 68 pub type CStoreContext<'a> = StoreContext<'a, StoreData>; 69 pub type CStoreContextMut<'a> = StoreContextMut<'a, StoreData>; 70 71 pub struct StoreData { 72 foreign: crate::ForeignData, 73 #[cfg(feature = "wasi")] 74 pub(crate) wasi: Option<wasi_common::WasiCtx>, 75 76 /// Temporary storage for usage during a wasm->host call to store values 77 /// in a slice we pass to the C API. 78 pub hostcall_val_storage: Vec<wasmtime_val_t>, 79 80 /// Temporary storage for usage during host->wasm calls, same as above but 81 /// for a different direction. 82 pub wasm_val_storage: Vec<Val>, 83 84 /// Limits for the store. 85 pub store_limits: StoreLimits, 86 } 87 88 #[no_mangle] 89 pub extern "C" fn wasmtime_store_new( 90 engine: &wasm_engine_t, 91 data: *mut c_void, 92 finalizer: Option<extern "C" fn(*mut c_void)>, 93 ) -> Box<wasmtime_store_t> { 94 Box::new(wasmtime_store_t { 95 store: Store::new( 96 &engine.engine, 97 StoreData { 98 foreign: ForeignData { data, finalizer }, 99 #[cfg(feature = "wasi")] 100 wasi: None, 101 hostcall_val_storage: Vec::new(), 102 wasm_val_storage: Vec::new(), 103 store_limits: StoreLimits::default(), 104 }, 105 ), 106 }) 107 } 108 109 pub type wasmtime_update_deadline_kind_t = u8; 110 pub const WASMTIME_UPDATE_DEADLINE_CONTINUE: wasmtime_update_deadline_kind_t = 0; 111 pub const WASMTIME_UPDATE_DEADLINE_YIELD: wasmtime_update_deadline_kind_t = 1; 112 113 #[no_mangle] 114 pub extern "C" fn wasmtime_store_epoch_deadline_callback( 115 store: &mut wasmtime_store_t, 116 func: extern "C" fn( 117 CStoreContextMut<'_>, 118 *mut c_void, 119 *mut u64, 120 *mut wasmtime_update_deadline_kind_t, 121 ) -> Option<Box<wasmtime_error_t>>, 122 data: *mut c_void, 123 finalizer: Option<extern "C" fn(*mut c_void)>, 124 ) { 125 let foreign = crate::ForeignData { data, finalizer }; 126 store.store.epoch_deadline_callback(move |mut store_ctx| { 127 let _ = &foreign; // Move foreign into this closure 128 let mut delta: u64 = 0; 129 let mut kind = WASMTIME_UPDATE_DEADLINE_CONTINUE; 130 let result = (func)( 131 store_ctx.as_context_mut(), 132 foreign.data, 133 &mut delta as *mut u64, 134 &mut kind as *mut wasmtime_update_deadline_kind_t, 135 ); 136 match result { 137 Some(err) => Err(wasmtime::Error::from(<wasmtime_error_t as Into< 138 anyhow::Error, 139 >>::into(*err))), 140 None if kind == WASMTIME_UPDATE_DEADLINE_CONTINUE => { 141 Ok(UpdateDeadline::Continue(delta)) 142 } 143 #[cfg(feature = "async")] 144 None if kind == WASMTIME_UPDATE_DEADLINE_YIELD => Ok(UpdateDeadline::Yield(delta)), 145 _ => panic!("unknown wasmtime_update_deadline_kind_t: {}", kind), 146 } 147 }); 148 } 149 150 #[no_mangle] 151 pub extern "C" fn wasmtime_store_context(store: &mut wasmtime_store_t) -> CStoreContextMut<'_> { 152 store.store.as_context_mut() 153 } 154 155 #[no_mangle] 156 pub extern "C" fn wasmtime_store_limiter( 157 store: &mut wasmtime_store_t, 158 memory_size: i64, 159 table_elements: i64, 160 instances: i64, 161 tables: i64, 162 memories: i64, 163 ) { 164 let mut limiter = StoreLimitsBuilder::new(); 165 if memory_size >= 0 { 166 limiter = limiter.memory_size(memory_size as usize); 167 } 168 if table_elements >= 0 { 169 limiter = limiter.table_elements(table_elements as u32); 170 } 171 if instances >= 0 { 172 limiter = limiter.instances(instances as usize); 173 } 174 if tables >= 0 { 175 limiter = limiter.tables(tables as usize); 176 } 177 if memories >= 0 { 178 limiter = limiter.memories(memories as usize); 179 } 180 store.store.data_mut().store_limits = limiter.build(); 181 store.store.limiter(|data| &mut data.store_limits); 182 } 183 184 #[no_mangle] 185 pub extern "C" fn wasmtime_context_get_data(store: CStoreContext<'_>) -> *mut c_void { 186 store.data().foreign.data 187 } 188 189 #[no_mangle] 190 pub extern "C" fn wasmtime_context_set_data(mut store: CStoreContextMut<'_>, data: *mut c_void) { 191 store.data_mut().foreign.data = data; 192 } 193 194 #[cfg(feature = "wasi")] 195 #[no_mangle] 196 pub extern "C" fn wasmtime_context_set_wasi( 197 mut context: CStoreContextMut<'_>, 198 wasi: Box<crate::wasi_config_t>, 199 ) -> Option<Box<wasmtime_error_t>> { 200 crate::handle_result(wasi.into_wasi_ctx(), |wasi| { 201 context.data_mut().wasi = Some(wasi); 202 }) 203 } 204 205 #[no_mangle] 206 pub extern "C" fn wasmtime_context_gc(mut context: CStoreContextMut<'_>) { 207 context.gc(); 208 } 209 210 #[no_mangle] 211 pub extern "C" fn wasmtime_context_set_fuel( 212 mut store: CStoreContextMut<'_>, 213 fuel: u64, 214 ) -> Option<Box<wasmtime_error_t>> { 215 crate::handle_result(store.set_fuel(fuel), |()| {}) 216 } 217 218 #[no_mangle] 219 pub extern "C" fn wasmtime_context_get_fuel( 220 store: CStoreContext<'_>, 221 fuel: &mut u64, 222 ) -> Option<Box<wasmtime_error_t>> { 223 crate::handle_result(store.get_fuel(), |amt| { 224 *fuel = amt; 225 }) 226 } 227 228 #[no_mangle] 229 pub extern "C" fn wasmtime_context_set_epoch_deadline( 230 mut store: CStoreContextMut<'_>, 231 ticks_beyond_current: u64, 232 ) { 233 store.set_epoch_deadline(ticks_beyond_current); 234 } 235