1use anyhow::{Context, Result, bail};
2use std::{
3 collections::{HashMap, VecDeque},
4 fmt::Display,
5 mem::size_of,
6 num::NonZeroI32,
7 ptr, slice,
8 sync::{Arc, Mutex},
9};
10
11use wasmer_api::{
12 Extern, ExternType, Function, Function as WasmerFunction, FunctionEnv, FunctionEnvMut,
13 FunctionType, Global, GlobalType, Imports, Instance, Memory, Memory32, MemoryType, Module,
14 Mutability, Pages, RuntimeError, StoreMut, Table, Type, TypedFunction, Value, WasmPtr,
15 namespace,
16};
17
18pub const WASM_C_API_MODULE_NAME: &str = "wasm_c_api_v0";
20const WASM_C_API_MODULE_PREFIX: &str = "wasm_c_api_v";
21
22const INVALID_HANDLE: i32 = 0;
23const BOOL_FALSE: i32 = 0;
24const BOOL_TRUE: i32 = 1;
25const INVALID_KIND: i32 = -1;
26const INVALID_SIZE: i32 = -1;
27const WASM_VEC_SIZE: usize = 8;
28const WASM_VEC_DATA_OFFSET: i32 = 4;
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum WasmCAPIVersion {
33 V0,
35 Unknown,
37}
38
39impl Display for WasmCAPIVersion {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 match self {
42 WasmCAPIVersion::V0 => write!(f, "wasm_c_api_v0"),
43 WasmCAPIVersion::Unknown => write!(f, "wasm_c_api_unknown"),
44 }
45 }
46}
47
48impl WasmCAPIVersion {
49 pub const fn is_compatible_with(self, other: Self) -> bool {
51 matches!((self, other), (Self::V0, Self::V0))
52 }
53}
54
55pub fn module_wasm_c_api_version_used(module: &Module) -> Option<WasmCAPIVersion> {
57 let mut version = None;
58
59 for import in module.imports() {
60 let Some(detected_version) = wasm_c_api_version_from_namespace(import.module()) else {
61 continue;
62 };
63
64 version = Some(match version {
65 None => detected_version,
66 Some(existing) if existing == detected_version => existing,
67 Some(_) => WasmCAPIVersion::Unknown,
68 });
69 }
70
71 version
72}
73
74fn wasm_c_api_version_from_namespace(namespace: &str) -> Option<WasmCAPIVersion> {
75 if namespace == WASM_C_API_MODULE_NAME {
76 return Some(WasmCAPIVersion::V0);
77 }
78
79 let suffix = namespace.strip_prefix(WASM_C_API_MODULE_PREFIX)?;
80 Some(match suffix {
81 "0" => WasmCAPIVersion::V0,
82 _ => WasmCAPIVersion::Unknown,
83 })
84}
85
86type ResolveModuleSync = Arc<dyn Fn(Vec<u8>) -> Result<Module> + Send + Sync>;
87
88struct WasmCapiSession {
89 version: Option<WasmCAPIVersion>,
90 imported_memory_type: Option<MemoryType>,
91 imported_table_type: Option<wasmer_api::TableType>,
92 resolve_module_sync: Option<ResolveModuleSync>,
93 func_env: Mutex<Option<FunctionEnv<WasmCapiEnv>>>,
94}
95
96impl WasmCapiSession {
97 fn new(module: &Module, resolve_module_sync: Option<ResolveModuleSync>) -> Self {
98 let imported_memory_type = module.imports().find_map(|import| {
99 if import.module() == "env"
100 && import.name() == "memory"
101 && let ExternType::Memory(ty) = import.ty()
102 {
103 return Some(*ty);
104 }
105 None
106 });
107
108 let imported_table_type = module.imports().find_map(|import| {
109 if import.module() == "env"
110 && import.name() == "__indirect_function_table"
111 && let ExternType::Table(ty) = import.ty()
112 {
113 return Some(*ty);
114 }
115 None
116 });
117
118 Self {
119 version: module_wasm_c_api_version_used(module),
120 imported_memory_type,
121 imported_table_type,
122 resolve_module_sync,
123 func_env: Mutex::new(None),
124 }
125 }
126
127 fn needs_imports(&self) -> bool {
128 self.version.is_some()
129 }
130
131 fn validate_supported(&self) -> Result<()> {
132 if let Some(version) = self.version
133 && !WasmCAPIVersion::V0.is_compatible_with(version)
134 {
135 bail!("unsupported Wasm C API import version: {version:?}");
136 }
137 Ok(())
138 }
139
140 fn register_imports(&self, store: &mut StoreMut<'_>, io: &mut Imports) -> Result<()> {
141 self.validate_supported()?;
142 if self.version.is_none() {
143 return Ok(());
144 }
145
146 let func_env = FunctionEnv::new(
147 &mut *store,
148 WasmCapiEnv {
149 resolve_module_sync: self.resolve_module_sync.clone(),
150 ..WasmCapiEnv::default()
151 },
152 );
153 *self
154 .func_env
155 .lock()
156 .expect("poisoned WasmCapiSession mutex") = Some(func_env.clone());
157 register_wasm_c_api_imports(store, &func_env, io);
158
159 if let Some(memory_type) = self.imported_memory_type {
160 if let Some(existing) = io.get_export("env", "memory") {
161 let Extern::Memory(memory) = existing else {
162 bail!("env.memory import for Wasm C API module is not a memory");
163 };
164 self.set_memory(store, &memory)?;
165 } else {
166 let memory = Memory::new(&mut *store, memory_type)?;
167 io.define("env", "memory", memory.clone());
168 self.set_memory(store, &memory)?;
169 }
170 }
171
172 if let Some(table_type) = self.imported_table_type {
173 if let Some(existing) = io.get_export("env", "__indirect_function_table") {
174 let Extern::Table(table) = existing else {
175 bail!(
176 "env.__indirect_function_table import for Wasm C API module is not a table"
177 );
178 };
179 self.set_table(store, &table)?;
180 } else {
181 let table = Table::new(&mut *store, table_type, Value::FuncRef(None))?;
182 io.define("env", "__indirect_function_table", table.clone());
183 self.set_table(store, &table)?;
184 }
185 }
186
187 Ok(())
188 }
189
190 fn set_memory(&self, store: &mut StoreMut<'_>, memory: &Memory) -> Result<()> {
191 let Some(func_env) = self.func_env()? else {
192 return Ok(());
193 };
194 func_env.as_mut(&mut *store).memory = Some(memory.clone());
195 Ok(())
196 }
197
198 fn set_table(&self, store: &mut StoreMut<'_>, table: &Table) -> Result<()> {
199 let Some(func_env) = self.func_env()? else {
200 return Ok(());
201 };
202 func_env.as_mut(&mut *store).table = Some(table.clone());
203 Ok(())
204 }
205
206 fn configure_instance(
207 &self,
208 store: &mut StoreMut<'_>,
209 instance: &Instance,
210 imported_memory: Option<&Memory>,
211 ) -> Result<()> {
212 let Some(func_env) = self.func_env()? else {
213 return Ok(());
214 };
215
216 if let Some(memory) = imported_memory {
217 func_env.as_mut(&mut *store).memory = Some(memory.clone());
218 } else if let Ok(memory) = instance.exports.get_memory("memory") {
219 func_env.as_mut(&mut *store).memory = Some(memory.clone());
220 }
221
222 for export_name in ["unofficial_napi_guest_malloc", "malloc"] {
223 if let Ok(malloc) = instance
224 .exports
225 .get_typed_function::<i32, i32>(&store, export_name)
226 {
227 func_env.as_mut(&mut *store).malloc_fn = Some(malloc);
228 break;
229 }
230 }
231
232 for export_name in ["unofficial_napi_guest_free", "free"] {
233 if let Ok(free) = instance
234 .exports
235 .get_typed_function::<i32, ()>(&store, export_name)
236 {
237 func_env.as_mut(&mut *store).free_fn = Some(free);
238 break;
239 }
240 }
241
242 if let Ok(table) = instance.exports.get_table("__indirect_function_table") {
243 func_env.as_mut(&mut *store).table = Some(table.clone());
244 }
245
246 Ok(())
247 }
248
249 fn func_env(&self) -> Result<Option<FunctionEnv<WasmCapiEnv>>> {
250 if self.version.is_none() {
251 return Ok(None);
252 }
253
254 self.func_env
255 .lock()
256 .expect("poisoned WasmCapiSession mutex")
257 .clone()
258 .context("missing Wasm C API function env during instance setup")
259 .map(Some)
260 }
261}
262
263#[derive(Clone, Debug, Eq, Hash, PartialEq)]
264struct ModuleKey(String);
265
266impl ModuleKey {
267 fn new(module: &Module) -> Self {
268 Self(module.info().id.id())
269 }
270}
271
272#[derive(Clone, Default)]
274pub struct WasmCapiRuntimeHooks {
275 sessions: Arc<Mutex<HashMap<ModuleKey, VecDeque<WasmCapiSession>>>>,
282 resolve_module_sync: Option<ResolveModuleSync>,
283}
284
285impl WasmCapiRuntimeHooks {
286 pub fn new() -> Self {
288 Self::default()
289 }
290
291 pub fn with_resolve_module_sync(
293 mut self,
294 resolve: impl Fn(Vec<u8>) -> Result<Module> + Send + Sync + 'static,
295 ) -> Self {
296 self.resolve_module_sync = Some(Arc::new(resolve));
297 self
298 }
299
300 fn module_key(module: &Module) -> ModuleKey {
301 ModuleKey::new(module)
302 }
303
304 pub fn additional_imports(&self, module: &Module, store: &mut StoreMut<'_>) -> Result<Imports> {
306 let mut imports = Imports::new();
307 self.add_imports(module, store, &mut imports)?;
308 Ok(imports)
309 }
310
311 pub fn add_imports(
313 &self,
314 module: &Module,
315 store: &mut StoreMut<'_>,
316 imports: &mut Imports,
317 ) -> Result<()> {
318 let session = WasmCapiSession::new(module, self.resolve_module_sync.clone());
319 if !session.needs_imports() {
320 return Ok(());
321 }
322
323 session.register_imports(store, imports)?;
324 let mut sessions = self
325 .sessions
326 .lock()
327 .expect("poisoned WasmCapiRuntimeHooks session queue");
328 sessions
329 .entry(Self::module_key(module))
330 .or_default()
331 .push_back(session);
332 Ok(())
333 }
334
335 pub fn configure_instance(
337 &self,
338 module: &Module,
339 store: &mut StoreMut<'_>,
340 instance: &Instance,
341 imported_memory: Option<&Memory>,
342 ) -> Result<()> {
343 if module_wasm_c_api_version_used(module).is_none() {
344 return Ok(());
345 }
346
347 let session = {
348 let mut sessions = self
349 .sessions
350 .lock()
351 .expect("poisoned WasmCapiRuntimeHooks session queue");
352 let key = Self::module_key(module);
353 let Some(queue) = sessions.get_mut(&key) else {
354 bail!("missing pending Wasm C API session for module instance setup");
355 };
356 let session = queue
357 .pop_front()
358 .context("missing queued Wasm C API session for module instance setup")?;
359 if queue.is_empty() {
360 sessions.remove(&key);
361 }
362 session
363 };
364
365 session.configure_instance(store, instance, imported_memory)
366 }
367}
368
369#[derive(Default)]
374struct WasmCapiEnv {
375 resolve_module_sync: Option<ResolveModuleSync>,
377 memory: Option<Memory>,
379 malloc_fn: Option<TypedFunction<i32, i32>>,
381 free_fn: Option<TypedFunction<i32, ()>>,
383 table: Option<Table>,
385 state: WasmCapiState,
387 func_env: Option<FunctionEnv<WasmCapiEnv>>,
389}
390
391const WASM_I32: u8 = 0;
394const WASM_I64: u8 = 1;
395const WASM_F32: u8 = 2;
396const WASM_F64: u8 = 3;
397const WASM_EXTERNREF: u8 = 128;
398const WASM_FUNCREF: u8 = 129;
399
400const WASM_EXTERN_FUNC: i32 = 0;
401const WASM_EXTERN_GLOBAL: i32 = 1;
402const WASM_EXTERN_TABLE: i32 = 2;
403const WASM_EXTERN_MEMORY: i32 = 3;
404
405const WASM_CONST: i32 = 0;
406const WASM_VAR: i32 = 1;
407
408const WASM_VAL_SIZE: usize = 16;
411const WASM_VAL_PAYLOAD_OFFSET: i32 = 8;
412
413#[derive(Clone)]
414enum WasmExtern {
415 Func(Function),
416 Global(Global),
417 Table(Table),
418 Memory(Memory),
419}
420
421#[derive(Clone)]
422enum WasmObject {
423 Engine,
424 Store,
425 Module(Module),
426 Instance(Instance),
427 Func(Function),
428 FuncType(FunctionType),
429 ValType(Type),
430 ExternType(ExternType),
431 ImportType {
432 module: String,
433 name: String,
434 ty: ExternType,
435 },
436 ExportType {
437 name: String,
438 ty: ExternType,
439 },
440 Extern(WasmExtern),
441 Memory(Memory),
442 MemoryType(MemoryType),
443 Global(Global),
444 GlobalType(GlobalType),
445 Table(Table),
446 Trap(String),
447}
448
449#[derive(Clone, Copy)]
451struct MemoryShadow {
452 guest_ptr: i32,
454 len: usize,
456}
457
458struct WasmCapiState {
464 next_handle: i32,
466 objects: HashMap<i32, WasmObject>,
468 memory_shadows: HashMap<i32, MemoryShadow>,
470}
471
472impl Default for WasmCapiState {
473 fn default() -> Self {
474 Self {
475 next_handle: 1,
476 objects: HashMap::new(),
477 memory_shadows: HashMap::new(),
478 }
479 }
480}
481
482impl WasmCapiState {
483 fn insert(&mut self, object: WasmObject) -> i32 {
484 let handle = self.next_handle;
485 if handle <= INVALID_HANDLE {
486 return INVALID_HANDLE;
487 }
488
489 self.next_handle = handle.checked_add(1).unwrap_or(INVALID_HANDLE);
490 self.objects.insert(handle, object);
491 handle
492 }
493
494 fn get(&self, handle: i32) -> Option<&WasmObject> {
495 if handle <= INVALID_HANDLE {
496 return None;
497 }
498 self.objects.get(&handle)
499 }
500
501 fn remove(&mut self, handle: i32) -> Option<MemoryShadow> {
502 if handle > INVALID_HANDLE {
503 self.objects.remove(&handle);
504 return self.memory_shadows.remove(&handle);
505 }
506 None
507 }
508}
509
510fn guest_memory_offset(guest_ptr: i32) -> Option<u64> {
511 u64::try_from(guest_ptr).ok()
512}
513
514fn guest_byte_ptr(guest_ptr: i32) -> Option<WasmPtr<u8, Memory32>> {
515 Some(WasmPtr::new(u32::try_from(guest_ptr).ok()?))
516}
517
518fn checked_memory_offset(guest_ptr: i32, len: usize, data_size: u64) -> Option<usize> {
519 let offset = usize::try_from(guest_ptr).ok()?;
520 let end = offset.checked_add(len)?;
521 (u64::try_from(end).ok()? <= data_size).then_some(offset)
522}
523
524fn non_null_guest_ptr(guest_ptr: i32) -> Option<NonZeroI32> {
525 let ptr = NonZeroI32::new(guest_ptr)?;
526 (ptr.get() > INVALID_HANDLE).then_some(ptr)
527}
528
529fn write_guest_bytes(env: &mut FunctionEnvMut<WasmCapiEnv>, guest_ptr: i32, data: &[u8]) -> bool {
530 let Some(ptr) = guest_byte_ptr(guest_ptr) else {
531 return false;
532 };
533 let Ok(len) = u32::try_from(data.len()) else {
534 return false;
535 };
536 let (state, store) = env.data_and_store_mut();
537 let Some(memory) = state.memory.clone() else {
538 return false;
539 };
540 let view = memory.view(&store);
541 ptr.slice(&view, len)
542 .and_then(|slice| slice.write_slice(data))
543 .is_ok()
544}
545
546fn write_guest_u32(env: &mut FunctionEnvMut<WasmCapiEnv>, guest_ptr: i32, val: u32) -> bool {
547 write_guest_bytes(env, guest_ptr, &val.to_le_bytes())
548}
549
550fn write_guest_i32(env: &mut FunctionEnvMut<WasmCapiEnv>, guest_ptr: i32, val: i32) -> bool {
551 write_guest_bytes(env, guest_ptr, &val.to_le_bytes())
552}
553
554fn write_guest_i32_offset(
555 env: &mut FunctionEnvMut<WasmCapiEnv>,
556 guest_ptr: i32,
557 offset: i32,
558 val: i32,
559) -> bool {
560 let Some(ptr) = guest_ptr.checked_add(offset) else {
561 return false;
562 };
563 write_guest_i32(env, ptr, val)
564}
565
566fn write_guest_bytes_offset(
567 env: &mut FunctionEnvMut<WasmCapiEnv>,
568 guest_ptr: i32,
569 offset: i32,
570 data: &[u8],
571) -> bool {
572 let Some(ptr) = guest_ptr.checked_add(offset) else {
573 return false;
574 };
575 write_guest_bytes(env, ptr, data)
576}
577
578fn guest_ptr_with_offset(guest_ptr: i32, offset: i32) -> Option<i32> {
579 guest_ptr
580 .checked_add(offset)
581 .filter(|ptr| *ptr >= INVALID_HANDLE)
582}
583
584fn read_guest_bytes(
585 env: &mut FunctionEnvMut<WasmCapiEnv>,
586 guest_ptr: i32,
587 len: usize,
588) -> Option<Vec<u8>> {
589 let ptr = guest_byte_ptr(guest_ptr)?;
590 let len = u32::try_from(len).ok()?;
591 if len == 0 {
592 return Some(Vec::new());
593 }
594
595 let (state, store) = env.data_and_store_mut();
596 let memory = state.memory.clone()?;
597 let view = memory.view(&store);
598
599 ptr.slice(&view, len).ok()?.read_to_vec().ok()
600}
601
602fn read_guest_array<const N: usize>(
603 env: &mut FunctionEnvMut<WasmCapiEnv>,
604 guest_ptr: i32,
605) -> Option<[u8; N]> {
606 let ptr = guest_byte_ptr(guest_ptr)?;
607 let len = u32::try_from(N).ok()?;
608 let (state, store) = env.data_and_store_mut();
609 let memory = state.memory.clone()?;
610 let mut bytes = [0u8; N];
611 let view = memory.view(&store);
612 ptr.slice(&view, len).ok()?.read_slice(&mut bytes).ok()?;
613 Some(bytes)
614}
615
616fn read_guest_i32_vec(
617 env: &mut FunctionEnvMut<WasmCapiEnv>,
618 guest_ptr: i32,
619 len: usize,
620) -> Option<Vec<i32>> {
621 let offset = guest_memory_offset(guest_ptr)?;
622 if len == 0 {
623 return Some(Vec::new());
624 }
625 let byte_len = len.checked_mul(size_of::<i32>())?;
626
627 let (state, store) = env.data_and_store_mut();
628 let memory = state.memory.clone()?;
629 let view = memory.view(&store);
630
631 let mut out = Vec::<i32>::with_capacity(len);
632 unsafe {
633 let spare = out.spare_capacity_mut();
634 let bytes = slice::from_raw_parts_mut(spare.as_mut_ptr().cast::<u8>(), byte_len);
635 view.read(offset, bytes).ok()?;
636 out.set_len(len);
637 }
638
639 for value in &mut out {
642 *value = i32::from_le(*value);
643 }
644
645 Some(out)
646}
647
648fn write_guest_i32_slice(
649 env: &mut FunctionEnvMut<WasmCapiEnv>,
650 guest_ptr: i32,
651 values: &[i32],
652) -> bool {
653 let Some(byte_len) = values.len().checked_mul(size_of::<i32>()) else {
654 return false;
655 };
656 if byte_len == 0 {
657 return true;
658 }
659
660 for (index, value) in values.iter().enumerate() {
661 let Ok(index) = i32::try_from(index) else {
662 return false;
663 };
664 let Some(offset) = index.checked_mul(size_of::<i32>() as i32) else {
665 return false;
666 };
667 if !write_guest_bytes_offset(env, guest_ptr, offset, &value.to_le_bytes()) {
668 return false;
669 }
670 }
671 true
672}
673
674fn allocate_guest_memory(env: &mut FunctionEnvMut<WasmCapiEnv>, len: usize) -> Option<i32> {
675 if len == 0 {
676 return Some(0);
677 }
678
679 let malloc_fn = env.data().malloc_fn.clone()?;
680 let len = i32::try_from(len).ok()?;
681 let guest_ptr: i32 = {
682 let (_, mut store_ref) = env.data_and_store_mut();
683 malloc_fn.call(&mut store_ref, len).ok()?
684 };
685 if guest_ptr <= INVALID_HANDLE {
686 return None;
687 }
688 Some(guest_ptr)
689}
690
691fn free_guest_memory(env: &mut FunctionEnvMut<WasmCapiEnv>, guest_ptr: i32) {
692 if guest_ptr <= INVALID_HANDLE {
693 return;
694 }
695
696 let Some(free_fn) = env.data().free_fn.clone() else {
697 return;
698 };
699 let (_, mut store_ref) = env.data_and_store_mut();
700 let _ = free_fn.call(&mut store_ref, guest_ptr);
701}
702
703struct GuestAllocation<'env, 'store> {
704 env: &'env mut FunctionEnvMut<'store, WasmCapiEnv>,
705 ptr: i32,
706}
707
708impl<'env, 'store> GuestAllocation<'env, 'store> {
709 fn new(env: &'env mut FunctionEnvMut<'store, WasmCapiEnv>, len: usize) -> Option<Self> {
710 let ptr = allocate_guest_memory(env, len)?;
711 non_null_guest_ptr(ptr)?;
712 Some(Self { env, ptr })
713 }
714
715 fn write_bytes(&mut self, data: &[u8]) -> bool {
716 write_guest_bytes(self.env, self.ptr, data)
717 }
718
719 fn write_i32_slice(&mut self, values: &[i32]) -> bool {
720 write_guest_i32_slice(self.env, self.ptr, values)
721 }
722
723 fn write_wasm_val_at(&mut self, offset: usize, value: &Value) -> bool {
724 let Ok(offset) = i32::try_from(offset) else {
725 return false;
726 };
727 let Some(ptr) = self.ptr.checked_add(offset) else {
728 return false;
729 };
730 write_wasm_val(self.env, ptr, value)
731 }
732
733 fn write_vec_header(&mut self, vec_ptr: NonZeroI32, len: u32) -> bool {
734 write_guest_vec_header(self.env, vec_ptr.get(), len, self.ptr)
735 }
736
737 fn write_self_vec_header(&mut self, len: u32, data_ptr: i32) -> bool {
738 write_guest_vec_header(self.env, self.ptr, len, data_ptr)
739 }
740
741 fn write_handles(&mut self, handles: &[i32]) -> bool {
742 write_handle_vec(self.env, self.ptr, handles)
743 }
744
745 fn allocate_vec_header(&mut self, len: u32) -> Option<i32> {
746 allocate_guest_vec_header(self.env, len, self.ptr)
747 }
748
749 fn copy_from_wasmer_memory(&mut self, memory: &Memory, len: usize) -> bool {
750 copy_wasmer_memory_to_guest(self.env, memory, self.ptr, len)
751 }
752
753 fn into_raw(mut self) -> i32 {
754 let ptr = self.ptr;
755 self.ptr = INVALID_HANDLE;
756 ptr
757 }
758}
759
760impl Drop for GuestAllocation<'_, '_> {
761 fn drop(&mut self) {
762 if self.ptr != INVALID_HANDLE {
763 free_guest_memory(self.env, self.ptr);
764 }
765 }
766}
767
768fn read_u32(env: &mut FunctionEnvMut<WasmCapiEnv>, ptr: i32) -> Option<u32> {
769 Some(u32::from_le_bytes(read_guest_array(env, ptr)?))
770}
771
772fn read_i32(env: &mut FunctionEnvMut<WasmCapiEnv>, ptr: i32) -> Option<i32> {
773 Some(read_u32(env, ptr)? as i32)
774}
775
776fn read_u64(env: &mut FunctionEnvMut<WasmCapiEnv>, ptr: i32) -> Option<u64> {
777 Some(u64::from_le_bytes(read_guest_array(env, ptr)?))
778}
779
780fn write_guest_vec_header(
781 env: &mut FunctionEnvMut<WasmCapiEnv>,
782 vec_ptr: i32,
783 len: u32,
784 data_ptr: i32,
785) -> bool {
786 write_guest_u32(env, vec_ptr, len)
787 && write_guest_i32_offset(env, vec_ptr, WASM_VEC_DATA_OFFSET, data_ptr)
788}
789
790fn allocate_guest_vec_header(
791 env: &mut FunctionEnvMut<WasmCapiEnv>,
792 len: u32,
793 data_ptr: i32,
794) -> Option<i32> {
795 let mut allocation = GuestAllocation::new(env, WASM_VEC_SIZE)?;
796 if !allocation.write_self_vec_header(len, data_ptr) {
797 return None;
798 }
799 Some(allocation.into_raw())
800}
801
802fn read_byte_vec(env: &mut FunctionEnvMut<WasmCapiEnv>, vec_ptr: i32) -> Option<Vec<u8>> {
803 if vec_ptr <= INVALID_HANDLE {
804 return None;
805 }
806 let size = read_u32(env, vec_ptr)? as usize;
807 let data_ptr = read_i32(env, guest_ptr_with_offset(vec_ptr, WASM_VEC_DATA_OFFSET)?)?;
808 if size == 0 {
809 return Some(Vec::new());
810 }
811 read_guest_bytes(env, data_ptr, size)
812}
813
814fn write_byte_vec(env: &mut FunctionEnvMut<WasmCapiEnv>, vec_ptr: i32, bytes: &[u8]) -> bool {
815 let Some(vec_ptr) = non_null_guest_ptr(vec_ptr) else {
816 return false;
817 };
818 let Ok(len) = u32::try_from(bytes.len()) else {
819 return false;
820 };
821
822 if bytes.is_empty() {
823 return write_guest_vec_header(env, vec_ptr.get(), len, 0);
824 }
825
826 let Some(mut data) = GuestAllocation::new(env, bytes.len()) else {
827 return false;
828 };
829 if !data.write_bytes(bytes) || !data.write_vec_header(vec_ptr, len) {
830 return false;
831 }
832 let _ = data.into_raw();
833 true
834}
835
836fn allocate_name(env: &mut FunctionEnvMut<WasmCapiEnv>, name: &str) -> i32 {
837 let Ok(len) = u32::try_from(name.len()) else {
838 return INVALID_HANDLE;
839 };
840
841 if name.is_empty() {
842 return allocate_guest_vec_header(env, len, 0).unwrap_or(INVALID_HANDLE);
843 }
844
845 let Some(mut data) = GuestAllocation::new(env, name.len()) else {
846 return INVALID_HANDLE;
847 };
848 if !data.write_bytes(name.as_bytes()) {
849 return INVALID_HANDLE;
850 }
851 let Some(vec_ptr) = data.allocate_vec_header(len) else {
852 return INVALID_HANDLE;
853 };
854 let _ = data.into_raw();
855 vec_ptr
856}
857
858fn write_handle_vec(env: &mut FunctionEnvMut<WasmCapiEnv>, out_ptr: i32, handles: &[i32]) -> bool {
859 let Some(out_ptr) = non_null_guest_ptr(out_ptr) else {
860 return false;
861 };
862 let Ok(len) = u32::try_from(handles.len()) else {
863 return false;
864 };
865 let Some(byte_len) = handles.len().checked_mul(size_of::<i32>()) else {
866 return false;
867 };
868
869 if handles.is_empty() {
870 return write_guest_vec_header(env, out_ptr.get(), len, 0);
871 }
872
873 let Some(mut data) = GuestAllocation::new(env, byte_len) else {
874 return false;
875 };
876 if !data.write_i32_slice(handles) || !data.write_vec_header(out_ptr, len) {
877 return false;
878 }
879 let _ = data.into_raw();
880 true
881}
882
883fn read_handle_vec(env: &mut FunctionEnvMut<WasmCapiEnv>, vec_ptr: i32) -> Option<Vec<i32>> {
884 if vec_ptr <= INVALID_HANDLE {
885 return None;
886 }
887 let size = read_u32(env, vec_ptr)? as usize;
888 let data_ptr = read_i32(env, guest_ptr_with_offset(vec_ptr, WASM_VEC_DATA_OFFSET)?)?;
889 if size == 0 {
890 return Some(Vec::new());
891 }
892 read_guest_i32_vec(env, data_ptr, size)
893}
894
895fn type_to_wasm_valkind(ty: Type) -> Option<u8> {
896 Some(match ty {
897 Type::I32 => WASM_I32,
898 Type::I64 => WASM_I64,
899 Type::F32 => WASM_F32,
900 Type::F64 => WASM_F64,
901 Type::FuncRef | Type::ExternRef | Type::V128 | Type::ExceptionRef => return None,
902 })
903}
904
905fn type_to_wasm_kind(ty: Type) -> Option<u8> {
906 Some(match ty {
907 Type::I32 => WASM_I32,
908 Type::I64 => WASM_I64,
909 Type::F32 => WASM_F32,
910 Type::F64 => WASM_F64,
911 Type::FuncRef => WASM_FUNCREF,
912 Type::ExternRef => WASM_EXTERNREF,
913 Type::V128 | Type::ExceptionRef => return None,
914 })
915}
916
917fn wasm_kind_to_type(kind: i32) -> Option<Type> {
918 match kind as u8 {
919 WASM_I32 => Some(Type::I32),
920 WASM_I64 => Some(Type::I64),
921 WASM_F32 => Some(Type::F32),
922 WASM_F64 => Some(Type::F64),
923 WASM_FUNCREF => Some(Type::FuncRef),
924 WASM_EXTERNREF => Some(Type::ExternRef),
925 _ => None,
926 }
927}
928
929fn extern_kind(ty: &ExternType) -> i32 {
930 match ty {
931 ExternType::Function(_) => WASM_EXTERN_FUNC,
932 ExternType::Global(_) => WASM_EXTERN_GLOBAL,
933 ExternType::Table(_) => WASM_EXTERN_TABLE,
934 ExternType::Memory(_) => WASM_EXTERN_MEMORY,
935 ExternType::Tag(_) => INVALID_KIND,
936 }
937}
938
939fn read_wasm_val(env: &mut FunctionEnvMut<WasmCapiEnv>, val_ptr: i32, ty: Type) -> Option<Value> {
940 match ty {
941 Type::I32 => Some(Value::I32(read_i32(
942 env,
943 val_ptr + WASM_VAL_PAYLOAD_OFFSET,
944 )?)),
945 Type::I64 => Some(Value::I64(
946 read_u64(env, val_ptr + WASM_VAL_PAYLOAD_OFFSET)? as i64,
947 )),
948 Type::F32 => {
949 let raw = read_u32(env, val_ptr + WASM_VAL_PAYLOAD_OFFSET)?;
950 Some(Value::F32(f32::from_bits(raw)))
951 }
952 Type::F64 => {
953 let raw = read_u64(env, val_ptr + WASM_VAL_PAYLOAD_OFFSET)?;
954 Some(Value::F64(f64::from_bits(raw)))
955 }
956 Type::FuncRef | Type::ExternRef | Type::V128 | Type::ExceptionRef => None,
959 }
960}
961
962fn write_wasm_val(env: &mut FunctionEnvMut<WasmCapiEnv>, val_ptr: i32, value: &Value) -> bool {
963 let Some(val_ptr) = non_null_guest_ptr(val_ptr) else {
964 return false;
965 };
966 let val_ptr = val_ptr.get();
967 let Some(kind) = type_to_wasm_valkind(value.ty()) else {
968 return false;
969 };
970 if !write_guest_bytes(env, val_ptr, &[kind]) {
971 return false;
972 }
973 match value {
974 Value::I32(v) => {
975 write_guest_bytes_offset(env, val_ptr, WASM_VAL_PAYLOAD_OFFSET, &v.to_le_bytes())
976 }
977 Value::I64(v) => {
978 write_guest_bytes_offset(env, val_ptr, WASM_VAL_PAYLOAD_OFFSET, &v.to_le_bytes())
979 }
980 Value::F32(v) => write_guest_bytes_offset(
981 env,
982 val_ptr,
983 WASM_VAL_PAYLOAD_OFFSET,
984 &v.to_bits().to_le_bytes(),
985 ),
986 Value::F64(v) => write_guest_bytes_offset(
987 env,
988 val_ptr,
989 WASM_VAL_PAYLOAD_OFFSET,
990 &v.to_bits().to_le_bytes(),
991 ),
992 Value::FuncRef(_) | Value::ExternRef(_) | Value::V128(_) | Value::ExceptionRef(_) => false,
995 }
996}
997
998fn read_limits(
999 env: &mut FunctionEnvMut<WasmCapiEnv>,
1000 limits_ptr: i32,
1001) -> Option<(u32, Option<u32>)> {
1002 if limits_ptr <= INVALID_HANDLE {
1003 return None;
1004 }
1005 let min = read_u32(env, limits_ptr)?;
1006 let max = read_u32(env, guest_ptr_with_offset(limits_ptr, 4)?)?;
1007 let max = if max == u32::MAX { None } else { Some(max) };
1008 Some((min, max))
1009}
1010
1011fn clone_extern_from_handle(env: &FunctionEnvMut<WasmCapiEnv>, handle: i32) -> Option<Extern> {
1012 match env.data().state.get(handle)? {
1013 WasmObject::Extern(WasmExtern::Func(f)) => Some(Extern::Function(f.clone())),
1014 WasmObject::Extern(WasmExtern::Global(g)) => Some(Extern::Global(g.clone())),
1015 WasmObject::Extern(WasmExtern::Table(t)) => Some(Extern::Table(t.clone())),
1016 WasmObject::Extern(WasmExtern::Memory(m)) => Some(Extern::Memory(m.clone())),
1017 WasmObject::Func(f) => Some(Extern::Function(f.clone())),
1018 WasmObject::Global(g) => Some(Extern::Global(g.clone())),
1019 WasmObject::Table(t) => Some(Extern::Table(t.clone())),
1020 WasmObject::Memory(m) => Some(Extern::Memory(m.clone())),
1021 _ => None,
1022 }
1023}
1024
1025fn insert(env: &mut FunctionEnvMut<WasmCapiEnv>, object: WasmObject) -> i32 {
1026 env.data_mut().state.insert(object)
1027}
1028
1029fn delete_handle(mut env: FunctionEnvMut<WasmCapiEnv>, handle: i32) {
1030 if let Some(shadow) = env.data_mut().state.remove(handle) {
1031 free_guest_memory(&mut env, shadow.guest_ptr);
1032 }
1033}
1034
1035fn wasm_engine_new(mut env: FunctionEnvMut<WasmCapiEnv>) -> i32 {
1036 insert(&mut env, WasmObject::Engine)
1037}
1038
1039fn wasm_store_new(mut env: FunctionEnvMut<WasmCapiEnv>, _engine: i32) -> i32 {
1040 insert(&mut env, WasmObject::Store)
1041}
1042
1043fn wasm_module_new(mut env: FunctionEnvMut<WasmCapiEnv>, _store: i32, bytes_ptr: i32) -> i32 {
1044 let Some(bytes) = read_byte_vec(&mut env, bytes_ptr) else {
1045 return INVALID_HANDLE;
1046 };
1047
1048 let module = if let Some(resolve_module_sync) = env.data().resolve_module_sync.clone() {
1049 resolve_module_sync(bytes)
1050 } else {
1051 let (_, store) = env.data_and_store_mut();
1052 Module::new(&store, bytes).map_err(Into::into)
1053 };
1054
1055 match module {
1056 Ok(module) => env.data_mut().state.insert(WasmObject::Module(module)),
1057 Err(_) => INVALID_HANDLE,
1058 }
1059}
1060
1061fn wasm_module_validate(mut env: FunctionEnvMut<WasmCapiEnv>, _store: i32, bytes_ptr: i32) -> i32 {
1062 let Some(bytes) = read_byte_vec(&mut env, bytes_ptr) else {
1063 return BOOL_FALSE;
1064 };
1065 let (_, store) = env.data_and_store_mut();
1066 Module::validate(&store, &bytes).is_ok() as i32
1067}
1068
1069fn wasm_module_imports(mut env: FunctionEnvMut<WasmCapiEnv>, module_handle: i32, out_ptr: i32) {
1070 let module = match env.data().state.get(module_handle) {
1071 Some(WasmObject::Module(module)) => module.clone(),
1072 _ => {
1073 write_handle_vec(&mut env, out_ptr, &[]);
1074 return;
1075 }
1076 };
1077 let handles: Vec<i32> = module
1078 .imports()
1079 .map(|import| {
1080 insert(
1081 &mut env,
1082 WasmObject::ImportType {
1083 module: import.module().to_string(),
1084 name: import.name().to_string(),
1085 ty: import.ty().clone(),
1086 },
1087 )
1088 })
1089 .collect();
1090 write_handle_vec(&mut env, out_ptr, &handles);
1091}
1092
1093fn wasm_module_exports(mut env: FunctionEnvMut<WasmCapiEnv>, module_handle: i32, out_ptr: i32) {
1094 let module = match env.data().state.get(module_handle) {
1095 Some(WasmObject::Module(module)) => module.clone(),
1096 _ => {
1097 write_handle_vec(&mut env, out_ptr, &[]);
1098 return;
1099 }
1100 };
1101 let handles: Vec<i32> = module
1102 .exports()
1103 .map(|export| {
1104 insert(
1105 &mut env,
1106 WasmObject::ExportType {
1107 name: export.name().to_string(),
1108 ty: export.ty().clone(),
1109 },
1110 )
1111 })
1112 .collect();
1113 write_handle_vec(&mut env, out_ptr, &handles);
1114}
1115
1116fn wasm_importtype_module(mut env: FunctionEnvMut<WasmCapiEnv>, import_handle: i32) -> i32 {
1117 let name = match env.data().state.get(import_handle) {
1118 Some(WasmObject::ImportType { module, .. }) => module.clone(),
1119 _ => return INVALID_HANDLE,
1120 };
1121 allocate_name(&mut env, &name)
1122}
1123
1124fn wasm_importtype_name(mut env: FunctionEnvMut<WasmCapiEnv>, import_handle: i32) -> i32 {
1125 let name = match env.data().state.get(import_handle) {
1126 Some(WasmObject::ImportType { name, .. }) => name.clone(),
1127 _ => return INVALID_HANDLE,
1128 };
1129 allocate_name(&mut env, &name)
1130}
1131
1132fn wasm_importtype_type(mut env: FunctionEnvMut<WasmCapiEnv>, import_handle: i32) -> i32 {
1133 let ty = match env.data().state.get(import_handle) {
1134 Some(WasmObject::ImportType { ty, .. }) => ty.clone(),
1135 _ => return INVALID_HANDLE,
1136 };
1137 insert(&mut env, WasmObject::ExternType(ty))
1138}
1139
1140fn wasm_exporttype_name(mut env: FunctionEnvMut<WasmCapiEnv>, export_handle: i32) -> i32 {
1141 let name = match env.data().state.get(export_handle) {
1142 Some(WasmObject::ExportType { name, .. }) => name.clone(),
1143 _ => return INVALID_HANDLE,
1144 };
1145 allocate_name(&mut env, &name)
1146}
1147
1148fn wasm_exporttype_type(mut env: FunctionEnvMut<WasmCapiEnv>, export_handle: i32) -> i32 {
1149 let ty = match env.data().state.get(export_handle) {
1150 Some(WasmObject::ExportType { ty, .. }) => ty.clone(),
1151 _ => return INVALID_HANDLE,
1152 };
1153 insert(&mut env, WasmObject::ExternType(ty))
1154}
1155
1156fn wasm_externtype_kind(env: FunctionEnvMut<WasmCapiEnv>, type_handle: i32) -> i32 {
1157 match env.data().state.get(type_handle) {
1158 Some(WasmObject::ExternType(ty)) => extern_kind(ty),
1159 _ => INVALID_KIND,
1160 }
1161}
1162
1163fn wasm_externtype_as_functype_const(
1164 mut env: FunctionEnvMut<WasmCapiEnv>,
1165 type_handle: i32,
1166) -> i32 {
1167 let ty = match env.data().state.get(type_handle) {
1168 Some(WasmObject::ExternType(ExternType::Function(ty))) => ty.clone(),
1169 _ => return INVALID_HANDLE,
1170 };
1171 insert(&mut env, WasmObject::FuncType(ty))
1172}
1173
1174fn wasm_functype_copy(mut env: FunctionEnvMut<WasmCapiEnv>, type_handle: i32) -> i32 {
1175 let ty = match env.data().state.get(type_handle) {
1176 Some(WasmObject::FuncType(ty)) => ty.clone(),
1177 _ => return INVALID_HANDLE,
1178 };
1179 insert(&mut env, WasmObject::FuncType(ty))
1180}
1181
1182fn write_valtype_vec_for_types(env: &mut FunctionEnvMut<WasmCapiEnv>, types: &[Type]) -> i32 {
1183 let handles: Vec<i32> = types
1184 .iter()
1185 .map(|ty| insert(env, WasmObject::ValType(*ty)))
1186 .collect();
1187 let Some(mut allocation) = GuestAllocation::new(env, WASM_VEC_SIZE) else {
1188 return INVALID_HANDLE;
1189 };
1190 if !allocation.write_handles(&handles) {
1191 return INVALID_HANDLE;
1192 }
1193 allocation.into_raw()
1194}
1195
1196fn wasm_functype_params(mut env: FunctionEnvMut<WasmCapiEnv>, type_handle: i32) -> i32 {
1197 let params = match env.data().state.get(type_handle) {
1198 Some(WasmObject::FuncType(ty)) => ty.params().to_vec(),
1199 _ => return INVALID_HANDLE,
1200 };
1201 write_valtype_vec_for_types(&mut env, ¶ms)
1202}
1203
1204fn wasm_functype_results(mut env: FunctionEnvMut<WasmCapiEnv>, type_handle: i32) -> i32 {
1205 let results = match env.data().state.get(type_handle) {
1206 Some(WasmObject::FuncType(ty)) => ty.results().to_vec(),
1207 _ => return INVALID_HANDLE,
1208 };
1209 write_valtype_vec_for_types(&mut env, &results)
1210}
1211
1212fn wasm_valtype_new(mut env: FunctionEnvMut<WasmCapiEnv>, kind: i32) -> i32 {
1213 match wasm_kind_to_type(kind) {
1214 Some(ty) => insert(&mut env, WasmObject::ValType(ty)),
1215 None => INVALID_HANDLE,
1216 }
1217}
1218
1219fn wasm_valtype_kind(env: FunctionEnvMut<WasmCapiEnv>, valtype_handle: i32) -> i32 {
1220 match env.data().state.get(valtype_handle) {
1221 Some(WasmObject::ValType(ty)) => type_to_wasm_kind(*ty)
1222 .map(i32::from)
1223 .unwrap_or(INVALID_KIND),
1224 _ => INVALID_KIND,
1225 }
1226}
1227
1228fn wasm_memorytype_new(mut env: FunctionEnvMut<WasmCapiEnv>, limits_ptr: i32) -> i32 {
1229 let Some((min, max)) = read_limits(&mut env, limits_ptr) else {
1230 return INVALID_HANDLE;
1231 };
1232 insert(
1233 &mut env,
1234 WasmObject::MemoryType(MemoryType::new(Pages(min), max.map(Pages), false)),
1235 )
1236}
1237
1238fn wasm_memory_new(mut env: FunctionEnvMut<WasmCapiEnv>, _store: i32, type_handle: i32) -> i32 {
1239 let ty = match env.data().state.get(type_handle) {
1240 Some(WasmObject::MemoryType(ty)) => *ty,
1241 _ => return INVALID_HANDLE,
1242 };
1243 match Memory::new(&mut env, ty) {
1244 Ok(memory) => insert(&mut env, WasmObject::Memory(memory)),
1245 Err(_) => INVALID_HANDLE,
1246 }
1247}
1248
1249fn wasm_memory_size(env: FunctionEnvMut<WasmCapiEnv>, memory_handle: i32) -> i32 {
1250 let Some(memory) = memory_from_handle(&env, memory_handle) else {
1251 return INVALID_SIZE;
1252 };
1253 i32::try_from(memory.size(&env).0).unwrap_or(INVALID_SIZE)
1254}
1255
1256fn wasm_memory_grow(mut env: FunctionEnvMut<WasmCapiEnv>, memory_handle: i32, delta: i32) -> i32 {
1257 let Ok(delta) = u32::try_from(delta) else {
1258 return BOOL_FALSE;
1259 };
1260 let memory = match env.data().state.get(memory_handle) {
1261 Some(WasmObject::Memory(memory)) => memory.clone(),
1262 Some(WasmObject::Extern(WasmExtern::Memory(memory))) => memory.clone(),
1263 _ => return BOOL_FALSE,
1264 };
1265 if memory.grow(&mut env, Pages(delta)).is_ok() {
1266 BOOL_TRUE
1267 } else {
1268 BOOL_FALSE
1269 }
1270}
1271
1272fn memory_from_handle(env: &FunctionEnvMut<WasmCapiEnv>, memory_handle: i32) -> Option<Memory> {
1273 match env.data().state.get(memory_handle)? {
1274 WasmObject::Memory(memory) => Some(memory.clone()),
1275 WasmObject::Extern(WasmExtern::Memory(memory)) => Some(memory.clone()),
1276 _ => None,
1277 }
1278}
1279
1280fn memory_supports_shadow(env: &FunctionEnvMut<WasmCapiEnv>, memory: &Memory) -> bool {
1281 !memory.ty(env).shared
1285}
1286
1287fn copy_wasmer_memory_to_guest(
1288 env: &mut FunctionEnvMut<WasmCapiEnv>,
1289 memory: &Memory,
1290 guest_ptr: i32,
1291 len: usize,
1292) -> bool {
1293 if len == 0 {
1294 return true;
1295 }
1296 let Some(guest_memory) = env.data().memory.clone() else {
1297 return false;
1298 };
1299 let source_view = memory.view(&*env);
1300 let guest_view = guest_memory.view(&*env);
1301 let Some(source_offset) = checked_memory_offset(0, len, source_view.data_size()) else {
1302 return false;
1303 };
1304 let Some(guest_offset) = checked_memory_offset(guest_ptr, len, guest_view.data_size()) else {
1305 return false;
1306 };
1307 let source_base = source_view.data_ptr();
1308 let guest_base = guest_view.data_ptr();
1309 if ptr::eq(source_base, guest_base) {
1310 return false;
1311 }
1312 unsafe {
1313 ptr::copy_nonoverlapping(
1315 source_base.add(source_offset),
1316 guest_base.add(guest_offset),
1317 len,
1318 );
1319 }
1320 true
1321}
1322
1323fn copy_guest_memory_to_wasmer(
1324 env: &mut FunctionEnvMut<WasmCapiEnv>,
1325 guest_ptr: i32,
1326 memory: &Memory,
1327 len: usize,
1328) -> bool {
1329 if len == 0 {
1330 return true;
1331 }
1332 let Some(guest_memory) = env.data().memory.clone() else {
1333 return false;
1334 };
1335 let guest_view = guest_memory.view(&*env);
1336 let target_view = memory.view(&*env);
1337 let Some(guest_offset) = checked_memory_offset(guest_ptr, len, guest_view.data_size()) else {
1338 return false;
1339 };
1340 let Some(target_offset) = checked_memory_offset(0, len, target_view.data_size()) else {
1341 return false;
1342 };
1343 let guest_base = guest_view.data_ptr();
1344 let target_base = target_view.data_ptr();
1345 if ptr::eq(guest_base, target_base) {
1346 return false;
1347 }
1348 unsafe {
1349 ptr::copy_nonoverlapping(
1351 guest_base.add(guest_offset),
1352 target_base.add(target_offset),
1353 len,
1354 );
1355 }
1356 true
1357}
1358
1359fn wasm_memory_data_size(env: FunctionEnvMut<WasmCapiEnv>, memory_handle: i32) -> i32 {
1360 let Some(memory) = memory_from_handle(&env, memory_handle) else {
1361 return INVALID_SIZE;
1362 };
1363 if !memory_supports_shadow(&env, &memory) {
1364 return INVALID_SIZE;
1365 }
1366 i32::try_from(memory.view(&env).data_size()).unwrap_or(INVALID_SIZE)
1367}
1368
1369fn wasm_memory_data(mut env: FunctionEnvMut<WasmCapiEnv>, memory_handle: i32) -> i32 {
1370 let Some(memory) = memory_from_handle(&env, memory_handle) else {
1371 return INVALID_HANDLE;
1372 };
1373
1374 if !memory_supports_shadow(&env, &memory) {
1375 return INVALID_HANDLE;
1376 };
1377 let size = {
1378 let view = memory.view(&env);
1379 let Ok(size) = usize::try_from(view.data_size()) else {
1380 return INVALID_HANDLE;
1381 };
1382 size
1383 };
1384 if size == 0 {
1385 return INVALID_HANDLE;
1386 }
1387
1388 let existing = env.data().state.memory_shadows.get(&memory_handle).copied();
1389 let shadow = match existing {
1390 Some(existing) if existing.len >= size => {
1391 let shadow = MemoryShadow {
1392 guest_ptr: existing.guest_ptr,
1393 len: size,
1394 };
1395 if !copy_wasmer_memory_to_guest(&mut env, &memory, shadow.guest_ptr, size) {
1396 return INVALID_HANDLE;
1397 }
1398 shadow
1399 }
1400 _ => {
1401 let Some(mut allocation) = GuestAllocation::new(&mut env, size) else {
1402 return INVALID_HANDLE;
1403 };
1404 if !allocation.copy_from_wasmer_memory(&memory, size) {
1405 return INVALID_HANDLE;
1406 }
1407 let guest_ptr = allocation.into_raw();
1408 if let Some(existing) = existing {
1409 free_guest_memory(&mut env, existing.guest_ptr);
1410 }
1411 MemoryShadow {
1412 guest_ptr,
1413 len: size,
1414 }
1415 }
1416 };
1417
1418 env.data_mut()
1419 .state
1420 .memory_shadows
1421 .insert(memory_handle, shadow);
1422 shadow.guest_ptr
1423}
1424
1425fn sync_memory_shadows_to_wasmer(env: &mut FunctionEnvMut<WasmCapiEnv>) {
1426 let shadows: Vec<_> = env
1427 .data()
1428 .state
1429 .memory_shadows
1430 .iter()
1431 .map(|(&handle, &shadow)| (handle, shadow))
1432 .collect();
1433
1434 for (handle, shadow) in shadows {
1435 let Some(memory) = memory_from_handle(env, handle) else {
1436 continue;
1437 };
1438 if !memory_supports_shadow(env, &memory) {
1439 continue;
1440 };
1441 let _ = copy_guest_memory_to_wasmer(env, shadow.guest_ptr, &memory, shadow.len);
1442 }
1443}
1444
1445fn refresh_memory_shadows_from_wasmer(env: &mut FunctionEnvMut<WasmCapiEnv>) {
1446 let shadows: Vec<_> = env
1447 .data()
1448 .state
1449 .memory_shadows
1450 .iter()
1451 .map(|(&handle, &shadow)| (handle, shadow))
1452 .collect();
1453
1454 for (handle, shadow) in shadows {
1455 let Some(memory) = memory_from_handle(env, handle) else {
1456 continue;
1457 };
1458 if !memory_supports_shadow(env, &memory) {
1459 continue;
1460 }
1461 let _ = copy_wasmer_memory_to_guest(env, &memory, shadow.guest_ptr, shadow.len);
1462 }
1463}
1464
1465fn wasm_globaltype_new(
1466 mut env: FunctionEnvMut<WasmCapiEnv>,
1467 valtype_handle: i32,
1468 mutability: i32,
1469) -> i32 {
1470 let ty = match env.data().state.get(valtype_handle) {
1471 Some(WasmObject::ValType(ty)) => *ty,
1472 _ => return INVALID_HANDLE,
1473 };
1474 let mutability = if mutability == WASM_VAR {
1475 Mutability::Var
1476 } else {
1477 Mutability::Const
1478 };
1479 insert(
1480 &mut env,
1481 WasmObject::GlobalType(GlobalType::new(ty, mutability)),
1482 )
1483}
1484
1485fn wasm_globaltype_content(mut env: FunctionEnvMut<WasmCapiEnv>, globaltype_handle: i32) -> i32 {
1486 let ty = match env.data().state.get(globaltype_handle) {
1487 Some(WasmObject::GlobalType(ty)) => ty.ty,
1488 _ => return INVALID_HANDLE,
1489 };
1490 insert(&mut env, WasmObject::ValType(ty))
1491}
1492
1493fn wasm_globaltype_mutability(env: FunctionEnvMut<WasmCapiEnv>, globaltype_handle: i32) -> i32 {
1494 match env.data().state.get(globaltype_handle) {
1495 Some(WasmObject::GlobalType(ty)) if ty.mutability == Mutability::Var => WASM_VAR,
1496 Some(WasmObject::GlobalType(_)) => WASM_CONST,
1497 _ => WASM_CONST,
1498 }
1499}
1500
1501fn wasm_global_new(
1502 mut env: FunctionEnvMut<WasmCapiEnv>,
1503 _store: i32,
1504 globaltype_handle: i32,
1505 val_ptr: i32,
1506) -> i32 {
1507 let ty = match env.data().state.get(globaltype_handle) {
1508 Some(WasmObject::GlobalType(ty)) => *ty,
1509 _ => return INVALID_HANDLE,
1510 };
1511 let Some(value) = read_wasm_val(&mut env, val_ptr, ty.ty) else {
1512 return INVALID_HANDLE;
1513 };
1514 let global = if ty.mutability == Mutability::Var {
1515 Global::new_mut(&mut env, value)
1516 } else {
1517 Global::new(&mut env, value)
1518 };
1519 insert(&mut env, WasmObject::Global(global))
1520}
1521
1522fn global_from_handle(env: &FunctionEnvMut<WasmCapiEnv>, global_handle: i32) -> Option<Global> {
1523 match env.data().state.get(global_handle)? {
1524 WasmObject::Global(global) => Some(global.clone()),
1525 WasmObject::Extern(WasmExtern::Global(global)) => Some(global.clone()),
1526 _ => None,
1527 }
1528}
1529
1530fn wasm_global_type(mut env: FunctionEnvMut<WasmCapiEnv>, global_handle: i32) -> i32 {
1531 let Some(global) = global_from_handle(&env, global_handle) else {
1532 return INVALID_HANDLE;
1533 };
1534 let ty = global.ty(&env);
1535 insert(&mut env, WasmObject::GlobalType(ty))
1536}
1537
1538fn wasm_global_get(mut env: FunctionEnvMut<WasmCapiEnv>, global_handle: i32, out_ptr: i32) {
1539 let Some(global) = global_from_handle(&env, global_handle) else {
1540 return;
1541 };
1542 let value = global.get(&mut env);
1543 write_wasm_val(&mut env, out_ptr, &value);
1544}
1545
1546fn wasm_global_set(mut env: FunctionEnvMut<WasmCapiEnv>, global_handle: i32, val_ptr: i32) {
1547 let Some(global) = global_from_handle(&env, global_handle) else {
1548 return;
1549 };
1550 let ty = global.ty(&env).ty;
1551 if let Some(value) = read_wasm_val(&mut env, val_ptr, ty) {
1552 let _ = global.set(&mut env, value);
1553 }
1554}
1555
1556fn wasm_instance_new(
1557 mut env: FunctionEnvMut<WasmCapiEnv>,
1558 _store: i32,
1559 module_handle: i32,
1560 imports_vec_ptr: i32,
1561 trap_out_ptr: i32,
1562) -> i32 {
1563 if let Some(trap_out_ptr) = non_null_guest_ptr(trap_out_ptr) {
1564 write_guest_u32(&mut env, trap_out_ptr.get(), INVALID_HANDLE as u32);
1565 }
1566 let module = match env.data().state.get(module_handle) {
1567 Some(WasmObject::Module(module)) => module.clone(),
1568 _ => return INVALID_HANDLE,
1569 };
1570
1571 let import_handles = read_handle_vec(&mut env, imports_vec_ptr).unwrap_or_default();
1572 let mut imports = Imports::new();
1573 for (import, handle) in module.imports().zip(import_handles.into_iter()) {
1574 let Some(ext) = clone_extern_from_handle(&env, handle) else {
1575 return INVALID_HANDLE;
1576 };
1577 imports.define(import.module(), import.name(), ext);
1578 }
1579
1580 match Instance::new(&mut env, &module, &imports) {
1581 Ok(instance) => insert(&mut env, WasmObject::Instance(instance)),
1582 Err(err) => {
1583 if let Some(trap_out_ptr) = non_null_guest_ptr(trap_out_ptr) {
1584 let trap = insert(&mut env, WasmObject::Trap(err.to_string()));
1585 write_guest_u32(
1586 &mut env,
1587 trap_out_ptr.get(),
1588 u32::try_from(trap).unwrap_or(INVALID_HANDLE as u32),
1589 );
1590 }
1591 INVALID_HANDLE
1592 }
1593 }
1594}
1595
1596fn wasm_instance_exports(mut env: FunctionEnvMut<WasmCapiEnv>, instance_handle: i32, out_ptr: i32) {
1597 let instance = match env.data().state.get(instance_handle) {
1598 Some(WasmObject::Instance(instance)) => instance.clone(),
1599 _ => {
1600 write_handle_vec(&mut env, out_ptr, &[]);
1601 return;
1602 }
1603 };
1604 let handles: Vec<i32> = instance
1605 .exports
1606 .iter()
1607 .map(|(_, ext)| {
1608 let object = match ext {
1609 Extern::Function(func) => WasmObject::Extern(WasmExtern::Func(func.clone())),
1610 Extern::Global(global) => WasmObject::Extern(WasmExtern::Global(global.clone())),
1611 Extern::Table(table) => WasmObject::Extern(WasmExtern::Table(table.clone())),
1612 Extern::Memory(memory) => WasmObject::Extern(WasmExtern::Memory(memory.clone())),
1613 Extern::Tag(_) => WasmObject::Trap("unsupported tag export".to_string()),
1614 };
1615 insert(&mut env, object)
1616 })
1617 .collect();
1618 write_handle_vec(&mut env, out_ptr, &handles);
1619}
1620
1621fn wasm_extern_kind(env: FunctionEnvMut<WasmCapiEnv>, extern_handle: i32) -> i32 {
1622 match env.data().state.get(extern_handle) {
1623 Some(WasmObject::Extern(WasmExtern::Func(_))) | Some(WasmObject::Func(_)) => {
1624 WASM_EXTERN_FUNC
1625 }
1626 Some(WasmObject::Extern(WasmExtern::Global(_))) | Some(WasmObject::Global(_)) => {
1627 WASM_EXTERN_GLOBAL
1628 }
1629 Some(WasmObject::Extern(WasmExtern::Table(_))) | Some(WasmObject::Table(_)) => {
1630 WASM_EXTERN_TABLE
1631 }
1632 Some(WasmObject::Extern(WasmExtern::Memory(_))) | Some(WasmObject::Memory(_)) => {
1633 WASM_EXTERN_MEMORY
1634 }
1635 _ => INVALID_KIND,
1636 }
1637}
1638
1639fn wasm_extern_as_func(mut env: FunctionEnvMut<WasmCapiEnv>, extern_handle: i32) -> i32 {
1640 let object = match env.data().state.get(extern_handle) {
1641 Some(WasmObject::Extern(WasmExtern::Func(func))) => WasmObject::Func(func.clone()),
1642 _ => return INVALID_HANDLE,
1643 };
1644 insert(&mut env, object)
1645}
1646
1647fn wasm_extern_as_global(mut env: FunctionEnvMut<WasmCapiEnv>, extern_handle: i32) -> i32 {
1648 let object = match env.data().state.get(extern_handle) {
1649 Some(WasmObject::Extern(WasmExtern::Global(global))) => WasmObject::Global(global.clone()),
1650 _ => return INVALID_HANDLE,
1651 };
1652 insert(&mut env, object)
1653}
1654
1655fn wasm_extern_as_table(mut env: FunctionEnvMut<WasmCapiEnv>, extern_handle: i32) -> i32 {
1656 let object = match env.data().state.get(extern_handle) {
1657 Some(WasmObject::Extern(WasmExtern::Table(table))) => WasmObject::Table(table.clone()),
1658 _ => return INVALID_HANDLE,
1659 };
1660 insert(&mut env, object)
1661}
1662
1663fn wasm_extern_as_memory(mut env: FunctionEnvMut<WasmCapiEnv>, extern_handle: i32) -> i32 {
1664 let object = match env.data().state.get(extern_handle) {
1665 Some(WasmObject::Extern(WasmExtern::Memory(memory))) => WasmObject::Memory(memory.clone()),
1666 _ => return INVALID_HANDLE,
1667 };
1668 insert(&mut env, object)
1669}
1670
1671fn wasm_func_copy(mut env: FunctionEnvMut<WasmCapiEnv>, func_handle: i32) -> i32 {
1672 let object = match env.data().state.get(func_handle) {
1673 Some(WasmObject::Func(func)) => WasmObject::Func(func.clone()),
1674 Some(WasmObject::Extern(WasmExtern::Func(func))) => WasmObject::Func(func.clone()),
1675 _ => return INVALID_HANDLE,
1676 };
1677 insert(&mut env, object)
1678}
1679
1680fn wasm_global_copy(mut env: FunctionEnvMut<WasmCapiEnv>, global_handle: i32) -> i32 {
1681 let Some(global) = global_from_handle(&env, global_handle) else {
1682 return INVALID_HANDLE;
1683 };
1684 insert(&mut env, WasmObject::Global(global))
1685}
1686
1687fn wasm_memory_copy(mut env: FunctionEnvMut<WasmCapiEnv>, memory_handle: i32) -> i32 {
1688 let Some(memory) = memory_from_handle(&env, memory_handle) else {
1689 return INVALID_HANDLE;
1690 };
1691 insert(&mut env, WasmObject::Memory(memory))
1692}
1693
1694fn wasm_table_copy(mut env: FunctionEnvMut<WasmCapiEnv>, table_handle: i32) -> i32 {
1695 let object = match env.data().state.get(table_handle) {
1696 Some(WasmObject::Table(table)) => WasmObject::Table(table.clone()),
1697 Some(WasmObject::Extern(WasmExtern::Table(table))) => WasmObject::Table(table.clone()),
1698 _ => return INVALID_HANDLE,
1699 };
1700 insert(&mut env, object)
1701}
1702
1703fn wasm_func_as_extern(mut env: FunctionEnvMut<WasmCapiEnv>, func_handle: i32) -> i32 {
1704 let object = match env.data().state.get(func_handle) {
1705 Some(WasmObject::Func(func)) => WasmObject::Extern(WasmExtern::Func(func.clone())),
1706 _ => return INVALID_HANDLE,
1707 };
1708 insert(&mut env, object)
1709}
1710
1711fn wasm_global_as_extern(mut env: FunctionEnvMut<WasmCapiEnv>, global_handle: i32) -> i32 {
1712 let Some(global) = global_from_handle(&env, global_handle) else {
1713 return INVALID_HANDLE;
1714 };
1715 insert(&mut env, WasmObject::Extern(WasmExtern::Global(global)))
1716}
1717
1718fn wasm_memory_as_extern(mut env: FunctionEnvMut<WasmCapiEnv>, memory_handle: i32) -> i32 {
1719 let Some(memory) = memory_from_handle(&env, memory_handle) else {
1720 return INVALID_HANDLE;
1721 };
1722 insert(&mut env, WasmObject::Extern(WasmExtern::Memory(memory)))
1723}
1724
1725fn wasm_table_as_extern(mut env: FunctionEnvMut<WasmCapiEnv>, table_handle: i32) -> i32 {
1726 let object = match env.data().state.get(table_handle) {
1727 Some(WasmObject::Table(table)) => WasmObject::Extern(WasmExtern::Table(table.clone())),
1728 _ => return INVALID_HANDLE,
1729 };
1730 insert(&mut env, object)
1731}
1732
1733fn wasm_func_type(mut env: FunctionEnvMut<WasmCapiEnv>, func_handle: i32) -> i32 {
1734 let func = match env.data().state.get(func_handle) {
1735 Some(WasmObject::Func(func)) => func.clone(),
1736 Some(WasmObject::Extern(WasmExtern::Func(func))) => func.clone(),
1737 _ => return INVALID_HANDLE,
1738 };
1739 let ty = func.ty(&env);
1740 insert(&mut env, WasmObject::FuncType(ty))
1741}
1742
1743fn wasm_func_call(
1744 mut env: FunctionEnvMut<WasmCapiEnv>,
1745 func_handle: i32,
1746 args_vec_ptr: i32,
1747 results_vec_ptr: i32,
1748) -> i32 {
1749 let func = match env.data().state.get(func_handle) {
1750 Some(WasmObject::Func(func)) => func.clone(),
1751 Some(WasmObject::Extern(WasmExtern::Func(func))) => func.clone(),
1752 _ => return insert(&mut env, WasmObject::Trap("invalid function".to_string())),
1753 };
1754 let ty = func.ty(&env);
1755 let arg_data_ptr = guest_ptr_with_offset(args_vec_ptr, WASM_VEC_DATA_OFFSET)
1756 .and_then(|ptr| read_i32(&mut env, ptr))
1757 .unwrap_or(INVALID_HANDLE);
1758 let mut args = Vec::with_capacity(ty.params().len());
1759 for (index, ty) in ty.params().iter().enumerate() {
1760 let val_ptr = arg_data_ptr + (index * WASM_VAL_SIZE) as i32;
1761 let Some(value) = read_wasm_val(&mut env, val_ptr, *ty) else {
1762 return insert(
1763 &mut env,
1764 WasmObject::Trap("invalid function argument".to_string()),
1765 );
1766 };
1767 args.push(value);
1768 }
1769 sync_memory_shadows_to_wasmer(&mut env);
1770 match func.call(&mut env, &args) {
1771 Ok(results) => {
1772 refresh_memory_shadows_from_wasmer(&mut env);
1773 let result_data_ptr = guest_ptr_with_offset(results_vec_ptr, WASM_VEC_DATA_OFFSET)
1774 .and_then(|ptr| read_i32(&mut env, ptr))
1775 .unwrap_or(INVALID_HANDLE);
1776 for (index, value) in results.iter().enumerate() {
1777 let val_ptr = result_data_ptr + (index * WASM_VAL_SIZE) as i32;
1778 write_wasm_val(&mut env, val_ptr, value);
1779 }
1780 INVALID_HANDLE
1781 }
1782 Err(err) => {
1783 refresh_memory_shadows_from_wasmer(&mut env);
1784 insert(&mut env, WasmObject::Trap(err.to_string()))
1785 }
1786 }
1787}
1788
1789fn allocate_wasm_val_vec_for_values(
1790 env: &mut FunctionEnvMut<WasmCapiEnv>,
1791 values: &[Value],
1792) -> Option<(i32, i32)> {
1793 let len = u32::try_from(values.len()).ok()?;
1794 let byte_len = values.len().checked_mul(WASM_VAL_SIZE)?;
1795
1796 if byte_len == 0 {
1797 let vec_ptr = allocate_guest_vec_header(env, len, 0)?;
1798 return Some((vec_ptr, 0));
1799 }
1800
1801 let mut data = GuestAllocation::new(env, byte_len)?;
1802 for (index, value) in values.iter().enumerate() {
1803 let offset = index.checked_mul(WASM_VAL_SIZE)?;
1804 if !data.write_wasm_val_at(offset, value) {
1805 return None;
1806 }
1807 }
1808
1809 let vec_ptr = data.allocate_vec_header(len)?;
1810 let data_ptr = data.into_raw();
1811 Some((vec_ptr, data_ptr))
1812}
1813
1814fn allocate_uninitialized_wasm_val_vec(
1815 env: &mut FunctionEnvMut<WasmCapiEnv>,
1816 len: usize,
1817) -> Option<(i32, i32)> {
1818 let len_u32 = u32::try_from(len).ok()?;
1819 let byte_len = len.checked_mul(WASM_VAL_SIZE)?;
1820
1821 if byte_len == 0 {
1822 let vec_ptr = allocate_guest_vec_header(env, len_u32, 0)?;
1823 return Some((vec_ptr, 0));
1824 }
1825
1826 let mut data = GuestAllocation::new(env, byte_len)?;
1827 let vec_ptr = data.allocate_vec_header(len_u32)?;
1828 let data_ptr = data.into_raw();
1829 Some((vec_ptr, data_ptr))
1830}
1831
1832fn trap_message_from_handle(env: &FunctionEnvMut<WasmCapiEnv>, trap_handle: i32) -> String {
1833 match env.data().state.get(trap_handle) {
1834 Some(WasmObject::Trap(message)) => message.clone(),
1835 _ => "WebAssembly import callback trapped".to_string(),
1836 }
1837}
1838
1839fn call_guest_wasm_callback(
1840 env: &mut FunctionEnvMut<WasmCapiEnv>,
1841 callback: u32,
1842 callback_env: i32,
1843 args_vec_ptr: i32,
1844 results_vec_ptr: i32,
1845) -> i32 {
1846 let Some(table) = env.data().table.clone() else {
1847 return insert(
1848 env,
1849 WasmObject::Trap("missing guest indirect function table".to_string()),
1850 );
1851 };
1852 let Some(elem) = table.get(&mut *env, callback) else {
1853 return insert(
1854 env,
1855 WasmObject::Trap("invalid guest WebAssembly callback pointer".to_string()),
1856 );
1857 };
1858 let func = match elem {
1859 Value::FuncRef(Some(func)) => func,
1860 _ => {
1861 return insert(
1862 env,
1863 WasmObject::Trap("invalid guest WebAssembly callback reference".to_string()),
1864 );
1865 }
1866 };
1867 match func.call(
1868 env,
1869 &[
1870 Value::I32(callback_env),
1871 Value::I32(args_vec_ptr),
1872 Value::I32(results_vec_ptr),
1873 ],
1874 ) {
1875 Ok(values) => match values.first() {
1876 Some(Value::I32(value)) => *value,
1877 Some(Value::I64(value)) => *value as i32,
1878 _ => INVALID_HANDLE,
1879 },
1880 Err(err) => insert(env, WasmObject::Trap(err.to_string())),
1881 }
1882}
1883
1884fn wasm_func_new_with_env(
1885 mut env: FunctionEnvMut<WasmCapiEnv>,
1886 _store: i32,
1887 type_handle: i32,
1888 callback: i32,
1889 callback_env: i32,
1890 _finalizer: i32,
1891) -> i32 {
1892 if callback <= INVALID_HANDLE {
1893 return INVALID_HANDLE;
1894 }
1895 let ty = match env.data().state.get(type_handle) {
1896 Some(WasmObject::FuncType(ty)) => ty.clone(),
1897 _ => return INVALID_HANDLE,
1898 };
1899 let Some(func_env) = env.data().func_env.clone() else {
1900 return INVALID_HANDLE;
1901 };
1902 let Ok(callback) = u32::try_from(callback) else {
1903 return INVALID_HANDLE;
1904 };
1905 let result_types = ty.results().to_vec();
1906 let func = WasmerFunction::new_with_env(&mut env, &func_env, ty, move |mut env, args| {
1907 let Some((args_vec_ptr, _args_data_ptr)) = allocate_wasm_val_vec_for_values(&mut env, args)
1908 else {
1909 return Err(RuntimeError::new(
1910 "failed to allocate WebAssembly callback arguments",
1911 ));
1912 };
1913 let Some((results_vec_ptr, results_data_ptr)) =
1914 allocate_uninitialized_wasm_val_vec(&mut env, result_types.len())
1915 else {
1916 return Err(RuntimeError::new(
1917 "failed to allocate WebAssembly callback results",
1918 ));
1919 };
1920
1921 let trap = call_guest_wasm_callback(
1922 &mut env,
1923 callback,
1924 callback_env,
1925 args_vec_ptr,
1926 results_vec_ptr,
1927 );
1928 if trap != INVALID_HANDLE {
1929 return Err(RuntimeError::new(trap_message_from_handle(&env, trap)));
1930 }
1931
1932 let mut results = Vec::with_capacity(result_types.len());
1933 for (index, ty) in result_types.iter().enumerate() {
1934 let val_ptr = results_data_ptr + (index * WASM_VAL_SIZE) as i32;
1935 let Some(value) = read_wasm_val(&mut env, val_ptr, *ty) else {
1936 return Err(RuntimeError::new(
1937 "guest WebAssembly callback returned an invalid value",
1938 ));
1939 };
1940 results.push(value);
1941 }
1942 Ok(results)
1943 });
1944 insert(&mut env, WasmObject::Func(func))
1945}
1946
1947fn wasm_table_size(env: FunctionEnvMut<WasmCapiEnv>, table_handle: i32) -> i32 {
1948 match env.data().state.get(table_handle) {
1949 Some(WasmObject::Table(table)) => i32::try_from(table.size(&env)).unwrap_or(INVALID_SIZE),
1950 Some(WasmObject::Extern(WasmExtern::Table(table))) => {
1951 i32::try_from(table.size(&env)).unwrap_or(INVALID_SIZE)
1952 }
1953 _ => INVALID_SIZE,
1954 }
1955}
1956
1957fn wasm_table_grow(
1958 mut env: FunctionEnvMut<WasmCapiEnv>,
1959 table_handle: i32,
1960 delta: i32,
1961 _init: i32,
1962) -> i32 {
1963 let Ok(delta) = u32::try_from(delta) else {
1964 return BOOL_FALSE;
1965 };
1966 let table = match env.data().state.get(table_handle) {
1967 Some(WasmObject::Table(table)) => table.clone(),
1968 Some(WasmObject::Extern(WasmExtern::Table(table))) => table.clone(),
1969 _ => return BOOL_FALSE,
1970 };
1971 if table.grow(&mut env, delta, Value::FuncRef(None)).is_ok() {
1972 BOOL_TRUE
1973 } else {
1974 BOOL_FALSE
1975 }
1976}
1977
1978fn wasm_trap_new(mut env: FunctionEnvMut<WasmCapiEnv>, _store: i32, message_ptr: i32) -> i32 {
1979 let bytes = read_byte_vec(&mut env, message_ptr).unwrap_or_default();
1980 let mut message = String::from_utf8_lossy(&bytes).to_string();
1981 if message.ends_with('\0') {
1982 message.pop();
1983 }
1984 insert(&mut env, WasmObject::Trap(message))
1985}
1986
1987fn wasm_trap_message(mut env: FunctionEnvMut<WasmCapiEnv>, trap_handle: i32, out_ptr: i32) {
1988 let message = match env.data().state.get(trap_handle) {
1989 Some(WasmObject::Trap(message)) => message.clone(),
1990 _ => "WebAssembly trap".to_string(),
1991 };
1992 let mut bytes = message.into_bytes();
1993 bytes.push(0);
1994 write_byte_vec(&mut env, out_ptr, &bytes);
1995}
1996
1997fn wasm_byte_vec_new(mut env: FunctionEnvMut<WasmCapiEnv>, out_ptr: i32, size: i32, data_ptr: i32) {
1998 if out_ptr <= INVALID_HANDLE || size < 0 || data_ptr < 0 {
1999 return;
2000 }
2001 let Ok(size) = usize::try_from(size) else {
2002 return;
2003 };
2004 let bytes = if size == 0 {
2005 Vec::new()
2006 } else {
2007 let Some(bytes) = read_guest_bytes(&mut env, data_ptr, size) else {
2008 return;
2009 };
2010 bytes
2011 };
2012 write_byte_vec(&mut env, out_ptr, &bytes);
2013}
2014
2015fn wasm_val_vec_new_uninitialized(mut env: FunctionEnvMut<WasmCapiEnv>, out_ptr: i32, size: i32) {
2016 if out_ptr <= INVALID_HANDLE || size < 0 {
2017 return;
2018 }
2019 let Ok(len) = u32::try_from(size) else {
2020 return;
2021 };
2022 let Ok(size) = usize::try_from(size) else {
2023 return;
2024 };
2025 let Some(byte_len) = size.checked_mul(WASM_VAL_SIZE) else {
2026 return;
2027 };
2028
2029 if byte_len == 0 {
2030 let Some(out_ptr) = non_null_guest_ptr(out_ptr) else {
2031 return;
2032 };
2033 write_guest_vec_header(&mut env, out_ptr.get(), len, 0);
2034 return;
2035 }
2036
2037 let Some(mut data) = GuestAllocation::new(&mut env, byte_len) else {
2038 return;
2039 };
2040 let Some(out_ptr) = non_null_guest_ptr(out_ptr) else {
2041 return;
2042 };
2043 if data.write_vec_header(out_ptr, len) {
2044 let _ = data.into_raw();
2045 }
2046}
2047
2048fn noop_delete(_env: FunctionEnvMut<WasmCapiEnv>, _handle: i32) {}
2049
2050fn vec_delete(mut env: FunctionEnvMut<WasmCapiEnv>, vec_ptr: i32) {
2051 if let Some(vec_ptr) = non_null_guest_ptr(vec_ptr) {
2052 write_guest_vec_header(&mut env, vec_ptr.get(), 0, 0);
2053 }
2054}
2055
2056fn register_wasm_c_api_imports(
2057 store: &mut impl wasmer_api::AsStoreMut,
2058 fe: &FunctionEnv<WasmCapiEnv>,
2059 io: &mut Imports,
2060) {
2061 fe.as_mut(&mut *store).func_env = Some(fe.clone());
2062
2063 let ns = namespace! {
2064 "wasm_engine_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_engine_new),
2065 "wasm_engine_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2066 "wasm_store_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_store_new),
2067 "wasm_store_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2068 "wasm_module_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_module_new),
2069 "wasm_module_validate" => WasmerFunction::new_typed_with_env(store, fe, wasm_module_validate),
2070 "wasm_module_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2071 "wasm_module_imports" => WasmerFunction::new_typed_with_env(store, fe, wasm_module_imports),
2072 "wasm_module_exports" => WasmerFunction::new_typed_with_env(store, fe, wasm_module_exports),
2073 "wasm_importtype_module" => WasmerFunction::new_typed_with_env(store, fe, wasm_importtype_module),
2074 "wasm_importtype_name" => WasmerFunction::new_typed_with_env(store, fe, wasm_importtype_name),
2075 "wasm_importtype_type" => WasmerFunction::new_typed_with_env(store, fe, wasm_importtype_type),
2076 "wasm_importtype_vec_delete" => WasmerFunction::new_typed_with_env(store, fe, vec_delete),
2077 "wasm_exporttype_name" => WasmerFunction::new_typed_with_env(store, fe, wasm_exporttype_name),
2078 "wasm_exporttype_type" => WasmerFunction::new_typed_with_env(store, fe, wasm_exporttype_type),
2079 "wasm_exporttype_vec_delete" => WasmerFunction::new_typed_with_env(store, fe, vec_delete),
2080 "wasm_externtype_kind" => WasmerFunction::new_typed_with_env(store, fe, wasm_externtype_kind),
2081 "wasm_externtype_as_functype_const" => WasmerFunction::new_typed_with_env(store, fe, wasm_externtype_as_functype_const),
2082 "wasm_functype_copy" => WasmerFunction::new_typed_with_env(store, fe, wasm_functype_copy),
2083 "wasm_functype_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2084 "wasm_functype_params" => WasmerFunction::new_typed_with_env(store, fe, wasm_functype_params),
2085 "wasm_functype_results" => WasmerFunction::new_typed_with_env(store, fe, wasm_functype_results),
2086 "wasm_valtype_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_valtype_new),
2087 "wasm_valtype_kind" => WasmerFunction::new_typed_with_env(store, fe, wasm_valtype_kind),
2088 "wasm_val_delete" => WasmerFunction::new_typed_with_env(store, fe, noop_delete),
2089 "wasm_val_vec_new_uninitialized" => WasmerFunction::new_typed_with_env(store, fe, wasm_val_vec_new_uninitialized),
2090 "wasm_val_vec_delete" => WasmerFunction::new_typed_with_env(store, fe, vec_delete),
2091 "wasm_memorytype_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_memorytype_new),
2092 "wasm_memorytype_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2093 "wasm_memory_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_memory_new),
2094 "wasm_memory_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2095 "wasm_memory_copy" => WasmerFunction::new_typed_with_env(store, fe, wasm_memory_copy),
2096 "wasm_memory_size" => WasmerFunction::new_typed_with_env(store, fe, wasm_memory_size),
2097 "wasm_memory_grow" => WasmerFunction::new_typed_with_env(store, fe, wasm_memory_grow),
2098 "wasm_memory_data" => WasmerFunction::new_typed_with_env(store, fe, wasm_memory_data),
2099 "wasm_memory_data_size" => WasmerFunction::new_typed_with_env(store, fe, wasm_memory_data_size),
2100 "wasm_memory_as_extern" => WasmerFunction::new_typed_with_env(store, fe, wasm_memory_as_extern),
2101 "wasm_globaltype_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_globaltype_new),
2102 "wasm_globaltype_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2103 "wasm_globaltype_content" => WasmerFunction::new_typed_with_env(store, fe, wasm_globaltype_content),
2104 "wasm_globaltype_mutability" => WasmerFunction::new_typed_with_env(store, fe, wasm_globaltype_mutability),
2105 "wasm_global_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_global_new),
2106 "wasm_global_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2107 "wasm_global_copy" => WasmerFunction::new_typed_with_env(store, fe, wasm_global_copy),
2108 "wasm_global_type" => WasmerFunction::new_typed_with_env(store, fe, wasm_global_type),
2109 "wasm_global_get" => WasmerFunction::new_typed_with_env(store, fe, wasm_global_get),
2110 "wasm_global_set" => WasmerFunction::new_typed_with_env(store, fe, wasm_global_set),
2111 "wasm_global_as_extern" => WasmerFunction::new_typed_with_env(store, fe, wasm_global_as_extern),
2112 "wasm_table_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2113 "wasm_table_copy" => WasmerFunction::new_typed_with_env(store, fe, wasm_table_copy),
2114 "wasm_table_size" => WasmerFunction::new_typed_with_env(store, fe, wasm_table_size),
2115 "wasm_table_grow" => WasmerFunction::new_typed_with_env(store, fe, wasm_table_grow),
2116 "wasm_table_as_extern" => WasmerFunction::new_typed_with_env(store, fe, wasm_table_as_extern),
2117 "wasm_instance_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_instance_new),
2118 "wasm_instance_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2119 "wasm_instance_exports" => WasmerFunction::new_typed_with_env(store, fe, wasm_instance_exports),
2120 "wasm_extern_kind" => WasmerFunction::new_typed_with_env(store, fe, wasm_extern_kind),
2121 "wasm_extern_as_func" => WasmerFunction::new_typed_with_env(store, fe, wasm_extern_as_func),
2122 "wasm_extern_as_global" => WasmerFunction::new_typed_with_env(store, fe, wasm_extern_as_global),
2123 "wasm_extern_as_table" => WasmerFunction::new_typed_with_env(store, fe, wasm_extern_as_table),
2124 "wasm_extern_as_memory" => WasmerFunction::new_typed_with_env(store, fe, wasm_extern_as_memory),
2125 "wasm_extern_vec_delete" => WasmerFunction::new_typed_with_env(store, fe, vec_delete),
2126 "wasm_func_copy" => WasmerFunction::new_typed_with_env(store, fe, wasm_func_copy),
2127 "wasm_func_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2128 "wasm_func_as_extern" => WasmerFunction::new_typed_with_env(store, fe, wasm_func_as_extern),
2129 "wasm_func_type" => WasmerFunction::new_typed_with_env(store, fe, wasm_func_type),
2130 "wasm_func_call" => WasmerFunction::new_typed_with_env(store, fe, wasm_func_call),
2131 "wasm_func_new_with_env" => WasmerFunction::new_typed_with_env(store, fe, wasm_func_new_with_env),
2132 "wasm_trap_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_trap_new),
2133 "wasm_trap_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2134 "wasm_trap_message" => WasmerFunction::new_typed_with_env(store, fe, wasm_trap_message),
2135 "wasm_byte_vec_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_byte_vec_new),
2136 "wasm_byte_vec_delete" => WasmerFunction::new_typed_with_env(store, fe, vec_delete),
2137 };
2138
2139 io.register_namespace(WASM_C_API_MODULE_NAME, ns);
2140}
2141
2142#[cfg(test)]
2143mod tests {
2144 use super::{
2145 INVALID_HANDLE, INVALID_SIZE, MemoryShadow, Type, WASM_EXTERNREF, WASM_F64, WASM_FUNCREF,
2146 WASM_I32, WasmCAPIVersion, WasmCapiEnv, WasmCapiState, WasmObject,
2147 copy_guest_memory_to_wasmer, copy_wasmer_memory_to_guest, guest_byte_ptr,
2148 guest_memory_offset, guest_ptr_with_offset, module_wasm_c_api_version_used,
2149 non_null_guest_ptr, refresh_memory_shadows_from_wasmer, sync_memory_shadows_to_wasmer,
2150 type_to_wasm_kind, type_to_wasm_valkind, wasm_kind_to_type, wasm_memory_data,
2151 wasm_memory_data_size, wasm_memory_size, wasm_table_size, write_wasm_val,
2152 };
2153 use wasmer_api::{
2154 FunctionEnv, Memory, MemoryType, Module, Pages, Store, Table, TableType, Value,
2155 };
2156 use wat::parse_str;
2157
2158 #[cfg(feature = "wasi")]
2159 use super::WasmCapiRuntimeHooks;
2160 #[cfg(feature = "wasi")]
2161 use std::sync::{
2162 Arc,
2163 atomic::{AtomicUsize, Ordering},
2164 };
2165 #[cfg(feature = "wasi")]
2166 use wasmer_types::ModuleHash;
2167 #[cfg(feature = "wasi")]
2168 use wasmer_wasix::{
2169 PluggableRuntime, WasiError,
2170 runners::wasi::{RuntimeOrEngine, WasiRunner},
2171 runtime::task_manager::tokio::TokioTaskManager,
2172 };
2173
2174 const EMPTY_WASM_MODULE: &[u8] = b"\0asm\x01\0\0\0";
2175
2176 fn compile_wat(store: &Store, wat: &str) -> Module {
2177 let wasm = parse_str(wat).expect("wat module parses");
2178 Module::new(store, wasm).expect("wat module compiles")
2179 }
2180
2181 #[test]
2182 fn module_wasm_c_api_version_used_detects_none() {
2183 let store = Store::default();
2184 let module = Module::new(&store, EMPTY_WASM_MODULE).expect("empty wasm module compiles");
2185
2186 assert_eq!(module_wasm_c_api_version_used(&module), None);
2187 }
2188
2189 #[test]
2190 fn module_wasm_c_api_version_used_detects_v0() {
2191 let store = Store::default();
2192 let module = compile_wat(
2193 &store,
2194 r#"(module
2195 (import "wasm_c_api_v0" "wasm_engine_new" (func))
2196 )"#,
2197 );
2198
2199 assert_eq!(
2200 module_wasm_c_api_version_used(&module),
2201 Some(WasmCAPIVersion::V0)
2202 );
2203 }
2204
2205 #[test]
2206 fn module_wasm_c_api_version_used_detects_unknown_version() {
2207 let store = Store::default();
2208 let module = compile_wat(
2209 &store,
2210 r#"(module
2211 (import "wasm_c_api_v1" "wasm_engine_new" (func))
2212 )"#,
2213 );
2214
2215 assert_eq!(
2216 module_wasm_c_api_version_used(&module),
2217 Some(WasmCAPIVersion::Unknown)
2218 );
2219 }
2220
2221 #[test]
2222 fn module_wasm_c_api_version_used_detects_mixed_versions() {
2223 let store = Store::default();
2224 let module = compile_wat(
2225 &store,
2226 r#"(module
2227 (import "wasm_c_api_v0" "wasm_engine_new" (func))
2228 (import "wasm_c_api_v1" "wasm_store_new" (func))
2229 )"#,
2230 );
2231
2232 assert_eq!(
2233 module_wasm_c_api_version_used(&module),
2234 Some(WasmCAPIVersion::Unknown)
2235 );
2236 }
2237
2238 #[test]
2239 fn wasm_c_api_version_compatibility_is_strict() {
2240 assert!(WasmCAPIVersion::V0.is_compatible_with(WasmCAPIVersion::V0));
2241 assert!(!WasmCAPIVersion::V0.is_compatible_with(WasmCAPIVersion::Unknown));
2242 assert!(!WasmCAPIVersion::Unknown.is_compatible_with(WasmCAPIVersion::V0));
2243 assert!(!WasmCAPIVersion::Unknown.is_compatible_with(WasmCAPIVersion::Unknown));
2244 }
2245
2246 #[cfg(feature = "wasi")]
2247 #[test]
2248 fn wasm_c_api_imports_run_from_wasix_guest() {
2249 let wasm = parse_str(
2250 r#"(module
2251 (import "wasi_snapshot_preview1" "proc_exit" (func $proc_exit (param i32)))
2252 (import "wasm_c_api_v0" "wasm_engine_new" (func $wasm_engine_new (result i32)))
2253 (import "wasm_c_api_v0" "wasm_store_new" (func $wasm_store_new (param i32) (result i32)))
2254 (import "wasm_c_api_v0" "wasm_module_validate" (func $wasm_module_validate (param i32 i32) (result i32)))
2255 (import "wasm_c_api_v0" "wasm_module_new" (func $wasm_module_new (param i32 i32) (result i32)))
2256
2257 (memory (export "memory") 1)
2258 ;; wasm_byte_vec_t { size: 8, data: 32 }
2259 (data (i32.const 16) "\08\00\00\00\20\00\00\00")
2260 ;; Empty wasm module: \0asm + version 1.
2261 (data (i32.const 32) "\00asm\01\00\00\00")
2262
2263 (func (export "_start")
2264 (local $engine i32)
2265 (local $store i32)
2266 (local $module i32)
2267
2268 (local.set $engine (call $wasm_engine_new))
2269 (if (i32.eqz (local.get $engine))
2270 (then (call $proc_exit (i32.const 10))))
2271
2272 (local.set $store (call $wasm_store_new (local.get $engine)))
2273 (if (i32.eqz (local.get $store))
2274 (then (call $proc_exit (i32.const 11))))
2275
2276 (if (i32.eqz (call $wasm_module_validate (local.get $store) (i32.const 16)))
2277 (then (call $proc_exit (i32.const 12))))
2278
2279 (local.set $module (call $wasm_module_new (local.get $store) (i32.const 16)))
2280 (if (i32.eqz (local.get $module))
2281 (then (call $proc_exit (i32.const 13))))
2282 )
2283 )"#,
2284 )
2285 .expect("guest wat parses");
2286 let store = Store::default();
2287 let module = Module::new(&store, &wasm).expect("guest module compiles");
2288
2289 let tokio_runtime = tokio::runtime::Builder::new_multi_thread()
2290 .enable_all()
2291 .build()
2292 .expect("tokio runtime starts");
2293 let _guard = tokio_runtime.enter();
2294 let mut runtime = PluggableRuntime::new(Arc::new(TokioTaskManager::new(
2295 tokio_runtime.handle().clone(),
2296 )));
2297 runtime.set_engine(store.engine().clone());
2298
2299 let resolve_calls = Arc::new(AtomicUsize::new(0));
2300 let resolve_engine = store.engine().clone();
2301 let hooks = WasmCapiRuntimeHooks::new().with_resolve_module_sync({
2302 let resolve_calls = resolve_calls.clone();
2303 move |bytes| {
2304 resolve_calls.fetch_add(1, Ordering::SeqCst);
2305 Ok(Module::new(&resolve_engine, bytes)?)
2306 }
2307 });
2308 runtime
2309 .with_additional_imports({
2310 let hooks = hooks.clone();
2311 move |module, store| hooks.additional_imports(module, store)
2312 })
2313 .with_instance_setup(move |module, store, instance, imported_memory| {
2314 hooks.configure_instance(module, store, instance, imported_memory)
2315 });
2316
2317 let result = WasiRunner::new().run_wasm(
2318 RuntimeOrEngine::Runtime(Arc::new(runtime)),
2319 "wasm-c-api-smoke",
2320 module,
2321 ModuleHash::new(&wasm),
2322 );
2323
2324 match result {
2325 Ok(()) => {}
2326 Err(err) => {
2327 if let Some(WasiError::Exit(code)) = err.downcast_ref::<WasiError>() {
2328 panic!("guest exited with status {code}");
2329 }
2330 panic!("guest failed: {err:?}");
2331 }
2332 }
2333 assert_eq!(resolve_calls.load(Ordering::SeqCst), 1);
2334 }
2335
2336 #[test]
2337 fn wasm_capi_state_allocates_positive_non_reused_handles() {
2338 let mut state = WasmCapiState::default();
2339
2340 let first = state.insert(WasmObject::Engine);
2341 assert_eq!(first, 1);
2342 assert!(state.get(first).is_some());
2343
2344 let _ = state.remove(first);
2345 assert!(state.get(first).is_none());
2346
2347 let second = state.insert(WasmObject::Store);
2348 assert_eq!(second, 2);
2349 assert!(state.get(INVALID_HANDLE).is_none());
2350 assert!(state.get(-1).is_none());
2351 }
2352
2353 #[test]
2354 fn wasm_capi_state_fails_closed_on_handle_exhaustion() {
2355 let mut state = WasmCapiState {
2356 next_handle: i32::MAX,
2357 ..WasmCapiState::default()
2358 };
2359
2360 assert_eq!(state.insert(WasmObject::Engine), i32::MAX);
2361 assert_eq!(state.insert(WasmObject::Store), INVALID_HANDLE);
2362 }
2363
2364 #[test]
2365 fn guest_pointer_helpers_reject_invalid_values() {
2366 assert_eq!(guest_memory_offset(-1), None);
2367 assert_eq!(guest_memory_offset(0), Some(0));
2368 assert_eq!(guest_byte_ptr(-1), None);
2369 assert_eq!(guest_byte_ptr(1).map(|ptr| ptr.offset()), Some(1));
2370 assert_eq!(non_null_guest_ptr(0), None);
2371 assert_eq!(non_null_guest_ptr(1).map(|ptr| ptr.get()), Some(1));
2372 assert_eq!(guest_ptr_with_offset(i32::MAX, 4), None);
2373 }
2374
2375 #[test]
2376 fn size_apis_distinguish_invalid_from_zero() {
2377 let mut store = Store::default();
2378 let memory = Memory::new(&mut store, MemoryType::new(Pages(0), Some(Pages(1)), false))
2379 .expect("zero-page memory can be created");
2380 let table = Table::new(
2381 &mut store,
2382 TableType::new(Type::FuncRef, 0, Some(1)),
2383 Value::FuncRef(None),
2384 )
2385 .expect("zero-sized table can be created");
2386 let func_env = FunctionEnv::new(&mut store, WasmCapiEnv::default());
2387 let (memory_handle, table_handle) = {
2388 let env = func_env.as_mut(&mut store);
2389 let memory_handle = env.state.insert(WasmObject::Memory(memory));
2390 let table_handle = env.state.insert(WasmObject::Table(table));
2391 (memory_handle, table_handle)
2392 };
2393
2394 assert_eq!(
2395 wasm_memory_size(func_env.clone().into_mut(&mut store), INVALID_HANDLE),
2396 INVALID_SIZE
2397 );
2398 assert_eq!(
2399 wasm_memory_data_size(func_env.clone().into_mut(&mut store), INVALID_HANDLE),
2400 INVALID_SIZE
2401 );
2402 assert_eq!(
2403 wasm_table_size(func_env.clone().into_mut(&mut store), INVALID_HANDLE),
2404 INVALID_SIZE
2405 );
2406 assert_eq!(
2407 wasm_memory_size(func_env.clone().into_mut(&mut store), memory_handle),
2408 0
2409 );
2410 assert_eq!(
2411 wasm_memory_data_size(func_env.clone().into_mut(&mut store), memory_handle),
2412 0
2413 );
2414 assert_eq!(
2415 wasm_table_size(func_env.into_mut(&mut store), table_handle),
2416 0
2417 );
2418 }
2419
2420 #[test]
2421 fn shared_memory_data_shadow_fails_closed() {
2422 let mut store = Store::default();
2423 let memory = Memory::new(&mut store, MemoryType::new(Pages(1), Some(Pages(2)), true))
2424 .expect("shared memory can be created");
2425 let func_env = FunctionEnv::new(&mut store, WasmCapiEnv::default());
2426 let memory_handle = func_env
2427 .as_mut(&mut store)
2428 .state
2429 .insert(WasmObject::Memory(memory));
2430
2431 assert_eq!(
2432 wasm_memory_data_size(func_env.clone().into_mut(&mut store), memory_handle),
2433 INVALID_SIZE
2434 );
2435 assert_eq!(
2436 wasm_memory_data(func_env.into_mut(&mut store), memory_handle),
2437 INVALID_HANDLE
2438 );
2439 }
2440
2441 #[test]
2442 fn memory_shadow_copy_rejects_same_memory() {
2443 let mut store = Store::default();
2444 let memory = Memory::new(&mut store, MemoryType::new(Pages(1), Some(Pages(1)), false))
2445 .expect("memory can be created");
2446 let func_env = FunctionEnv::new(
2447 &mut store,
2448 WasmCapiEnv {
2449 memory: Some(memory.clone()),
2450 ..WasmCapiEnv::default()
2451 },
2452 );
2453 let mut env = func_env.into_mut(&mut store);
2454
2455 assert!(!copy_wasmer_memory_to_guest(&mut env, &memory, 16, 4));
2456 assert!(!copy_guest_memory_to_wasmer(&mut env, 16, &memory, 4));
2457 }
2458
2459 #[test]
2460 fn memory_shadow_sync_preserves_registered_shadows() {
2461 let mut store = Store::default();
2462 let guest_memory =
2463 Memory::new(&mut store, MemoryType::new(Pages(1), Some(Pages(1)), false))
2464 .expect("guest memory can be created");
2465 let memory = Memory::new(&mut store, MemoryType::new(Pages(1), Some(Pages(1)), false))
2466 .expect("memory can be created");
2467 guest_memory
2468 .view(&store)
2469 .write(16, &[1, 2, 3, 4])
2470 .expect("guest shadow write succeeds");
2471 let func_env = FunctionEnv::new(
2472 &mut store,
2473 WasmCapiEnv {
2474 memory: Some(guest_memory.clone()),
2475 ..WasmCapiEnv::default()
2476 },
2477 );
2478 let memory_handle = {
2479 let env = func_env.as_mut(&mut store);
2480 let memory_handle = env.state.insert(WasmObject::Memory(memory.clone()));
2481 env.state.memory_shadows.insert(
2482 memory_handle,
2483 MemoryShadow {
2484 guest_ptr: 16,
2485 len: 4,
2486 },
2487 );
2488 memory_handle
2489 };
2490
2491 {
2492 let mut env = func_env.clone().into_mut(&mut store);
2493 sync_memory_shadows_to_wasmer(&mut env);
2494 assert!(env.data().state.memory_shadows.contains_key(&memory_handle));
2495 }
2496 let mut bytes = [0; 4];
2497 memory
2498 .view(&store)
2499 .read(0, &mut bytes)
2500 .expect("memory read succeeds");
2501 assert_eq!(bytes, [1, 2, 3, 4]);
2502
2503 memory
2504 .view(&store)
2505 .write(0, &[5, 6, 7, 8])
2506 .expect("memory write succeeds");
2507 {
2508 let mut env = func_env.into_mut(&mut store);
2509 refresh_memory_shadows_from_wasmer(&mut env);
2510 assert!(env.data().state.memory_shadows.contains_key(&memory_handle));
2511 }
2512 guest_memory
2513 .view(&store)
2514 .read(16, &mut bytes)
2515 .expect("guest shadow read succeeds");
2516 assert_eq!(bytes, [5, 6, 7, 8]);
2517 }
2518
2519 #[test]
2520 fn wasm_kind_conversion_is_explicit_for_supported_types() {
2521 assert_eq!(type_to_wasm_kind(Type::I32), Some(WASM_I32));
2522 assert_eq!(type_to_wasm_kind(Type::F64), Some(WASM_F64));
2523 assert_eq!(type_to_wasm_kind(Type::ExternRef), Some(WASM_EXTERNREF));
2524 assert_eq!(type_to_wasm_kind(Type::FuncRef), Some(WASM_FUNCREF));
2525 assert_eq!(type_to_wasm_kind(Type::V128), None);
2526 assert_eq!(type_to_wasm_kind(Type::ExceptionRef), None);
2527 assert_eq!(type_to_wasm_valkind(Type::I32), Some(WASM_I32));
2528 assert_eq!(type_to_wasm_valkind(Type::FuncRef), None);
2529 assert_eq!(type_to_wasm_valkind(Type::ExternRef), None);
2530
2531 assert_eq!(wasm_kind_to_type(i32::from(WASM_I32)), Some(Type::I32));
2532 assert_eq!(
2533 wasm_kind_to_type(i32::from(WASM_EXTERNREF)),
2534 Some(Type::ExternRef)
2535 );
2536 assert_eq!(wasm_kind_to_type(-1), None);
2537 }
2538
2539 #[test]
2540 fn unsupported_ref_values_are_not_marshalled() {
2541 let mut store = Store::default();
2542 let func_env = FunctionEnv::new(&mut store, WasmCapiEnv::default());
2543
2544 assert!(!write_wasm_val(
2545 &mut func_env.clone().into_mut(&mut store),
2546 1,
2547 &Value::FuncRef(None)
2548 ));
2549 assert!(!write_wasm_val(
2550 &mut func_env.into_mut(&mut store),
2551 1,
2552 &Value::ExternRef(None)
2553 ));
2554 }
2555}