1use anyhow::{Context, Result, bail};
2use std::{
3 collections::HashMap, fmt::Display, mem::size_of, num::NonZeroI32, ptr, slice, sync::Arc,
4};
5
6use wasmer_api::{
7 Extern, ExternRef, ExternType, Function, Function as WasmerFunction, FunctionEnv,
8 FunctionEnvMut, FunctionType, Global, GlobalType, Imports, Instance, Memory, Memory32,
9 MemoryType, Module, Mutability, Pages, RuntimeError, StoreMut, Table, TableType, Type,
10 TypedFunction, Value, WasmPtr, namespace,
11};
12
13#[cfg(feature = "wasix")]
14mod wasix;
15
16pub const WASM_C_API_MODULE_NAME: &str = "wasm_c_api_v0";
18const WASM_C_API_MODULE_PREFIX: &str = "wasm_c_api_v";
19
20const INVALID_HANDLE: i32 = 0;
21const BOOL_FALSE: i32 = 0;
22const BOOL_TRUE: i32 = 1;
23const INVALID_KIND: i32 = -1;
24const INVALID_SIZE: i32 = -1;
25const WASM_VEC_SIZE: usize = 8;
26const WASM_VEC_DATA_OFFSET: i32 = 4;
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum WasmCAPIVersion {
31 V0,
33 Unknown,
35}
36
37impl Display for WasmCAPIVersion {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 match self {
40 WasmCAPIVersion::V0 => write!(f, "wasm_c_api_v0"),
41 WasmCAPIVersion::Unknown => write!(f, "wasm_c_api_unknown"),
42 }
43 }
44}
45
46impl WasmCAPIVersion {
47 pub const fn is_compatible_with(self, other: Self) -> bool {
49 matches!((self, other), (Self::V0, Self::V0))
50 }
51}
52
53pub fn module_wasm_c_api_version_used(module: &Module) -> Option<WasmCAPIVersion> {
55 let mut version = None;
56
57 for import in module.imports() {
58 let Some(detected_version) = wasm_c_api_version_from_namespace(import.module()) else {
59 continue;
60 };
61
62 version = Some(match version {
63 None => detected_version,
64 Some(existing) if existing == detected_version => existing,
65 Some(_) => WasmCAPIVersion::Unknown,
66 });
67 }
68
69 version
70}
71
72fn wasm_c_api_version_from_namespace(namespace: &str) -> Option<WasmCAPIVersion> {
73 if namespace == WASM_C_API_MODULE_NAME {
74 return Some(WasmCAPIVersion::V0);
75 }
76
77 let suffix = namespace.strip_prefix(WASM_C_API_MODULE_PREFIX)?;
78 Some(match suffix {
79 "0" => WasmCAPIVersion::V0,
80 _ => WasmCAPIVersion::Unknown,
81 })
82}
83
84type ResolveModuleSync = Arc<dyn Fn(Vec<u8>) -> Result<Module> + Send + Sync>;
85
86struct WasmCapiSession {
87 version: Option<WasmCAPIVersion>,
88 imported_memory_type: Option<MemoryType>,
89 imported_table_type: Option<wasmer_api::TableType>,
90 resolve_module_sync: Option<ResolveModuleSync>,
91 func_env: Option<FunctionEnv<WasmCapiEnv>>,
92}
93
94impl WasmCapiSession {
95 fn new(module: &Module, resolve_module_sync: Option<ResolveModuleSync>) -> Self {
96 let imported_memory_type = module.imports().find_map(|import| {
97 if import.module() == "env"
98 && import.name() == "memory"
99 && let ExternType::Memory(ty) = import.ty()
100 {
101 return Some(*ty);
102 }
103 None
104 });
105
106 let imported_table_type = module.imports().find_map(|import| {
107 if import.module() == "env"
108 && import.name() == "__indirect_function_table"
109 && let ExternType::Table(ty) = import.ty()
110 {
111 return Some(*ty);
112 }
113 None
114 });
115
116 Self {
117 version: module_wasm_c_api_version_used(module),
118 imported_memory_type,
119 imported_table_type,
120 resolve_module_sync,
121 func_env: None,
122 }
123 }
124
125 fn needs_imports(&self) -> bool {
126 self.version.is_some()
127 }
128
129 fn validate_supported(&self) -> Result<()> {
130 if let Some(version) = self.version
131 && !WasmCAPIVersion::V0.is_compatible_with(version)
132 {
133 bail!("unsupported Wasm C API import version: {version:?}");
134 }
135 Ok(())
136 }
137
138 fn register_imports(&mut self, store: &mut StoreMut<'_>, io: &mut Imports) -> Result<()> {
139 self.validate_supported()?;
140 if self.version.is_none() {
141 return Ok(());
142 }
143
144 let func_env = FunctionEnv::new(
145 &mut *store,
146 WasmCapiEnv {
147 resolve_module_sync: self.resolve_module_sync.clone(),
148 ..WasmCapiEnv::default()
149 },
150 );
151 self.func_env = Some(func_env.clone());
152 register_wasm_c_api_imports(store, &func_env, io);
153
154 if let Some(memory_type) = self.imported_memory_type {
155 if let Some(existing) = io.get_export("env", "memory") {
156 let Extern::Memory(memory) = existing else {
157 bail!("env.memory import for Wasm C API module is not a memory");
158 };
159 self.set_memory(store, &memory)?;
160 } else {
161 let memory = Memory::new(&mut *store, memory_type)?;
162 io.define("env", "memory", memory.clone());
163 self.set_memory(store, &memory)?;
164 }
165 }
166
167 if let Some(table_type) = self.imported_table_type {
168 if let Some(existing) = io.get_export("env", "__indirect_function_table") {
169 let Extern::Table(table) = existing else {
170 bail!(
171 "env.__indirect_function_table import for Wasm C API module is not a table"
172 );
173 };
174 self.set_table(store, &table)?;
175 } else {
176 let table = Table::new(&mut *store, table_type, Value::FuncRef(None))?;
177 io.define("env", "__indirect_function_table", table.clone());
178 self.set_table(store, &table)?;
179 }
180 }
181
182 Ok(())
183 }
184
185 fn set_memory(&self, store: &mut StoreMut<'_>, memory: &Memory) -> Result<()> {
186 let Some(func_env) = self.func_env()? else {
187 return Ok(());
188 };
189 func_env.as_mut(&mut *store).memory = Some(memory.clone());
190 Ok(())
191 }
192
193 fn set_table(&self, store: &mut StoreMut<'_>, table: &Table) -> Result<()> {
194 let Some(func_env) = self.func_env()? else {
195 return Ok(());
196 };
197 func_env.as_mut(&mut *store).table = Some(table.clone());
198 Ok(())
199 }
200
201 fn configure_instance(
202 &self,
203 store: &mut StoreMut<'_>,
204 instance: &Instance,
205 imported_memory: Option<&Memory>,
206 ) -> Result<()> {
207 let Some(func_env) = self.func_env()? else {
208 return Ok(());
209 };
210
211 if let Some(memory) = imported_memory {
212 func_env.as_mut(&mut *store).memory = Some(memory.clone());
213 } else if let Ok(memory) = instance.exports.get_memory("memory") {
214 func_env.as_mut(&mut *store).memory = Some(memory.clone());
215 }
216
217 for export_name in ["unofficial_napi_guest_malloc", "malloc"] {
218 if let Ok(malloc) = instance
219 .exports
220 .get_typed_function::<i32, i32>(&store, export_name)
221 {
222 func_env.as_mut(&mut *store).malloc_fn = Some(malloc);
223 break;
224 }
225 }
226
227 for export_name in ["unofficial_napi_guest_free", "free"] {
228 if let Ok(free) = instance
229 .exports
230 .get_typed_function::<i32, ()>(&store, export_name)
231 {
232 func_env.as_mut(&mut *store).free_fn = Some(free);
233 break;
234 }
235 }
236
237 if let Ok(table) = instance.exports.get_table("__indirect_function_table") {
238 func_env.as_mut(&mut *store).table = Some(table.clone());
239 }
240
241 Ok(())
242 }
243
244 fn func_env(&self) -> Result<Option<FunctionEnv<WasmCapiEnv>>> {
245 if self.version.is_none() {
246 return Ok(None);
247 }
248
249 self.func_env
250 .clone()
251 .context("missing Wasm C API function env during instance setup")
252 .map(Some)
253 }
254}
255
256pub struct WasmCapiInstantiationState {
263 session: Option<WasmCapiSession>,
264}
265
266#[derive(Clone, Default)]
273pub struct WasmCapiRuntimeHooks {
274 resolve_module_sync: Option<ResolveModuleSync>,
275}
276
277impl std::fmt::Debug for WasmCapiRuntimeHooks {
278 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
279 f.debug_struct("WasmCapiRuntimeHooks")
280 .field(
281 "resolve_module_sync",
282 &self.resolve_module_sync.as_ref().map(|_| ".."),
283 )
284 .finish()
285 }
286}
287
288impl WasmCapiRuntimeHooks {
289 pub fn new() -> Self {
291 Self::default()
292 }
293
294 pub fn with_resolve_module_sync(
296 mut self,
297 resolve: impl Fn(Vec<u8>) -> Result<Module> + Send + Sync + 'static,
298 ) -> Self {
299 self.resolve_module_sync = Some(Arc::new(resolve));
300 self
301 }
302
303 pub fn additional_imports(
305 &self,
306 module: &Module,
307 store: &mut StoreMut<'_>,
308 ) -> Result<(Imports, WasmCapiInstantiationState)> {
309 let mut imports = Imports::new();
310 let state = self.add_imports(module, store, &mut imports)?;
311 Ok((imports, state))
312 }
313
314 pub fn add_imports(
316 &self,
317 module: &Module,
318 store: &mut StoreMut<'_>,
319 imports: &mut Imports,
320 ) -> Result<WasmCapiInstantiationState> {
321 let mut session = WasmCapiSession::new(module, self.resolve_module_sync.clone());
322 if !session.needs_imports() {
323 return Ok(WasmCapiInstantiationState { session: None });
324 }
325
326 session.register_imports(store, imports)?;
327 Ok(WasmCapiInstantiationState {
328 session: Some(session),
329 })
330 }
331
332 pub fn configure_instance(
338 &self,
339 module: &Module,
340 store: &mut StoreMut<'_>,
341 instance: &Instance,
342 imported_memory: Option<&Memory>,
343 state: WasmCapiInstantiationState,
344 ) -> Result<()> {
345 if module_wasm_c_api_version_used(module).is_none() {
346 return Ok(());
347 }
348
349 let session = state.session.context(
350 "missing Wasm C API session for module instance setup \
351 (the state was not created for this module's imports)",
352 )?;
353 session.configure_instance(store, instance, imported_memory)
354 }
355}
356
357#[derive(Default)]
362struct WasmCapiEnv {
363 resolve_module_sync: Option<ResolveModuleSync>,
365 memory: Option<Memory>,
367 malloc_fn: Option<TypedFunction<i32, i32>>,
369 free_fn: Option<TypedFunction<i32, ()>>,
371 table: Option<Table>,
373 state: WasmCapiState,
375 func_env: Option<FunctionEnv<WasmCapiEnv>>,
377}
378
379const WASM_I32: u8 = 0;
382const WASM_I64: u8 = 1;
383const WASM_F32: u8 = 2;
384const WASM_F64: u8 = 3;
385const WASM_EXTERNREF: u8 = 128;
386const WASM_FUNCREF: u8 = 129;
387
388const WASM_EXTERN_FUNC: i32 = 0;
389const WASM_EXTERN_GLOBAL: i32 = 1;
390const WASM_EXTERN_TABLE: i32 = 2;
391const WASM_EXTERN_MEMORY: i32 = 3;
392
393const WASM_CONST: i32 = 0;
394const WASM_VAR: i32 = 1;
395
396const WASM_VAL_SIZE: usize = 16;
399const WASM_VAL_PAYLOAD_OFFSET: i32 = 8;
400
401#[derive(Clone)]
402enum WasmExtern {
403 Func(Function),
404 Global(Global),
405 Table(Table),
406 Memory(Memory),
407}
408
409#[derive(Clone)]
410enum WasmObject {
411 Engine,
412 Store,
413 Module(Module),
414 Instance(Instance),
415 Func(Function),
416 FuncType(FunctionType),
417 ValType(Type),
418 ExternType(ExternType),
419 ImportType {
420 module: String,
421 name: String,
422 ty: ExternType,
423 },
424 ExportType {
425 name: String,
426 ty: ExternType,
427 },
428 Extern(WasmExtern),
429 Memory(Memory),
430 MemoryType(MemoryType),
431 Global(Global),
432 GlobalType(GlobalType),
433 Table(Table),
434 TableType(TableType),
435 Ref(Value),
438 Trap(String),
439}
440
441#[derive(Clone, Copy)]
443struct MemoryShadow {
444 guest_ptr: i32,
446 len: usize,
448}
449
450struct WasmCapiState {
456 next_handle: i32,
458 objects: HashMap<i32, WasmObject>,
460 memory_shadows: HashMap<i32, MemoryShadow>,
462}
463
464impl Default for WasmCapiState {
465 fn default() -> Self {
466 Self {
467 next_handle: 1,
468 objects: HashMap::new(),
469 memory_shadows: HashMap::new(),
470 }
471 }
472}
473
474impl WasmCapiState {
475 fn insert(&mut self, object: WasmObject) -> i32 {
476 let handle = self.next_handle;
477 if handle <= INVALID_HANDLE {
478 return INVALID_HANDLE;
479 }
480
481 self.next_handle = handle.checked_add(1).unwrap_or(INVALID_HANDLE);
482 self.objects.insert(handle, object);
483 handle
484 }
485
486 fn get(&self, handle: i32) -> Option<&WasmObject> {
487 if handle <= INVALID_HANDLE {
488 return None;
489 }
490 self.objects.get(&handle)
491 }
492
493 fn remove(&mut self, handle: i32) -> Option<MemoryShadow> {
494 if handle > INVALID_HANDLE {
495 self.objects.remove(&handle);
496 return self.memory_shadows.remove(&handle);
497 }
498 None
499 }
500}
501
502fn guest_memory_offset(guest_ptr: i32) -> Option<u64> {
503 u64::try_from(guest_ptr).ok()
504}
505
506fn guest_byte_ptr(guest_ptr: i32) -> Option<WasmPtr<u8, Memory32>> {
507 Some(WasmPtr::new(u32::try_from(guest_ptr).ok()?))
508}
509
510fn checked_memory_offset(guest_ptr: i32, len: usize, data_size: u64) -> Option<usize> {
511 let offset = usize::try_from(guest_ptr).ok()?;
512 let end = offset.checked_add(len)?;
513 (u64::try_from(end).ok()? <= data_size).then_some(offset)
514}
515
516fn non_null_guest_ptr(guest_ptr: i32) -> Option<NonZeroI32> {
517 let ptr = NonZeroI32::new(guest_ptr)?;
518 (ptr.get() > INVALID_HANDLE).then_some(ptr)
519}
520
521fn write_guest_bytes(env: &mut FunctionEnvMut<WasmCapiEnv>, guest_ptr: i32, data: &[u8]) -> bool {
522 let Some(ptr) = guest_byte_ptr(guest_ptr) else {
523 return false;
524 };
525 let Ok(len) = u32::try_from(data.len()) else {
526 return false;
527 };
528 let (state, store) = env.data_and_store_mut();
529 let Some(memory) = state.memory.clone() else {
530 return false;
531 };
532 let view = memory.view(&store);
533 ptr.slice(&view, len)
534 .and_then(|slice| slice.write_slice(data))
535 .is_ok()
536}
537
538fn write_guest_u32(env: &mut FunctionEnvMut<WasmCapiEnv>, guest_ptr: i32, val: u32) -> bool {
539 write_guest_bytes(env, guest_ptr, &val.to_le_bytes())
540}
541
542fn write_guest_i32(env: &mut FunctionEnvMut<WasmCapiEnv>, guest_ptr: i32, val: i32) -> bool {
543 write_guest_bytes(env, guest_ptr, &val.to_le_bytes())
544}
545
546fn write_guest_i32_offset(
547 env: &mut FunctionEnvMut<WasmCapiEnv>,
548 guest_ptr: i32,
549 offset: i32,
550 val: i32,
551) -> bool {
552 let Some(ptr) = guest_ptr.checked_add(offset) else {
553 return false;
554 };
555 write_guest_i32(env, ptr, val)
556}
557
558fn write_guest_bytes_offset(
559 env: &mut FunctionEnvMut<WasmCapiEnv>,
560 guest_ptr: i32,
561 offset: i32,
562 data: &[u8],
563) -> bool {
564 let Some(ptr) = guest_ptr.checked_add(offset) else {
565 return false;
566 };
567 write_guest_bytes(env, ptr, data)
568}
569
570fn guest_ptr_with_offset(guest_ptr: i32, offset: i32) -> Option<i32> {
571 guest_ptr
572 .checked_add(offset)
573 .filter(|ptr| *ptr >= INVALID_HANDLE)
574}
575
576fn read_guest_bytes(
577 env: &mut FunctionEnvMut<WasmCapiEnv>,
578 guest_ptr: i32,
579 len: usize,
580) -> Option<Vec<u8>> {
581 let ptr = guest_byte_ptr(guest_ptr)?;
582 let len = u32::try_from(len).ok()?;
583 if len == 0 {
584 return Some(Vec::new());
585 }
586
587 let (state, store) = env.data_and_store_mut();
588 let memory = state.memory.clone()?;
589 let view = memory.view(&store);
590
591 ptr.slice(&view, len).ok()?.read_to_vec().ok()
592}
593
594fn read_guest_array<const N: usize>(
595 env: &mut FunctionEnvMut<WasmCapiEnv>,
596 guest_ptr: i32,
597) -> Option<[u8; N]> {
598 let ptr = guest_byte_ptr(guest_ptr)?;
599 let len = u32::try_from(N).ok()?;
600 let (state, store) = env.data_and_store_mut();
601 let memory = state.memory.clone()?;
602 let mut bytes = [0u8; N];
603 let view = memory.view(&store);
604 ptr.slice(&view, len).ok()?.read_slice(&mut bytes).ok()?;
605 Some(bytes)
606}
607
608fn read_guest_i32_vec(
609 env: &mut FunctionEnvMut<WasmCapiEnv>,
610 guest_ptr: i32,
611 len: usize,
612) -> Option<Vec<i32>> {
613 let offset = guest_memory_offset(guest_ptr)?;
614 if len == 0 {
615 return Some(Vec::new());
616 }
617 let byte_len = len.checked_mul(size_of::<i32>())?;
618
619 let (state, store) = env.data_and_store_mut();
620 let memory = state.memory.clone()?;
621 let view = memory.view(&store);
622
623 let mut out = Vec::<i32>::with_capacity(len);
624 unsafe {
625 let spare = out.spare_capacity_mut();
626 let bytes = slice::from_raw_parts_mut(spare.as_mut_ptr().cast::<u8>(), byte_len);
627 view.read(offset, bytes).ok()?;
628 out.set_len(len);
629 }
630
631 for value in &mut out {
634 *value = i32::from_le(*value);
635 }
636
637 Some(out)
638}
639
640fn write_guest_i32_slice(
641 env: &mut FunctionEnvMut<WasmCapiEnv>,
642 guest_ptr: i32,
643 values: &[i32],
644) -> bool {
645 let Some(byte_len) = values.len().checked_mul(size_of::<i32>()) else {
646 return false;
647 };
648 if byte_len == 0 {
649 return true;
650 }
651
652 for (index, value) in values.iter().enumerate() {
653 let Ok(index) = i32::try_from(index) else {
654 return false;
655 };
656 let Some(offset) = index.checked_mul(size_of::<i32>() as i32) else {
657 return false;
658 };
659 if !write_guest_bytes_offset(env, guest_ptr, offset, &value.to_le_bytes()) {
660 return false;
661 }
662 }
663 true
664}
665
666fn allocate_guest_memory(env: &mut FunctionEnvMut<WasmCapiEnv>, len: usize) -> Option<i32> {
667 if len == 0 {
668 return Some(0);
669 }
670
671 let malloc_fn = env.data().malloc_fn.clone()?;
672 let len = i32::try_from(len).ok()?;
673 let guest_ptr: i32 = {
674 let (_, mut store_ref) = env.data_and_store_mut();
675 malloc_fn.call(&mut store_ref, len).ok()?
676 };
677 if guest_ptr <= INVALID_HANDLE {
678 return None;
679 }
680 Some(guest_ptr)
681}
682
683fn free_guest_memory(env: &mut FunctionEnvMut<WasmCapiEnv>, guest_ptr: i32) {
684 if guest_ptr <= INVALID_HANDLE {
685 return;
686 }
687
688 let Some(free_fn) = env.data().free_fn.clone() else {
689 return;
690 };
691 let (_, mut store_ref) = env.data_and_store_mut();
692 let _ = free_fn.call(&mut store_ref, guest_ptr);
693}
694
695struct GuestAllocation<'env, 'store> {
696 env: &'env mut FunctionEnvMut<'store, WasmCapiEnv>,
697 ptr: i32,
698}
699
700impl<'env, 'store> GuestAllocation<'env, 'store> {
701 fn new(env: &'env mut FunctionEnvMut<'store, WasmCapiEnv>, len: usize) -> Option<Self> {
702 let ptr = allocate_guest_memory(env, len)?;
703 non_null_guest_ptr(ptr)?;
704 Some(Self { env, ptr })
705 }
706
707 fn write_bytes(&mut self, data: &[u8]) -> bool {
708 write_guest_bytes(self.env, self.ptr, data)
709 }
710
711 fn write_i32_slice(&mut self, values: &[i32]) -> bool {
712 write_guest_i32_slice(self.env, self.ptr, values)
713 }
714
715 fn write_wasm_val_at(&mut self, offset: usize, value: &Value) -> bool {
716 let Ok(offset) = i32::try_from(offset) else {
717 return false;
718 };
719 let Some(ptr) = self.ptr.checked_add(offset) else {
720 return false;
721 };
722 write_wasm_val(self.env, ptr, value)
723 }
724
725 fn write_vec_header(&mut self, vec_ptr: NonZeroI32, len: u32) -> bool {
726 write_guest_vec_header(self.env, vec_ptr.get(), len, self.ptr)
727 }
728
729 fn write_self_vec_header(&mut self, len: u32, data_ptr: i32) -> bool {
730 write_guest_vec_header(self.env, self.ptr, len, data_ptr)
731 }
732
733 fn write_handles(&mut self, handles: &[i32]) -> bool {
734 write_handle_vec(self.env, self.ptr, handles)
735 }
736
737 fn allocate_vec_header(&mut self, len: u32) -> Option<i32> {
738 allocate_guest_vec_header(self.env, len, self.ptr)
739 }
740
741 fn copy_from_wasmer_memory(&mut self, memory: &Memory, len: usize) -> bool {
742 copy_wasmer_memory_to_guest(self.env, memory, self.ptr, len)
743 }
744
745 fn into_raw(mut self) -> i32 {
746 let ptr = self.ptr;
747 self.ptr = INVALID_HANDLE;
748 ptr
749 }
750}
751
752impl Drop for GuestAllocation<'_, '_> {
753 fn drop(&mut self) {
754 if self.ptr != INVALID_HANDLE {
755 free_guest_memory(self.env, self.ptr);
756 }
757 }
758}
759
760fn read_u32(env: &mut FunctionEnvMut<WasmCapiEnv>, ptr: i32) -> Option<u32> {
761 Some(u32::from_le_bytes(read_guest_array(env, ptr)?))
762}
763
764fn read_i32(env: &mut FunctionEnvMut<WasmCapiEnv>, ptr: i32) -> Option<i32> {
765 Some(read_u32(env, ptr)? as i32)
766}
767
768fn read_u64(env: &mut FunctionEnvMut<WasmCapiEnv>, ptr: i32) -> Option<u64> {
769 Some(u64::from_le_bytes(read_guest_array(env, ptr)?))
770}
771
772fn write_guest_vec_header(
773 env: &mut FunctionEnvMut<WasmCapiEnv>,
774 vec_ptr: i32,
775 len: u32,
776 data_ptr: i32,
777) -> bool {
778 write_guest_u32(env, vec_ptr, len)
779 && write_guest_i32_offset(env, vec_ptr, WASM_VEC_DATA_OFFSET, data_ptr)
780}
781
782fn allocate_guest_vec_header(
783 env: &mut FunctionEnvMut<WasmCapiEnv>,
784 len: u32,
785 data_ptr: i32,
786) -> Option<i32> {
787 let mut allocation = GuestAllocation::new(env, WASM_VEC_SIZE)?;
788 if !allocation.write_self_vec_header(len, data_ptr) {
789 return None;
790 }
791 Some(allocation.into_raw())
792}
793
794fn read_byte_vec(env: &mut FunctionEnvMut<WasmCapiEnv>, vec_ptr: i32) -> Option<Vec<u8>> {
795 if vec_ptr <= INVALID_HANDLE {
796 return None;
797 }
798 let size = read_u32(env, vec_ptr)? as usize;
799 let data_ptr = read_i32(env, guest_ptr_with_offset(vec_ptr, WASM_VEC_DATA_OFFSET)?)?;
800 if size == 0 {
801 return Some(Vec::new());
802 }
803 read_guest_bytes(env, data_ptr, size)
804}
805
806fn write_byte_vec(env: &mut FunctionEnvMut<WasmCapiEnv>, vec_ptr: i32, bytes: &[u8]) -> bool {
807 let Some(vec_ptr) = non_null_guest_ptr(vec_ptr) else {
808 return false;
809 };
810 let Ok(len) = u32::try_from(bytes.len()) else {
811 return false;
812 };
813
814 if bytes.is_empty() {
815 return write_guest_vec_header(env, vec_ptr.get(), len, 0);
816 }
817
818 let Some(mut data) = GuestAllocation::new(env, bytes.len()) else {
819 return false;
820 };
821 if !data.write_bytes(bytes) || !data.write_vec_header(vec_ptr, len) {
822 return false;
823 }
824 let _ = data.into_raw();
825 true
826}
827
828fn allocate_name(env: &mut FunctionEnvMut<WasmCapiEnv>, name: &str) -> i32 {
829 let Ok(len) = u32::try_from(name.len()) else {
830 return INVALID_HANDLE;
831 };
832
833 if name.is_empty() {
834 return allocate_guest_vec_header(env, len, 0).unwrap_or(INVALID_HANDLE);
835 }
836
837 let Some(mut data) = GuestAllocation::new(env, name.len()) else {
838 return INVALID_HANDLE;
839 };
840 if !data.write_bytes(name.as_bytes()) {
841 return INVALID_HANDLE;
842 }
843 let Some(vec_ptr) = data.allocate_vec_header(len) else {
844 return INVALID_HANDLE;
845 };
846 let _ = data.into_raw();
847 vec_ptr
848}
849
850fn write_handle_vec(env: &mut FunctionEnvMut<WasmCapiEnv>, out_ptr: i32, handles: &[i32]) -> bool {
851 let Some(out_ptr) = non_null_guest_ptr(out_ptr) else {
852 return false;
853 };
854 let Ok(len) = u32::try_from(handles.len()) else {
855 return false;
856 };
857 let Some(byte_len) = handles.len().checked_mul(size_of::<i32>()) else {
858 return false;
859 };
860
861 if handles.is_empty() {
862 return write_guest_vec_header(env, out_ptr.get(), len, 0);
863 }
864
865 let Some(mut data) = GuestAllocation::new(env, byte_len) else {
866 return false;
867 };
868 if !data.write_i32_slice(handles) || !data.write_vec_header(out_ptr, len) {
869 return false;
870 }
871 let _ = data.into_raw();
872 true
873}
874
875fn read_handle_vec(env: &mut FunctionEnvMut<WasmCapiEnv>, vec_ptr: i32) -> Option<Vec<i32>> {
876 if vec_ptr <= INVALID_HANDLE {
877 return None;
878 }
879 let size = read_u32(env, vec_ptr)? as usize;
880 let data_ptr = read_i32(env, guest_ptr_with_offset(vec_ptr, WASM_VEC_DATA_OFFSET)?)?;
881 if size == 0 {
882 return Some(Vec::new());
883 }
884 read_guest_i32_vec(env, data_ptr, size)
885}
886
887fn type_to_wasm_kind(ty: Type) -> Option<u8> {
888 Some(match ty {
889 Type::I32 => WASM_I32,
890 Type::I64 => WASM_I64,
891 Type::F32 => WASM_F32,
892 Type::F64 => WASM_F64,
893 Type::FuncRef => WASM_FUNCREF,
894 Type::ExternRef => WASM_EXTERNREF,
895 Type::V128 | Type::ExceptionRef => return None,
896 })
897}
898
899fn wasm_kind_to_type(kind: i32) -> Option<Type> {
900 match kind as u8 {
901 WASM_I32 => Some(Type::I32),
902 WASM_I64 => Some(Type::I64),
903 WASM_F32 => Some(Type::F32),
904 WASM_F64 => Some(Type::F64),
905 WASM_FUNCREF => Some(Type::FuncRef),
906 WASM_EXTERNREF => Some(Type::ExternRef),
907 _ => None,
908 }
909}
910
911fn extern_kind(ty: &ExternType) -> i32 {
912 match ty {
913 ExternType::Function(_) => WASM_EXTERN_FUNC,
914 ExternType::Global(_) => WASM_EXTERN_GLOBAL,
915 ExternType::Table(_) => WASM_EXTERN_TABLE,
916 ExternType::Memory(_) => WASM_EXTERN_MEMORY,
917 ExternType::Tag(_) => INVALID_KIND,
918 }
919}
920
921fn read_wasm_val(env: &mut FunctionEnvMut<WasmCapiEnv>, val_ptr: i32, ty: Type) -> Option<Value> {
922 match ty {
923 Type::I32 => Some(Value::I32(read_i32(
924 env,
925 val_ptr + WASM_VAL_PAYLOAD_OFFSET,
926 )?)),
927 Type::I64 => Some(Value::I64(
928 read_u64(env, val_ptr + WASM_VAL_PAYLOAD_OFFSET)? as i64,
929 )),
930 Type::F32 => {
931 let raw = read_u32(env, val_ptr + WASM_VAL_PAYLOAD_OFFSET)?;
932 Some(Value::F32(f32::from_bits(raw)))
933 }
934 Type::F64 => {
935 let raw = read_u64(env, val_ptr + WASM_VAL_PAYLOAD_OFFSET)?;
936 Some(Value::F64(f64::from_bits(raw)))
937 }
938 Type::FuncRef | Type::ExternRef => {
940 let handle = read_i32(env, val_ptr + WASM_VAL_PAYLOAD_OFFSET)?;
941 Some(ref_value_from_handle(env, handle, ty))
942 }
943 Type::V128 | Type::ExceptionRef => None,
944 }
945}
946
947fn ref_value_from_handle(env: &FunctionEnvMut<WasmCapiEnv>, handle: i32, ty: Type) -> Value {
950 let null = match ty {
951 Type::FuncRef => Value::FuncRef(None),
952 _ => Value::ExternRef(None),
953 };
954 if handle <= INVALID_HANDLE {
955 return null;
956 }
957 match env.data().state.get(handle) {
958 Some(WasmObject::Ref(value)) => value.clone(),
959 Some(WasmObject::Func(f)) | Some(WasmObject::Extern(WasmExtern::Func(f))) => {
961 Value::FuncRef(Some(f.clone()))
962 }
963 _ => null,
964 }
965}
966
967fn ref_value_to_handle(env: &mut FunctionEnvMut<WasmCapiEnv>, value: &Value) -> i32 {
970 match value {
971 Value::ExternRef(None) | Value::FuncRef(None) => INVALID_HANDLE,
972 Value::ExternRef(Some(_)) | Value::FuncRef(Some(_)) => {
973 insert(env, WasmObject::Ref(value.clone()))
974 }
975 _ => INVALID_HANDLE,
976 }
977}
978
979fn write_wasm_val(env: &mut FunctionEnvMut<WasmCapiEnv>, val_ptr: i32, value: &Value) -> bool {
980 let Some(val_ptr) = non_null_guest_ptr(val_ptr) else {
981 return false;
982 };
983 let val_ptr = val_ptr.get();
984 let Some(kind) = type_to_wasm_kind(value.ty()) else {
985 return false;
986 };
987 let ref_handle = matches!(value, Value::FuncRef(_) | Value::ExternRef(_))
989 .then(|| ref_value_to_handle(env, value));
990 if !write_guest_bytes(env, val_ptr, &[kind]) {
991 return false;
992 }
993 match value {
994 Value::I32(v) => {
995 write_guest_bytes_offset(env, val_ptr, WASM_VAL_PAYLOAD_OFFSET, &v.to_le_bytes())
996 }
997 Value::I64(v) => {
998 write_guest_bytes_offset(env, val_ptr, WASM_VAL_PAYLOAD_OFFSET, &v.to_le_bytes())
999 }
1000 Value::F32(v) => write_guest_bytes_offset(
1001 env,
1002 val_ptr,
1003 WASM_VAL_PAYLOAD_OFFSET,
1004 &v.to_bits().to_le_bytes(),
1005 ),
1006 Value::F64(v) => write_guest_bytes_offset(
1007 env,
1008 val_ptr,
1009 WASM_VAL_PAYLOAD_OFFSET,
1010 &v.to_bits().to_le_bytes(),
1011 ),
1012 Value::FuncRef(_) | Value::ExternRef(_) => write_guest_bytes_offset(
1013 env,
1014 val_ptr,
1015 WASM_VAL_PAYLOAD_OFFSET,
1016 &ref_handle.unwrap_or(INVALID_HANDLE).to_le_bytes(),
1017 ),
1018 Value::V128(_) | Value::ExceptionRef(_) => false,
1019 }
1020}
1021
1022fn read_limits(
1023 env: &mut FunctionEnvMut<WasmCapiEnv>,
1024 limits_ptr: i32,
1025) -> Option<(u32, Option<u32>)> {
1026 if limits_ptr <= INVALID_HANDLE {
1027 return None;
1028 }
1029 let min = read_u32(env, limits_ptr)?;
1030 let max = read_u32(env, guest_ptr_with_offset(limits_ptr, 4)?)?;
1031 let max = if max == u32::MAX { None } else { Some(max) };
1032 Some((min, max))
1033}
1034
1035fn clone_extern_from_handle(env: &FunctionEnvMut<WasmCapiEnv>, handle: i32) -> Option<Extern> {
1036 match env.data().state.get(handle)? {
1037 WasmObject::Extern(WasmExtern::Func(f)) => Some(Extern::Function(f.clone())),
1038 WasmObject::Extern(WasmExtern::Global(g)) => Some(Extern::Global(g.clone())),
1039 WasmObject::Extern(WasmExtern::Table(t)) => Some(Extern::Table(t.clone())),
1040 WasmObject::Extern(WasmExtern::Memory(m)) => Some(Extern::Memory(m.clone())),
1041 WasmObject::Func(f) => Some(Extern::Function(f.clone())),
1042 WasmObject::Global(g) => Some(Extern::Global(g.clone())),
1043 WasmObject::Table(t) => Some(Extern::Table(t.clone())),
1044 WasmObject::Memory(m) => Some(Extern::Memory(m.clone())),
1045 _ => None,
1046 }
1047}
1048
1049fn insert(env: &mut FunctionEnvMut<WasmCapiEnv>, object: WasmObject) -> i32 {
1050 env.data_mut().state.insert(object)
1051}
1052
1053fn delete_handle(mut env: FunctionEnvMut<WasmCapiEnv>, handle: i32) {
1054 if let Some(shadow) = env.data_mut().state.remove(handle) {
1055 free_guest_memory(&mut env, shadow.guest_ptr);
1056 }
1057}
1058
1059fn wasm_engine_new(mut env: FunctionEnvMut<WasmCapiEnv>) -> i32 {
1060 insert(&mut env, WasmObject::Engine)
1061}
1062
1063fn wasm_store_new(mut env: FunctionEnvMut<WasmCapiEnv>, _engine: i32) -> i32 {
1064 insert(&mut env, WasmObject::Store)
1065}
1066
1067fn wasm_module_new(mut env: FunctionEnvMut<WasmCapiEnv>, _store: i32, bytes_ptr: i32) -> i32 {
1068 let Some(bytes) = read_byte_vec(&mut env, bytes_ptr) else {
1069 return INVALID_HANDLE;
1070 };
1071
1072 let module = if let Some(resolve_module_sync) = env.data().resolve_module_sync.clone() {
1073 resolve_module_sync(bytes)
1074 } else {
1075 let (_, store) = env.data_and_store_mut();
1076 Module::new(&store, bytes).map_err(Into::into)
1077 };
1078
1079 match module {
1080 Ok(module) => env.data_mut().state.insert(WasmObject::Module(module)),
1081 Err(_) => INVALID_HANDLE,
1082 }
1083}
1084
1085fn wasm_module_validate(mut env: FunctionEnvMut<WasmCapiEnv>, _store: i32, bytes_ptr: i32) -> i32 {
1086 let Some(bytes) = read_byte_vec(&mut env, bytes_ptr) else {
1087 return BOOL_FALSE;
1088 };
1089 let (_, store) = env.data_and_store_mut();
1090 Module::validate(&store, &bytes).is_ok() as i32
1091}
1092
1093fn wasm_module_imports(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 .imports()
1103 .map(|import| {
1104 insert(
1105 &mut env,
1106 WasmObject::ImportType {
1107 module: import.module().to_string(),
1108 name: import.name().to_string(),
1109 ty: import.ty().clone(),
1110 },
1111 )
1112 })
1113 .collect();
1114 write_handle_vec(&mut env, out_ptr, &handles);
1115}
1116
1117fn wasm_module_exports(mut env: FunctionEnvMut<WasmCapiEnv>, module_handle: i32, out_ptr: i32) {
1118 let module = match env.data().state.get(module_handle) {
1119 Some(WasmObject::Module(module)) => module.clone(),
1120 _ => {
1121 write_handle_vec(&mut env, out_ptr, &[]);
1122 return;
1123 }
1124 };
1125 let handles: Vec<i32> = module
1126 .exports()
1127 .map(|export| {
1128 insert(
1129 &mut env,
1130 WasmObject::ExportType {
1131 name: export.name().to_string(),
1132 ty: export.ty().clone(),
1133 },
1134 )
1135 })
1136 .collect();
1137 write_handle_vec(&mut env, out_ptr, &handles);
1138}
1139
1140fn wasm_importtype_module(mut env: FunctionEnvMut<WasmCapiEnv>, import_handle: i32) -> i32 {
1141 let name = match env.data().state.get(import_handle) {
1142 Some(WasmObject::ImportType { module, .. }) => module.clone(),
1143 _ => return INVALID_HANDLE,
1144 };
1145 allocate_name(&mut env, &name)
1146}
1147
1148fn wasm_importtype_name(mut env: FunctionEnvMut<WasmCapiEnv>, import_handle: i32) -> i32 {
1149 let name = match env.data().state.get(import_handle) {
1150 Some(WasmObject::ImportType { name, .. }) => name.clone(),
1151 _ => return INVALID_HANDLE,
1152 };
1153 allocate_name(&mut env, &name)
1154}
1155
1156fn wasm_importtype_type(mut env: FunctionEnvMut<WasmCapiEnv>, import_handle: i32) -> i32 {
1157 let ty = match env.data().state.get(import_handle) {
1158 Some(WasmObject::ImportType { ty, .. }) => ty.clone(),
1159 _ => return INVALID_HANDLE,
1160 };
1161 insert(&mut env, WasmObject::ExternType(ty))
1162}
1163
1164fn wasm_exporttype_name(mut env: FunctionEnvMut<WasmCapiEnv>, export_handle: i32) -> i32 {
1165 let name = match env.data().state.get(export_handle) {
1166 Some(WasmObject::ExportType { name, .. }) => name.clone(),
1167 _ => return INVALID_HANDLE,
1168 };
1169 allocate_name(&mut env, &name)
1170}
1171
1172fn wasm_exporttype_type(mut env: FunctionEnvMut<WasmCapiEnv>, export_handle: i32) -> i32 {
1173 let ty = match env.data().state.get(export_handle) {
1174 Some(WasmObject::ExportType { ty, .. }) => ty.clone(),
1175 _ => return INVALID_HANDLE,
1176 };
1177 insert(&mut env, WasmObject::ExternType(ty))
1178}
1179
1180fn wasm_externtype_kind(env: FunctionEnvMut<WasmCapiEnv>, type_handle: i32) -> i32 {
1181 match env.data().state.get(type_handle) {
1182 Some(WasmObject::ExternType(ty)) => extern_kind(ty),
1183 _ => INVALID_KIND,
1184 }
1185}
1186
1187fn wasm_externtype_as_functype_const(
1188 mut env: FunctionEnvMut<WasmCapiEnv>,
1189 type_handle: i32,
1190) -> i32 {
1191 let ty = match env.data().state.get(type_handle) {
1192 Some(WasmObject::ExternType(ExternType::Function(ty))) => ty.clone(),
1193 _ => return INVALID_HANDLE,
1194 };
1195 insert(&mut env, WasmObject::FuncType(ty))
1196}
1197
1198fn wasm_functype_copy(mut env: FunctionEnvMut<WasmCapiEnv>, type_handle: i32) -> i32 {
1199 let ty = match env.data().state.get(type_handle) {
1200 Some(WasmObject::FuncType(ty)) => ty.clone(),
1201 _ => return INVALID_HANDLE,
1202 };
1203 insert(&mut env, WasmObject::FuncType(ty))
1204}
1205
1206fn write_valtype_vec_for_types(env: &mut FunctionEnvMut<WasmCapiEnv>, types: &[Type]) -> i32 {
1207 let handles: Vec<i32> = types
1208 .iter()
1209 .map(|ty| insert(env, WasmObject::ValType(*ty)))
1210 .collect();
1211 let Some(mut allocation) = GuestAllocation::new(env, WASM_VEC_SIZE) else {
1212 return INVALID_HANDLE;
1213 };
1214 if !allocation.write_handles(&handles) {
1215 return INVALID_HANDLE;
1216 }
1217 allocation.into_raw()
1218}
1219
1220fn wasm_functype_params(mut env: FunctionEnvMut<WasmCapiEnv>, type_handle: i32) -> i32 {
1221 let params = match env.data().state.get(type_handle) {
1222 Some(WasmObject::FuncType(ty)) => ty.params().to_vec(),
1223 _ => return INVALID_HANDLE,
1224 };
1225 write_valtype_vec_for_types(&mut env, ¶ms)
1226}
1227
1228fn wasm_functype_results(mut env: FunctionEnvMut<WasmCapiEnv>, type_handle: i32) -> i32 {
1229 let results = match env.data().state.get(type_handle) {
1230 Some(WasmObject::FuncType(ty)) => ty.results().to_vec(),
1231 _ => return INVALID_HANDLE,
1232 };
1233 write_valtype_vec_for_types(&mut env, &results)
1234}
1235
1236fn wasm_valtype_new(mut env: FunctionEnvMut<WasmCapiEnv>, kind: i32) -> i32 {
1237 match wasm_kind_to_type(kind) {
1238 Some(ty) => insert(&mut env, WasmObject::ValType(ty)),
1239 None => INVALID_HANDLE,
1240 }
1241}
1242
1243fn wasm_valtype_kind(env: FunctionEnvMut<WasmCapiEnv>, valtype_handle: i32) -> i32 {
1244 match env.data().state.get(valtype_handle) {
1245 Some(WasmObject::ValType(ty)) => type_to_wasm_kind(*ty)
1246 .map(i32::from)
1247 .unwrap_or(INVALID_KIND),
1248 _ => INVALID_KIND,
1249 }
1250}
1251
1252fn wasm_memorytype_new(mut env: FunctionEnvMut<WasmCapiEnv>, limits_ptr: i32) -> i32 {
1253 let Some((min, max)) = read_limits(&mut env, limits_ptr) else {
1254 return INVALID_HANDLE;
1255 };
1256 insert(
1257 &mut env,
1258 WasmObject::MemoryType(MemoryType::new(Pages(min), max.map(Pages), false)),
1259 )
1260}
1261
1262fn wasm_memory_new(mut env: FunctionEnvMut<WasmCapiEnv>, _store: i32, type_handle: i32) -> i32 {
1263 let ty = match env.data().state.get(type_handle) {
1264 Some(WasmObject::MemoryType(ty)) => *ty,
1265 _ => return INVALID_HANDLE,
1266 };
1267 match Memory::new(&mut env, ty) {
1268 Ok(memory) => insert(&mut env, WasmObject::Memory(memory)),
1269 Err(_) => INVALID_HANDLE,
1270 }
1271}
1272
1273fn wasm_memory_size(env: FunctionEnvMut<WasmCapiEnv>, memory_handle: i32) -> i32 {
1274 let Some(memory) = memory_from_handle(&env, memory_handle) else {
1275 return INVALID_SIZE;
1276 };
1277 i32::try_from(memory.size(&env).0).unwrap_or(INVALID_SIZE)
1278}
1279
1280fn wasm_memory_grow(mut env: FunctionEnvMut<WasmCapiEnv>, memory_handle: i32, delta: i32) -> i32 {
1281 let Ok(delta) = u32::try_from(delta) else {
1282 return BOOL_FALSE;
1283 };
1284 let memory = match env.data().state.get(memory_handle) {
1285 Some(WasmObject::Memory(memory)) => memory.clone(),
1286 Some(WasmObject::Extern(WasmExtern::Memory(memory))) => memory.clone(),
1287 _ => return BOOL_FALSE,
1288 };
1289 if memory.grow(&mut env, Pages(delta)).is_ok() {
1290 BOOL_TRUE
1291 } else {
1292 BOOL_FALSE
1293 }
1294}
1295
1296fn memory_from_handle(env: &FunctionEnvMut<WasmCapiEnv>, memory_handle: i32) -> Option<Memory> {
1297 match env.data().state.get(memory_handle)? {
1298 WasmObject::Memory(memory) => Some(memory.clone()),
1299 WasmObject::Extern(WasmExtern::Memory(memory)) => Some(memory.clone()),
1300 _ => None,
1301 }
1302}
1303
1304fn memory_supports_shadow(env: &FunctionEnvMut<WasmCapiEnv>, memory: &Memory) -> bool {
1305 !memory.ty(env).shared
1309}
1310
1311fn copy_wasmer_memory_to_guest(
1312 env: &mut FunctionEnvMut<WasmCapiEnv>,
1313 memory: &Memory,
1314 guest_ptr: i32,
1315 len: usize,
1316) -> bool {
1317 if len == 0 {
1318 return true;
1319 }
1320 let Some(guest_memory) = env.data().memory.clone() else {
1321 return false;
1322 };
1323 let source_view = memory.view(&*env);
1324 let guest_view = guest_memory.view(&*env);
1325 let Some(source_offset) = checked_memory_offset(0, len, source_view.data_size()) else {
1326 return false;
1327 };
1328 let Some(guest_offset) = checked_memory_offset(guest_ptr, len, guest_view.data_size()) else {
1329 return false;
1330 };
1331 let source_base = source_view.data_ptr();
1332 let guest_base = guest_view.data_ptr();
1333 if ptr::eq(source_base, guest_base) {
1334 return false;
1335 }
1336 unsafe {
1337 ptr::copy_nonoverlapping(
1339 source_base.add(source_offset),
1340 guest_base.add(guest_offset),
1341 len,
1342 );
1343 }
1344 true
1345}
1346
1347fn copy_guest_memory_to_wasmer(
1348 env: &mut FunctionEnvMut<WasmCapiEnv>,
1349 guest_ptr: i32,
1350 memory: &Memory,
1351 len: usize,
1352) -> bool {
1353 if len == 0 {
1354 return true;
1355 }
1356 let Some(guest_memory) = env.data().memory.clone() else {
1357 return false;
1358 };
1359 let guest_view = guest_memory.view(&*env);
1360 let target_view = memory.view(&*env);
1361 let Some(guest_offset) = checked_memory_offset(guest_ptr, len, guest_view.data_size()) else {
1362 return false;
1363 };
1364 let Some(target_offset) = checked_memory_offset(0, len, target_view.data_size()) else {
1365 return false;
1366 };
1367 let guest_base = guest_view.data_ptr();
1368 let target_base = target_view.data_ptr();
1369 if ptr::eq(guest_base, target_base) {
1370 return false;
1371 }
1372 unsafe {
1373 ptr::copy_nonoverlapping(
1375 guest_base.add(guest_offset),
1376 target_base.add(target_offset),
1377 len,
1378 );
1379 }
1380 true
1381}
1382
1383fn wasm_memory_data_size(env: FunctionEnvMut<WasmCapiEnv>, memory_handle: i32) -> i32 {
1384 let Some(memory) = memory_from_handle(&env, memory_handle) else {
1385 return INVALID_SIZE;
1386 };
1387 if !memory_supports_shadow(&env, &memory) {
1388 return INVALID_SIZE;
1389 }
1390 i32::try_from(memory.view(&env).data_size()).unwrap_or(INVALID_SIZE)
1391}
1392
1393fn wasm_memory_data(mut env: FunctionEnvMut<WasmCapiEnv>, memory_handle: i32) -> i32 {
1394 let Some(memory) = memory_from_handle(&env, memory_handle) else {
1395 return INVALID_HANDLE;
1396 };
1397
1398 if !memory_supports_shadow(&env, &memory) {
1399 return INVALID_HANDLE;
1400 };
1401 let size = {
1402 let view = memory.view(&env);
1403 let Ok(size) = usize::try_from(view.data_size()) else {
1404 return INVALID_HANDLE;
1405 };
1406 size
1407 };
1408 if size == 0 {
1409 return INVALID_HANDLE;
1410 }
1411
1412 let existing = env.data().state.memory_shadows.get(&memory_handle).copied();
1413 let shadow = match existing {
1414 Some(existing) if existing.len >= size => MemoryShadow {
1420 guest_ptr: existing.guest_ptr,
1421 len: size,
1422 },
1423 _ => {
1424 let Some(mut allocation) = GuestAllocation::new(&mut env, size) else {
1425 return INVALID_HANDLE;
1426 };
1427 if !allocation.copy_from_wasmer_memory(&memory, size) {
1428 return INVALID_HANDLE;
1429 }
1430 let guest_ptr = allocation.into_raw();
1431 if let Some(existing) = existing {
1432 free_guest_memory(&mut env, existing.guest_ptr);
1433 }
1434 MemoryShadow {
1435 guest_ptr,
1436 len: size,
1437 }
1438 }
1439 };
1440
1441 env.data_mut()
1442 .state
1443 .memory_shadows
1444 .insert(memory_handle, shadow);
1445 shadow.guest_ptr
1446}
1447
1448fn sync_memory_shadows_to_wasmer(env: &mut FunctionEnvMut<WasmCapiEnv>) {
1449 let shadows: Vec<_> = env
1450 .data()
1451 .state
1452 .memory_shadows
1453 .iter()
1454 .map(|(&handle, &shadow)| (handle, shadow))
1455 .collect();
1456
1457 for (handle, shadow) in shadows {
1458 let Some(memory) = memory_from_handle(env, handle) else {
1459 continue;
1460 };
1461 if !memory_supports_shadow(env, &memory) {
1462 continue;
1463 };
1464 let _ = copy_guest_memory_to_wasmer(env, shadow.guest_ptr, &memory, shadow.len);
1465 }
1466}
1467
1468fn refresh_memory_shadows_from_wasmer(env: &mut FunctionEnvMut<WasmCapiEnv>) {
1469 let shadows: Vec<_> = env
1470 .data()
1471 .state
1472 .memory_shadows
1473 .iter()
1474 .map(|(&handle, &shadow)| (handle, shadow))
1475 .collect();
1476
1477 for (handle, shadow) in shadows {
1478 let Some(memory) = memory_from_handle(env, handle) else {
1479 continue;
1480 };
1481 if !memory_supports_shadow(env, &memory) {
1482 continue;
1483 }
1484 let _ = copy_wasmer_memory_to_guest(env, &memory, shadow.guest_ptr, shadow.len);
1485 }
1486}
1487
1488fn wasm_globaltype_new(
1489 mut env: FunctionEnvMut<WasmCapiEnv>,
1490 valtype_handle: i32,
1491 mutability: i32,
1492) -> i32 {
1493 let ty = match env.data().state.get(valtype_handle) {
1494 Some(WasmObject::ValType(ty)) => *ty,
1495 _ => return INVALID_HANDLE,
1496 };
1497 let mutability = if mutability == WASM_VAR {
1498 Mutability::Var
1499 } else {
1500 Mutability::Const
1501 };
1502 insert(
1503 &mut env,
1504 WasmObject::GlobalType(GlobalType::new(ty, mutability)),
1505 )
1506}
1507
1508fn wasm_globaltype_content(mut env: FunctionEnvMut<WasmCapiEnv>, globaltype_handle: i32) -> i32 {
1509 let ty = match env.data().state.get(globaltype_handle) {
1510 Some(WasmObject::GlobalType(ty)) => ty.ty,
1511 _ => return INVALID_HANDLE,
1512 };
1513 insert(&mut env, WasmObject::ValType(ty))
1514}
1515
1516fn wasm_globaltype_mutability(env: FunctionEnvMut<WasmCapiEnv>, globaltype_handle: i32) -> i32 {
1517 match env.data().state.get(globaltype_handle) {
1518 Some(WasmObject::GlobalType(ty)) if ty.mutability == Mutability::Var => WASM_VAR,
1519 Some(WasmObject::GlobalType(_)) => WASM_CONST,
1520 _ => WASM_CONST,
1521 }
1522}
1523
1524fn wasm_global_new(
1525 mut env: FunctionEnvMut<WasmCapiEnv>,
1526 _store: i32,
1527 globaltype_handle: i32,
1528 val_ptr: i32,
1529) -> i32 {
1530 let ty = match env.data().state.get(globaltype_handle) {
1531 Some(WasmObject::GlobalType(ty)) => *ty,
1532 _ => return INVALID_HANDLE,
1533 };
1534 let Some(value) = read_wasm_val(&mut env, val_ptr, ty.ty) else {
1535 return INVALID_HANDLE;
1536 };
1537 let global = if ty.mutability == Mutability::Var {
1538 Global::new_mut(&mut env, value)
1539 } else {
1540 Global::new(&mut env, value)
1541 };
1542 insert(&mut env, WasmObject::Global(global))
1543}
1544
1545fn global_from_handle(env: &FunctionEnvMut<WasmCapiEnv>, global_handle: i32) -> Option<Global> {
1546 match env.data().state.get(global_handle)? {
1547 WasmObject::Global(global) => Some(global.clone()),
1548 WasmObject::Extern(WasmExtern::Global(global)) => Some(global.clone()),
1549 _ => None,
1550 }
1551}
1552
1553fn wasm_global_type(mut env: FunctionEnvMut<WasmCapiEnv>, global_handle: i32) -> i32 {
1554 let Some(global) = global_from_handle(&env, global_handle) else {
1555 return INVALID_HANDLE;
1556 };
1557 let ty = global.ty(&env);
1558 insert(&mut env, WasmObject::GlobalType(ty))
1559}
1560
1561fn wasm_global_get(mut env: FunctionEnvMut<WasmCapiEnv>, global_handle: i32, out_ptr: i32) {
1562 let Some(global) = global_from_handle(&env, global_handle) else {
1563 return;
1564 };
1565 let value = global.get(&mut env);
1566 write_wasm_val(&mut env, out_ptr, &value);
1567}
1568
1569fn wasm_global_set(mut env: FunctionEnvMut<WasmCapiEnv>, global_handle: i32, val_ptr: i32) {
1570 let Some(global) = global_from_handle(&env, global_handle) else {
1571 return;
1572 };
1573 let ty = global.ty(&env).ty;
1574 if let Some(value) = read_wasm_val(&mut env, val_ptr, ty) {
1575 let _ = global.set(&mut env, value);
1576 }
1577}
1578
1579fn wasm_instance_new(
1580 mut env: FunctionEnvMut<WasmCapiEnv>,
1581 _store: i32,
1582 module_handle: i32,
1583 imports_vec_ptr: i32,
1584 trap_out_ptr: i32,
1585) -> i32 {
1586 if let Some(trap_out_ptr) = non_null_guest_ptr(trap_out_ptr) {
1587 write_guest_u32(&mut env, trap_out_ptr.get(), INVALID_HANDLE as u32);
1588 }
1589 let module = match env.data().state.get(module_handle) {
1590 Some(WasmObject::Module(module)) => module.clone(),
1591 _ => return INVALID_HANDLE,
1592 };
1593
1594 let import_handles = read_handle_vec(&mut env, imports_vec_ptr).unwrap_or_default();
1595 let mut imports = Imports::new();
1596 for (import, handle) in module.imports().zip(import_handles.into_iter()) {
1597 let Some(ext) = clone_extern_from_handle(&env, handle) else {
1598 return INVALID_HANDLE;
1599 };
1600 imports.define(import.module(), import.name(), ext);
1601 }
1602
1603 let result = Instance::new(&mut env, &module, &imports);
1606 refresh_memory_shadows_from_wasmer(&mut env);
1607 match result {
1608 Ok(instance) => insert(&mut env, WasmObject::Instance(instance)),
1609 Err(err) => {
1610 if let Some(trap_out_ptr) = non_null_guest_ptr(trap_out_ptr) {
1611 let trap = insert(&mut env, WasmObject::Trap(err.to_string()));
1612 write_guest_u32(
1613 &mut env,
1614 trap_out_ptr.get(),
1615 u32::try_from(trap).unwrap_or(INVALID_HANDLE as u32),
1616 );
1617 }
1618 INVALID_HANDLE
1619 }
1620 }
1621}
1622
1623fn wasm_instance_exports(mut env: FunctionEnvMut<WasmCapiEnv>, instance_handle: i32, out_ptr: i32) {
1624 let instance = match env.data().state.get(instance_handle) {
1625 Some(WasmObject::Instance(instance)) => instance.clone(),
1626 _ => {
1627 write_handle_vec(&mut env, out_ptr, &[]);
1628 return;
1629 }
1630 };
1631 let handles: Vec<i32> = instance
1632 .exports
1633 .iter()
1634 .map(|(_, ext)| {
1635 let object = match ext {
1636 Extern::Function(func) => WasmObject::Extern(WasmExtern::Func(func.clone())),
1637 Extern::Global(global) => WasmObject::Extern(WasmExtern::Global(global.clone())),
1638 Extern::Table(table) => WasmObject::Extern(WasmExtern::Table(table.clone())),
1639 Extern::Memory(memory) => WasmObject::Extern(WasmExtern::Memory(memory.clone())),
1640 Extern::Tag(_) => WasmObject::Trap("unsupported tag export".to_string()),
1641 };
1642 insert(&mut env, object)
1643 })
1644 .collect();
1645 write_handle_vec(&mut env, out_ptr, &handles);
1646}
1647
1648fn wasm_extern_kind(env: FunctionEnvMut<WasmCapiEnv>, extern_handle: i32) -> i32 {
1649 match env.data().state.get(extern_handle) {
1650 Some(WasmObject::Extern(WasmExtern::Func(_))) | Some(WasmObject::Func(_)) => {
1651 WASM_EXTERN_FUNC
1652 }
1653 Some(WasmObject::Extern(WasmExtern::Global(_))) | Some(WasmObject::Global(_)) => {
1654 WASM_EXTERN_GLOBAL
1655 }
1656 Some(WasmObject::Extern(WasmExtern::Table(_))) | Some(WasmObject::Table(_)) => {
1657 WASM_EXTERN_TABLE
1658 }
1659 Some(WasmObject::Extern(WasmExtern::Memory(_))) | Some(WasmObject::Memory(_)) => {
1660 WASM_EXTERN_MEMORY
1661 }
1662 _ => INVALID_KIND,
1663 }
1664}
1665
1666fn wasm_extern_as_func(mut env: FunctionEnvMut<WasmCapiEnv>, extern_handle: i32) -> i32 {
1667 let object = match env.data().state.get(extern_handle) {
1668 Some(WasmObject::Extern(WasmExtern::Func(func))) => WasmObject::Func(func.clone()),
1669 _ => return INVALID_HANDLE,
1670 };
1671 insert(&mut env, object)
1672}
1673
1674fn wasm_extern_as_global(mut env: FunctionEnvMut<WasmCapiEnv>, extern_handle: i32) -> i32 {
1675 let object = match env.data().state.get(extern_handle) {
1676 Some(WasmObject::Extern(WasmExtern::Global(global))) => WasmObject::Global(global.clone()),
1677 _ => return INVALID_HANDLE,
1678 };
1679 insert(&mut env, object)
1680}
1681
1682fn wasm_extern_as_table(mut env: FunctionEnvMut<WasmCapiEnv>, extern_handle: i32) -> i32 {
1683 let object = match env.data().state.get(extern_handle) {
1684 Some(WasmObject::Extern(WasmExtern::Table(table))) => WasmObject::Table(table.clone()),
1685 _ => return INVALID_HANDLE,
1686 };
1687 insert(&mut env, object)
1688}
1689
1690fn wasm_extern_as_memory(mut env: FunctionEnvMut<WasmCapiEnv>, extern_handle: i32) -> i32 {
1691 let object = match env.data().state.get(extern_handle) {
1692 Some(WasmObject::Extern(WasmExtern::Memory(memory))) => WasmObject::Memory(memory.clone()),
1693 _ => return INVALID_HANDLE,
1694 };
1695 insert(&mut env, object)
1696}
1697
1698fn wasm_func_copy(mut env: FunctionEnvMut<WasmCapiEnv>, func_handle: i32) -> i32 {
1699 let object = match env.data().state.get(func_handle) {
1700 Some(WasmObject::Func(func)) => WasmObject::Func(func.clone()),
1701 Some(WasmObject::Extern(WasmExtern::Func(func))) => WasmObject::Func(func.clone()),
1702 _ => return INVALID_HANDLE,
1703 };
1704 insert(&mut env, object)
1705}
1706
1707fn wasm_global_copy(mut env: FunctionEnvMut<WasmCapiEnv>, global_handle: i32) -> i32 {
1708 let Some(global) = global_from_handle(&env, global_handle) else {
1709 return INVALID_HANDLE;
1710 };
1711 insert(&mut env, WasmObject::Global(global))
1712}
1713
1714fn wasm_memory_copy(mut env: FunctionEnvMut<WasmCapiEnv>, memory_handle: i32) -> i32 {
1715 let Some(memory) = memory_from_handle(&env, memory_handle) else {
1716 return INVALID_HANDLE;
1717 };
1718 insert(&mut env, WasmObject::Memory(memory))
1719}
1720
1721fn wasm_table_copy(mut env: FunctionEnvMut<WasmCapiEnv>, table_handle: i32) -> i32 {
1722 let object = match env.data().state.get(table_handle) {
1723 Some(WasmObject::Table(table)) => WasmObject::Table(table.clone()),
1724 Some(WasmObject::Extern(WasmExtern::Table(table))) => WasmObject::Table(table.clone()),
1725 _ => return INVALID_HANDLE,
1726 };
1727 insert(&mut env, object)
1728}
1729
1730fn wasm_func_as_extern(mut env: FunctionEnvMut<WasmCapiEnv>, func_handle: i32) -> i32 {
1731 let object = match env.data().state.get(func_handle) {
1732 Some(WasmObject::Func(func)) => WasmObject::Extern(WasmExtern::Func(func.clone())),
1733 _ => return INVALID_HANDLE,
1734 };
1735 insert(&mut env, object)
1736}
1737
1738fn wasm_global_as_extern(mut env: FunctionEnvMut<WasmCapiEnv>, global_handle: i32) -> i32 {
1739 let Some(global) = global_from_handle(&env, global_handle) else {
1740 return INVALID_HANDLE;
1741 };
1742 insert(&mut env, WasmObject::Extern(WasmExtern::Global(global)))
1743}
1744
1745fn wasm_memory_as_extern(mut env: FunctionEnvMut<WasmCapiEnv>, memory_handle: i32) -> i32 {
1746 let Some(memory) = memory_from_handle(&env, memory_handle) else {
1747 return INVALID_HANDLE;
1748 };
1749 insert(&mut env, WasmObject::Extern(WasmExtern::Memory(memory)))
1750}
1751
1752fn wasm_table_as_extern(mut env: FunctionEnvMut<WasmCapiEnv>, table_handle: i32) -> i32 {
1753 let object = match env.data().state.get(table_handle) {
1754 Some(WasmObject::Table(table)) => WasmObject::Extern(WasmExtern::Table(table.clone())),
1755 _ => return INVALID_HANDLE,
1756 };
1757 insert(&mut env, object)
1758}
1759
1760fn wasm_func_type(mut env: FunctionEnvMut<WasmCapiEnv>, func_handle: i32) -> i32 {
1761 let func = match env.data().state.get(func_handle) {
1762 Some(WasmObject::Func(func)) => func.clone(),
1763 Some(WasmObject::Extern(WasmExtern::Func(func))) => func.clone(),
1764 _ => return INVALID_HANDLE,
1765 };
1766 let ty = func.ty(&env);
1767 insert(&mut env, WasmObject::FuncType(ty))
1768}
1769
1770fn wasm_func_call(
1771 mut env: FunctionEnvMut<WasmCapiEnv>,
1772 func_handle: i32,
1773 args_vec_ptr: i32,
1774 results_vec_ptr: i32,
1775) -> i32 {
1776 let func = match env.data().state.get(func_handle) {
1777 Some(WasmObject::Func(func)) => func.clone(),
1778 Some(WasmObject::Extern(WasmExtern::Func(func))) => func.clone(),
1779 _ => return insert(&mut env, WasmObject::Trap("invalid function".to_string())),
1780 };
1781 let ty = func.ty(&env);
1782 let arg_data_ptr = guest_ptr_with_offset(args_vec_ptr, WASM_VEC_DATA_OFFSET)
1783 .and_then(|ptr| read_i32(&mut env, ptr))
1784 .unwrap_or(INVALID_HANDLE);
1785 let mut args = Vec::with_capacity(ty.params().len());
1786 for (index, ty) in ty.params().iter().enumerate() {
1787 let val_ptr = arg_data_ptr + (index * WASM_VAL_SIZE) as i32;
1788 let Some(value) = read_wasm_val(&mut env, val_ptr, *ty) else {
1789 return insert(
1790 &mut env,
1791 WasmObject::Trap("invalid function argument".to_string()),
1792 );
1793 };
1794 args.push(value);
1795 }
1796 sync_memory_shadows_to_wasmer(&mut env);
1797 match func.call(&mut env, &args) {
1798 Ok(results) => {
1799 refresh_memory_shadows_from_wasmer(&mut env);
1800 let result_data_ptr = guest_ptr_with_offset(results_vec_ptr, WASM_VEC_DATA_OFFSET)
1801 .and_then(|ptr| read_i32(&mut env, ptr))
1802 .unwrap_or(INVALID_HANDLE);
1803 for (index, value) in results.iter().enumerate() {
1804 let val_ptr = result_data_ptr + (index * WASM_VAL_SIZE) as i32;
1805 write_wasm_val(&mut env, val_ptr, value);
1806 }
1807 INVALID_HANDLE
1808 }
1809 Err(err) => {
1810 refresh_memory_shadows_from_wasmer(&mut env);
1811 insert(&mut env, WasmObject::Trap(err.to_string()))
1812 }
1813 }
1814}
1815
1816fn allocate_wasm_val_vec_for_values(
1817 env: &mut FunctionEnvMut<WasmCapiEnv>,
1818 values: &[Value],
1819) -> Option<(i32, i32)> {
1820 let len = u32::try_from(values.len()).ok()?;
1821 let byte_len = values.len().checked_mul(WASM_VAL_SIZE)?;
1822
1823 if byte_len == 0 {
1824 let vec_ptr = allocate_guest_vec_header(env, len, 0)?;
1825 return Some((vec_ptr, 0));
1826 }
1827
1828 let mut data = GuestAllocation::new(env, byte_len)?;
1829 for (index, value) in values.iter().enumerate() {
1830 let offset = index.checked_mul(WASM_VAL_SIZE)?;
1831 if !data.write_wasm_val_at(offset, value) {
1832 return None;
1833 }
1834 }
1835
1836 let vec_ptr = data.allocate_vec_header(len)?;
1837 let data_ptr = data.into_raw();
1838 Some((vec_ptr, data_ptr))
1839}
1840
1841fn allocate_uninitialized_wasm_val_vec(
1842 env: &mut FunctionEnvMut<WasmCapiEnv>,
1843 len: usize,
1844) -> Option<(i32, i32)> {
1845 let len_u32 = u32::try_from(len).ok()?;
1846 let byte_len = len.checked_mul(WASM_VAL_SIZE)?;
1847
1848 if byte_len == 0 {
1849 let vec_ptr = allocate_guest_vec_header(env, len_u32, 0)?;
1850 return Some((vec_ptr, 0));
1851 }
1852
1853 let mut data = GuestAllocation::new(env, byte_len)?;
1854 let vec_ptr = data.allocate_vec_header(len_u32)?;
1855 let data_ptr = data.into_raw();
1856 Some((vec_ptr, data_ptr))
1857}
1858
1859fn trap_message_from_handle(env: &FunctionEnvMut<WasmCapiEnv>, trap_handle: i32) -> String {
1860 match env.data().state.get(trap_handle) {
1861 Some(WasmObject::Trap(message)) => message.clone(),
1862 _ => "WebAssembly import callback trapped".to_string(),
1863 }
1864}
1865
1866fn call_guest_wasm_callback(
1867 env: &mut FunctionEnvMut<WasmCapiEnv>,
1868 callback: u32,
1869 callback_env: i32,
1870 args_vec_ptr: i32,
1871 results_vec_ptr: i32,
1872) -> i32 {
1873 let Some(table) = env.data().table.clone() else {
1874 return insert(
1875 env,
1876 WasmObject::Trap("missing guest indirect function table".to_string()),
1877 );
1878 };
1879 let Some(elem) = table.get(&mut *env, callback) else {
1880 return insert(
1881 env,
1882 WasmObject::Trap("invalid guest WebAssembly callback pointer".to_string()),
1883 );
1884 };
1885 let func = match elem {
1886 Value::FuncRef(Some(func)) => func,
1887 _ => {
1888 return insert(
1889 env,
1890 WasmObject::Trap("invalid guest WebAssembly callback reference".to_string()),
1891 );
1892 }
1893 };
1894 refresh_memory_shadows_from_wasmer(env);
1900 let call_result = func.call(
1901 env,
1902 &[
1903 Value::I32(callback_env),
1904 Value::I32(args_vec_ptr),
1905 Value::I32(results_vec_ptr),
1906 ],
1907 );
1908 sync_memory_shadows_to_wasmer(env);
1909 match call_result {
1910 Ok(values) => match values.first() {
1911 Some(Value::I32(value)) => *value,
1912 Some(Value::I64(value)) => *value as i32,
1913 _ => INVALID_HANDLE,
1914 },
1915 Err(err) => insert(env, WasmObject::Trap(err.to_string())),
1916 }
1917}
1918
1919fn wasm_func_new_with_env(
1920 mut env: FunctionEnvMut<WasmCapiEnv>,
1921 _store: i32,
1922 type_handle: i32,
1923 callback: i32,
1924 callback_env: i32,
1925 _finalizer: i32,
1926) -> i32 {
1927 if callback <= INVALID_HANDLE {
1928 return INVALID_HANDLE;
1929 }
1930 let ty = match env.data().state.get(type_handle) {
1931 Some(WasmObject::FuncType(ty)) => ty.clone(),
1932 _ => return INVALID_HANDLE,
1933 };
1934 let Some(func_env) = env.data().func_env.clone() else {
1935 return INVALID_HANDLE;
1936 };
1937 let Ok(callback) = u32::try_from(callback) else {
1938 return INVALID_HANDLE;
1939 };
1940 let result_types = ty.results().to_vec();
1941 let func = WasmerFunction::new_with_env(&mut env, &func_env, ty, move |mut env, args| {
1942 let Some((args_vec_ptr, _args_data_ptr)) = allocate_wasm_val_vec_for_values(&mut env, args)
1943 else {
1944 return Err(RuntimeError::new(
1945 "failed to allocate WebAssembly callback arguments",
1946 ));
1947 };
1948 let Some((results_vec_ptr, results_data_ptr)) =
1949 allocate_uninitialized_wasm_val_vec(&mut env, result_types.len())
1950 else {
1951 return Err(RuntimeError::new(
1952 "failed to allocate WebAssembly callback results",
1953 ));
1954 };
1955
1956 let trap = call_guest_wasm_callback(
1957 &mut env,
1958 callback,
1959 callback_env,
1960 args_vec_ptr,
1961 results_vec_ptr,
1962 );
1963 if trap != INVALID_HANDLE {
1964 return Err(RuntimeError::new(trap_message_from_handle(&env, trap)));
1965 }
1966
1967 let mut results = Vec::with_capacity(result_types.len());
1968 for (index, ty) in result_types.iter().enumerate() {
1969 let val_ptr = results_data_ptr + (index * WASM_VAL_SIZE) as i32;
1970 let Some(value) = read_wasm_val(&mut env, val_ptr, *ty) else {
1971 return Err(RuntimeError::new(
1972 "guest WebAssembly callback returned an invalid value",
1973 ));
1974 };
1975 results.push(value);
1976 }
1977 Ok(results)
1978 });
1979 insert(&mut env, WasmObject::Func(func))
1980}
1981
1982fn wasm_table_size(env: FunctionEnvMut<WasmCapiEnv>, table_handle: i32) -> i32 {
1983 match env.data().state.get(table_handle) {
1984 Some(WasmObject::Table(table)) => i32::try_from(table.size(&env)).unwrap_or(INVALID_SIZE),
1985 Some(WasmObject::Extern(WasmExtern::Table(table))) => {
1986 i32::try_from(table.size(&env)).unwrap_or(INVALID_SIZE)
1987 }
1988 _ => INVALID_SIZE,
1989 }
1990}
1991
1992fn table_from_handle(env: &FunctionEnvMut<WasmCapiEnv>, handle: i32) -> Option<Table> {
1994 match env.data().state.get(handle) {
1995 Some(WasmObject::Table(table)) => Some(table.clone()),
1996 Some(WasmObject::Extern(WasmExtern::Table(table))) => Some(table.clone()),
1997 _ => None,
1998 }
1999}
2000
2001fn null_ref_for(element_ty: Type) -> Value {
2003 match element_ty {
2004 Type::FuncRef => Value::FuncRef(None),
2005 _ => Value::ExternRef(None),
2006 }
2007}
2008
2009fn wasm_table_grow(
2010 mut env: FunctionEnvMut<WasmCapiEnv>,
2011 table_handle: i32,
2012 delta: i32,
2013 init: i32,
2014) -> i32 {
2015 let Ok(delta) = u32::try_from(delta) else {
2016 return BOOL_FALSE;
2017 };
2018 let Some(table) = table_from_handle(&env, table_handle) else {
2019 return BOOL_FALSE;
2020 };
2021 let element_ty = table.ty(&env).ty;
2022 let init_val = if init <= INVALID_HANDLE {
2023 null_ref_for(element_ty)
2024 } else {
2025 ref_value_from_handle(&env, init, element_ty)
2026 };
2027 if table.grow(&mut env, delta, init_val).is_ok() {
2028 BOOL_TRUE
2029 } else {
2030 BOOL_FALSE
2031 }
2032}
2033
2034fn wasm_table_get(mut env: FunctionEnvMut<WasmCapiEnv>, table_handle: i32, index: i32) -> i32 {
2035 let Ok(index) = u32::try_from(index) else {
2036 return INVALID_HANDLE;
2037 };
2038 let Some(table) = table_from_handle(&env, table_handle) else {
2039 return INVALID_HANDLE;
2040 };
2041 match table.get(&mut env, index) {
2042 Some(value) => ref_value_to_handle(&mut env, &value),
2043 None => INVALID_HANDLE,
2044 }
2045}
2046
2047fn wasm_table_set(
2048 mut env: FunctionEnvMut<WasmCapiEnv>,
2049 table_handle: i32,
2050 index: i32,
2051 ref_handle: i32,
2052) -> i32 {
2053 let Ok(index) = u32::try_from(index) else {
2054 return BOOL_FALSE;
2055 };
2056 let Some(table) = table_from_handle(&env, table_handle) else {
2057 return BOOL_FALSE;
2058 };
2059 let element_ty = table.ty(&env).ty;
2060 let value = ref_value_from_handle(&env, ref_handle, element_ty);
2061 if table.set(&mut env, index, value).is_ok() {
2062 BOOL_TRUE
2063 } else {
2064 BOOL_FALSE
2065 }
2066}
2067
2068fn wasm_tabletype_new(
2069 mut env: FunctionEnvMut<WasmCapiEnv>,
2070 valtype_handle: i32,
2071 limits_ptr: i32,
2072) -> i32 {
2073 let ty = match env.data().state.get(valtype_handle) {
2074 Some(WasmObject::ValType(ty)) => *ty,
2075 _ => return INVALID_HANDLE,
2076 };
2077 let Some((min, max)) = read_limits(&mut env, limits_ptr) else {
2078 return INVALID_HANDLE;
2079 };
2080 insert(
2081 &mut env,
2082 WasmObject::TableType(TableType::new(ty, min, max)),
2083 )
2084}
2085
2086fn wasm_tabletype_element(mut env: FunctionEnvMut<WasmCapiEnv>, tabletype_handle: i32) -> i32 {
2087 let ty = match env.data().state.get(tabletype_handle) {
2088 Some(WasmObject::TableType(ty)) => ty.ty,
2089 _ => return INVALID_HANDLE,
2090 };
2091 insert(&mut env, WasmObject::ValType(ty))
2092}
2093
2094fn wasm_table_type(mut env: FunctionEnvMut<WasmCapiEnv>, table_handle: i32) -> i32 {
2095 let Some(table) = table_from_handle(&env, table_handle) else {
2096 return INVALID_HANDLE;
2097 };
2098 let ty = table.ty(&env);
2099 insert(&mut env, WasmObject::TableType(ty))
2100}
2101
2102fn wasm_table_new(
2103 mut env: FunctionEnvMut<WasmCapiEnv>,
2104 _store: i32,
2105 tabletype_handle: i32,
2106 init_handle: i32,
2107) -> i32 {
2108 let ty = match env.data().state.get(tabletype_handle) {
2109 Some(WasmObject::TableType(ty)) => *ty,
2110 _ => return INVALID_HANDLE,
2111 };
2112 let init_val = if init_handle <= INVALID_HANDLE {
2113 null_ref_for(ty.ty)
2114 } else {
2115 ref_value_from_handle(&env, init_handle, ty.ty)
2116 };
2117 match Table::new(&mut env, ty, init_val) {
2118 Ok(table) => insert(&mut env, WasmObject::Table(table)),
2119 Err(_) => INVALID_HANDLE,
2120 }
2121}
2122
2123fn wasm_func_as_ref(mut env: FunctionEnvMut<WasmCapiEnv>, func_handle: i32) -> i32 {
2124 let func = match env.data().state.get(func_handle) {
2125 Some(WasmObject::Func(func)) => func.clone(),
2126 Some(WasmObject::Extern(WasmExtern::Func(func))) => func.clone(),
2127 _ => return INVALID_HANDLE,
2128 };
2129 insert(&mut env, WasmObject::Ref(Value::FuncRef(Some(func))))
2130}
2131
2132fn wasm_ref_as_func(mut env: FunctionEnvMut<WasmCapiEnv>, ref_handle: i32) -> i32 {
2133 let func = match env.data().state.get(ref_handle) {
2134 Some(WasmObject::Ref(Value::FuncRef(Some(func)))) => func.clone(),
2135 _ => return INVALID_HANDLE,
2136 };
2137 insert(&mut env, WasmObject::Func(func))
2138}
2139
2140struct GuestForeign {
2143 host_info: std::cell::Cell<i32>,
2144 finalizer: std::cell::Cell<i32>,
2145}
2146
2147unsafe impl Send for GuestForeign {}
2150unsafe impl Sync for GuestForeign {}
2151
2152fn wasm_foreign_new(mut env: FunctionEnvMut<WasmCapiEnv>, _store: i32) -> i32 {
2153 let extern_ref = {
2154 let (_, mut store) = env.data_and_store_mut();
2155 ExternRef::new(
2156 &mut store,
2157 GuestForeign {
2158 host_info: std::cell::Cell::new(0),
2159 finalizer: std::cell::Cell::new(0),
2160 },
2161 )
2162 };
2163 insert(
2164 &mut env,
2165 WasmObject::Ref(Value::ExternRef(Some(extern_ref))),
2166 )
2167}
2168
2169fn wasm_ref_copy(mut env: FunctionEnvMut<WasmCapiEnv>, ref_handle: i32) -> i32 {
2170 match env.data().state.get(ref_handle) {
2171 Some(WasmObject::Ref(value)) => {
2172 let value = value.clone();
2173 insert(&mut env, WasmObject::Ref(value))
2174 }
2175 _ => INVALID_HANDLE,
2176 }
2177}
2178
2179fn ref_values_same(a: &Value, b: &Value) -> bool {
2181 match (a, b) {
2182 (Value::ExternRef(Some(x)), Value::ExternRef(Some(y))) => x.ptr_eq(y),
2183 (Value::ExternRef(None), Value::ExternRef(None)) => true,
2184 (Value::FuncRef(Some(x)), Value::FuncRef(Some(y))) => x == y,
2185 (Value::FuncRef(None), Value::FuncRef(None)) => true,
2186 _ => false,
2187 }
2188}
2189
2190fn wasm_ref_same(env: FunctionEnvMut<WasmCapiEnv>, ref1: i32, ref2: i32) -> i32 {
2191 if ref1 == ref2 {
2192 return BOOL_TRUE;
2193 }
2194 let state = &env.data().state;
2195 let (Some(WasmObject::Ref(a)), Some(WasmObject::Ref(b))) = (state.get(ref1), state.get(ref2))
2196 else {
2197 return BOOL_FALSE;
2198 };
2199 if ref_values_same(a, b) {
2200 BOOL_TRUE
2201 } else {
2202 BOOL_FALSE
2203 }
2204}
2205
2206fn with_guest_foreign<R>(
2208 env: &FunctionEnvMut<WasmCapiEnv>,
2209 ref_handle: i32,
2210 f: impl FnOnce(&GuestForeign) -> R,
2211) -> Option<R> {
2212 match env.data().state.get(ref_handle) {
2213 Some(WasmObject::Ref(Value::ExternRef(Some(e)))) => e.downcast::<GuestForeign>(env).map(f),
2214 _ => None,
2215 }
2216}
2217
2218fn wasm_ref_get_host_info(env: FunctionEnvMut<WasmCapiEnv>, ref_handle: i32) -> i32 {
2219 with_guest_foreign(&env, ref_handle, |g| g.host_info.get()).unwrap_or(0)
2220}
2221
2222fn wasm_ref_set_host_info(env: FunctionEnvMut<WasmCapiEnv>, ref_handle: i32, info: i32) {
2223 with_guest_foreign(&env, ref_handle, |g| {
2224 g.finalizer.set(0);
2225 g.host_info.set(info);
2226 });
2227}
2228
2229fn wasm_ref_set_host_info_with_finalizer(
2230 env: FunctionEnvMut<WasmCapiEnv>,
2231 ref_handle: i32,
2232 info: i32,
2233 finalizer: i32,
2234) {
2235 with_guest_foreign(&env, ref_handle, |g| {
2236 g.host_info.set(info);
2237 g.finalizer.set(finalizer);
2238 });
2239}
2240
2241fn wasm_trap_new(mut env: FunctionEnvMut<WasmCapiEnv>, _store: i32, message_ptr: i32) -> i32 {
2242 let bytes = read_byte_vec(&mut env, message_ptr).unwrap_or_default();
2243 let mut message = String::from_utf8_lossy(&bytes).to_string();
2244 if message.ends_with('\0') {
2245 message.pop();
2246 }
2247 insert(&mut env, WasmObject::Trap(message))
2248}
2249
2250fn wasm_trap_message(mut env: FunctionEnvMut<WasmCapiEnv>, trap_handle: i32, out_ptr: i32) {
2251 let message = match env.data().state.get(trap_handle) {
2252 Some(WasmObject::Trap(message)) => message.clone(),
2253 _ => "WebAssembly trap".to_string(),
2254 };
2255 let mut bytes = message.into_bytes();
2256 bytes.push(0);
2257 write_byte_vec(&mut env, out_ptr, &bytes);
2258}
2259
2260fn wasm_byte_vec_new(mut env: FunctionEnvMut<WasmCapiEnv>, out_ptr: i32, size: i32, data_ptr: i32) {
2261 if out_ptr <= INVALID_HANDLE || size < 0 || data_ptr < 0 {
2262 return;
2263 }
2264 let Ok(size) = usize::try_from(size) else {
2265 return;
2266 };
2267 let bytes = if size == 0 {
2268 Vec::new()
2269 } else {
2270 let Some(bytes) = read_guest_bytes(&mut env, data_ptr, size) else {
2271 return;
2272 };
2273 bytes
2274 };
2275 write_byte_vec(&mut env, out_ptr, &bytes);
2276}
2277
2278fn wasm_val_vec_new_uninitialized(mut env: FunctionEnvMut<WasmCapiEnv>, out_ptr: i32, size: i32) {
2279 if out_ptr <= INVALID_HANDLE || size < 0 {
2280 return;
2281 }
2282 let Ok(len) = u32::try_from(size) else {
2283 return;
2284 };
2285 let Ok(size) = usize::try_from(size) else {
2286 return;
2287 };
2288 let Some(byte_len) = size.checked_mul(WASM_VAL_SIZE) else {
2289 return;
2290 };
2291
2292 if byte_len == 0 {
2293 let Some(out_ptr) = non_null_guest_ptr(out_ptr) else {
2294 return;
2295 };
2296 write_guest_vec_header(&mut env, out_ptr.get(), len, 0);
2297 return;
2298 }
2299
2300 let Some(mut data) = GuestAllocation::new(&mut env, byte_len) else {
2301 return;
2302 };
2303 let Some(out_ptr) = non_null_guest_ptr(out_ptr) else {
2304 return;
2305 };
2306 if data.write_vec_header(out_ptr, len) {
2307 let _ = data.into_raw();
2308 }
2309}
2310
2311fn noop_delete(_env: FunctionEnvMut<WasmCapiEnv>, _handle: i32) {}
2312
2313fn handle_identity(_env: FunctionEnvMut<WasmCapiEnv>, handle: i32) -> i32 {
2316 handle
2317}
2318
2319fn vec_delete(mut env: FunctionEnvMut<WasmCapiEnv>, vec_ptr: i32) {
2320 if let Some(vec_ptr) = non_null_guest_ptr(vec_ptr) {
2321 write_guest_vec_header(&mut env, vec_ptr.get(), 0, 0);
2322 }
2323}
2324
2325fn register_wasm_c_api_imports(
2326 store: &mut impl wasmer_api::AsStoreMut,
2327 fe: &FunctionEnv<WasmCapiEnv>,
2328 io: &mut Imports,
2329) {
2330 fe.as_mut(&mut *store).func_env = Some(fe.clone());
2331
2332 let ns = namespace! {
2333 "wasm_engine_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_engine_new),
2334 "wasm_engine_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2335 "wasm_store_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_store_new),
2336 "wasm_store_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2337 "wasm_module_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_module_new),
2338 "wasm_module_validate" => WasmerFunction::new_typed_with_env(store, fe, wasm_module_validate),
2339 "wasm_module_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2340 "wasm_module_imports" => WasmerFunction::new_typed_with_env(store, fe, wasm_module_imports),
2341 "wasm_module_exports" => WasmerFunction::new_typed_with_env(store, fe, wasm_module_exports),
2342 "wasm_importtype_module" => WasmerFunction::new_typed_with_env(store, fe, wasm_importtype_module),
2343 "wasm_importtype_name" => WasmerFunction::new_typed_with_env(store, fe, wasm_importtype_name),
2344 "wasm_importtype_type" => WasmerFunction::new_typed_with_env(store, fe, wasm_importtype_type),
2345 "wasm_importtype_vec_delete" => WasmerFunction::new_typed_with_env(store, fe, vec_delete),
2346 "wasm_exporttype_name" => WasmerFunction::new_typed_with_env(store, fe, wasm_exporttype_name),
2347 "wasm_exporttype_type" => WasmerFunction::new_typed_with_env(store, fe, wasm_exporttype_type),
2348 "wasm_exporttype_vec_delete" => WasmerFunction::new_typed_with_env(store, fe, vec_delete),
2349 "wasm_externtype_kind" => WasmerFunction::new_typed_with_env(store, fe, wasm_externtype_kind),
2350 "wasm_externtype_as_functype_const" => WasmerFunction::new_typed_with_env(store, fe, wasm_externtype_as_functype_const),
2351 "wasm_functype_copy" => WasmerFunction::new_typed_with_env(store, fe, wasm_functype_copy),
2352 "wasm_functype_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2353 "wasm_functype_params" => WasmerFunction::new_typed_with_env(store, fe, wasm_functype_params),
2354 "wasm_functype_results" => WasmerFunction::new_typed_with_env(store, fe, wasm_functype_results),
2355 "wasm_valtype_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_valtype_new),
2356 "wasm_valtype_kind" => WasmerFunction::new_typed_with_env(store, fe, wasm_valtype_kind),
2357 "wasm_val_delete" => WasmerFunction::new_typed_with_env(store, fe, noop_delete),
2358 "wasm_val_vec_new_uninitialized" => WasmerFunction::new_typed_with_env(store, fe, wasm_val_vec_new_uninitialized),
2359 "wasm_val_vec_delete" => WasmerFunction::new_typed_with_env(store, fe, vec_delete),
2360 "wasm_memorytype_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_memorytype_new),
2361 "wasm_memorytype_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2362 "wasm_memory_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_memory_new),
2363 "wasm_memory_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2364 "wasm_memory_copy" => WasmerFunction::new_typed_with_env(store, fe, wasm_memory_copy),
2365 "wasm_memory_size" => WasmerFunction::new_typed_with_env(store, fe, wasm_memory_size),
2366 "wasm_memory_grow" => WasmerFunction::new_typed_with_env(store, fe, wasm_memory_grow),
2367 "wasm_memory_data" => WasmerFunction::new_typed_with_env(store, fe, wasm_memory_data),
2368 "wasm_memory_data_size" => WasmerFunction::new_typed_with_env(store, fe, wasm_memory_data_size),
2369 "wasm_memory_as_extern" => WasmerFunction::new_typed_with_env(store, fe, wasm_memory_as_extern),
2370 "wasm_globaltype_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_globaltype_new),
2371 "wasm_globaltype_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2372 "wasm_globaltype_content" => WasmerFunction::new_typed_with_env(store, fe, wasm_globaltype_content),
2373 "wasm_globaltype_mutability" => WasmerFunction::new_typed_with_env(store, fe, wasm_globaltype_mutability),
2374 "wasm_global_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_global_new),
2375 "wasm_global_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2376 "wasm_global_copy" => WasmerFunction::new_typed_with_env(store, fe, wasm_global_copy),
2377 "wasm_global_type" => WasmerFunction::new_typed_with_env(store, fe, wasm_global_type),
2378 "wasm_global_get" => WasmerFunction::new_typed_with_env(store, fe, wasm_global_get),
2379 "wasm_global_set" => WasmerFunction::new_typed_with_env(store, fe, wasm_global_set),
2380 "wasm_global_as_extern" => WasmerFunction::new_typed_with_env(store, fe, wasm_global_as_extern),
2381 "wasm_table_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_table_new),
2382 "wasm_table_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2383 "wasm_table_copy" => WasmerFunction::new_typed_with_env(store, fe, wasm_table_copy),
2384 "wasm_table_type" => WasmerFunction::new_typed_with_env(store, fe, wasm_table_type),
2385 "wasm_table_size" => WasmerFunction::new_typed_with_env(store, fe, wasm_table_size),
2386 "wasm_table_grow" => WasmerFunction::new_typed_with_env(store, fe, wasm_table_grow),
2387 "wasm_table_get" => WasmerFunction::new_typed_with_env(store, fe, wasm_table_get),
2388 "wasm_table_set" => WasmerFunction::new_typed_with_env(store, fe, wasm_table_set),
2389 "wasm_table_as_extern" => WasmerFunction::new_typed_with_env(store, fe, wasm_table_as_extern),
2390 "wasm_tabletype_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_tabletype_new),
2391 "wasm_tabletype_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2392 "wasm_tabletype_element" => WasmerFunction::new_typed_with_env(store, fe, wasm_tabletype_element),
2393 "wasm_func_as_ref" => WasmerFunction::new_typed_with_env(store, fe, wasm_func_as_ref),
2394 "wasm_ref_as_func" => WasmerFunction::new_typed_with_env(store, fe, wasm_ref_as_func),
2395 "wasm_foreign_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_foreign_new),
2396 "wasm_foreign_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2397 "wasm_foreign_as_ref" => WasmerFunction::new_typed_with_env(store, fe, handle_identity),
2398 "wasm_ref_as_foreign" => WasmerFunction::new_typed_with_env(store, fe, handle_identity),
2399 "wasm_ref_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2400 "wasm_ref_copy" => WasmerFunction::new_typed_with_env(store, fe, wasm_ref_copy),
2401 "wasm_ref_same" => WasmerFunction::new_typed_with_env(store, fe, wasm_ref_same),
2402 "wasm_ref_get_host_info" => WasmerFunction::new_typed_with_env(store, fe, wasm_ref_get_host_info),
2403 "wasm_ref_set_host_info" => WasmerFunction::new_typed_with_env(store, fe, wasm_ref_set_host_info),
2404 "wasm_ref_set_host_info_with_finalizer" => WasmerFunction::new_typed_with_env(store, fe, wasm_ref_set_host_info_with_finalizer),
2405 "wasm_instance_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_instance_new),
2406 "wasm_instance_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2407 "wasm_instance_exports" => WasmerFunction::new_typed_with_env(store, fe, wasm_instance_exports),
2408 "wasm_extern_kind" => WasmerFunction::new_typed_with_env(store, fe, wasm_extern_kind),
2409 "wasm_extern_as_func" => WasmerFunction::new_typed_with_env(store, fe, wasm_extern_as_func),
2410 "wasm_extern_as_global" => WasmerFunction::new_typed_with_env(store, fe, wasm_extern_as_global),
2411 "wasm_extern_as_table" => WasmerFunction::new_typed_with_env(store, fe, wasm_extern_as_table),
2412 "wasm_extern_as_memory" => WasmerFunction::new_typed_with_env(store, fe, wasm_extern_as_memory),
2413 "wasm_extern_vec_delete" => WasmerFunction::new_typed_with_env(store, fe, vec_delete),
2414 "wasm_func_copy" => WasmerFunction::new_typed_with_env(store, fe, wasm_func_copy),
2415 "wasm_func_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2416 "wasm_func_as_extern" => WasmerFunction::new_typed_with_env(store, fe, wasm_func_as_extern),
2417 "wasm_func_type" => WasmerFunction::new_typed_with_env(store, fe, wasm_func_type),
2418 "wasm_func_call" => WasmerFunction::new_typed_with_env(store, fe, wasm_func_call),
2419 "wasm_func_new_with_env" => WasmerFunction::new_typed_with_env(store, fe, wasm_func_new_with_env),
2420 "wasm_trap_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_trap_new),
2421 "wasm_trap_delete" => WasmerFunction::new_typed_with_env(store, fe, delete_handle),
2422 "wasm_trap_message" => WasmerFunction::new_typed_with_env(store, fe, wasm_trap_message),
2423 "wasm_byte_vec_new" => WasmerFunction::new_typed_with_env(store, fe, wasm_byte_vec_new),
2424 "wasm_byte_vec_delete" => WasmerFunction::new_typed_with_env(store, fe, vec_delete),
2425 };
2426
2427 io.register_namespace(WASM_C_API_MODULE_NAME, ns);
2428}
2429
2430#[cfg(test)]
2431mod tests {
2432 use super::{
2433 BOOL_FALSE, BOOL_TRUE, INVALID_HANDLE, INVALID_SIZE, MemoryShadow, Type, WASM_EXTERNREF,
2434 WASM_F64, WASM_FUNCREF, WASM_I32, WASM_VAL_PAYLOAD_OFFSET, WasmCAPIVersion, WasmCapiEnv,
2435 WasmCapiInstantiationState, WasmCapiRuntimeHooks, WasmCapiState, WasmObject,
2436 copy_guest_memory_to_wasmer, copy_wasmer_memory_to_guest, guest_byte_ptr,
2437 guest_memory_offset, guest_ptr_with_offset, module_wasm_c_api_version_used,
2438 non_null_guest_ptr, read_wasm_val, ref_values_same, refresh_memory_shadows_from_wasmer,
2439 sync_memory_shadows_to_wasmer, type_to_wasm_kind, wasm_foreign_new, wasm_func_as_ref,
2440 wasm_kind_to_type, wasm_memory_data, wasm_memory_data_size, wasm_memory_size,
2441 wasm_ref_as_func, wasm_ref_copy, wasm_ref_get_host_info, wasm_ref_same,
2442 wasm_ref_set_host_info, wasm_table_get, wasm_table_grow, wasm_table_set, wasm_table_size,
2443 write_wasm_val,
2444 };
2445 use wasmer_api::{
2446 AsStoreMut, Function, FunctionEnv, Instance, Memory, MemoryType, Module, Pages, Store,
2447 Table, TableType, Value,
2448 };
2449 use wat::parse_str;
2450
2451 #[cfg(feature = "wasi")]
2452 use std::sync::{
2453 Arc,
2454 atomic::{AtomicUsize, Ordering},
2455 };
2456 #[cfg(feature = "wasi")]
2457 use wasmer_types::ModuleHash;
2458 #[cfg(feature = "wasi")]
2459 use wasmer_wasix::{
2460 PluggableRuntime, WasiError,
2461 runners::wasi::{RuntimeOrEngine, WasiRunner},
2462 runtime::task_manager::tokio::TokioTaskManager,
2463 };
2464
2465 const EMPTY_WASM_MODULE: &[u8] = b"\0asm\x01\0\0\0";
2466
2467 fn compile_wat(store: &Store, wat: &str) -> Module {
2468 let wasm = parse_str(wat).expect("wat module parses");
2469 Module::new(store, wasm).expect("wat module compiles")
2470 }
2471
2472 #[test]
2473 fn module_wasm_c_api_version_used_detects_none() {
2474 let store = Store::default();
2475 let module = Module::new(&store, EMPTY_WASM_MODULE).expect("empty wasm module compiles");
2476
2477 assert_eq!(module_wasm_c_api_version_used(&module), None);
2478 }
2479
2480 #[test]
2481 fn module_wasm_c_api_version_used_detects_v0() {
2482 let store = Store::default();
2483 let module = compile_wat(
2484 &store,
2485 r#"(module
2486 (import "wasm_c_api_v0" "wasm_engine_new" (func))
2487 )"#,
2488 );
2489
2490 assert_eq!(
2491 module_wasm_c_api_version_used(&module),
2492 Some(WasmCAPIVersion::V0)
2493 );
2494 }
2495
2496 #[test]
2497 fn module_wasm_c_api_version_used_detects_unknown_version() {
2498 let store = Store::default();
2499 let module = compile_wat(
2500 &store,
2501 r#"(module
2502 (import "wasm_c_api_v1" "wasm_engine_new" (func))
2503 )"#,
2504 );
2505
2506 assert_eq!(
2507 module_wasm_c_api_version_used(&module),
2508 Some(WasmCAPIVersion::Unknown)
2509 );
2510 }
2511
2512 #[test]
2513 fn module_wasm_c_api_version_used_detects_mixed_versions() {
2514 let store = Store::default();
2515 let module = compile_wat(
2516 &store,
2517 r#"(module
2518 (import "wasm_c_api_v0" "wasm_engine_new" (func))
2519 (import "wasm_c_api_v1" "wasm_store_new" (func))
2520 )"#,
2521 );
2522
2523 assert_eq!(
2524 module_wasm_c_api_version_used(&module),
2525 Some(WasmCAPIVersion::Unknown)
2526 );
2527 }
2528
2529 #[test]
2530 fn wasm_c_api_version_compatibility_is_strict() {
2531 assert!(WasmCAPIVersion::V0.is_compatible_with(WasmCAPIVersion::V0));
2532 assert!(!WasmCAPIVersion::V0.is_compatible_with(WasmCAPIVersion::Unknown));
2533 assert!(!WasmCAPIVersion::Unknown.is_compatible_with(WasmCAPIVersion::V0));
2534 assert!(!WasmCAPIVersion::Unknown.is_compatible_with(WasmCAPIVersion::Unknown));
2535 }
2536
2537 #[cfg(feature = "wasi")]
2538 #[test]
2539 fn wasm_c_api_imports_run_from_wasix_guest() {
2540 let wasm = parse_str(
2541 r#"(module
2542 (import "wasi_snapshot_preview1" "proc_exit" (func $proc_exit (param i32)))
2543 (import "wasm_c_api_v0" "wasm_engine_new" (func $wasm_engine_new (result i32)))
2544 (import "wasm_c_api_v0" "wasm_store_new" (func $wasm_store_new (param i32) (result i32)))
2545 (import "wasm_c_api_v0" "wasm_module_validate" (func $wasm_module_validate (param i32 i32) (result i32)))
2546 (import "wasm_c_api_v0" "wasm_module_new" (func $wasm_module_new (param i32 i32) (result i32)))
2547
2548 (memory (export "memory") 1)
2549 ;; wasm_byte_vec_t { size: 8, data: 32 }
2550 (data (i32.const 16) "\08\00\00\00\20\00\00\00")
2551 ;; Empty wasm module: \0asm + version 1.
2552 (data (i32.const 32) "\00asm\01\00\00\00")
2553
2554 (func (export "_start")
2555 (local $engine i32)
2556 (local $store i32)
2557 (local $module i32)
2558
2559 (local.set $engine (call $wasm_engine_new))
2560 (if (i32.eqz (local.get $engine))
2561 (then (call $proc_exit (i32.const 10))))
2562
2563 (local.set $store (call $wasm_store_new (local.get $engine)))
2564 (if (i32.eqz (local.get $store))
2565 (then (call $proc_exit (i32.const 11))))
2566
2567 (if (i32.eqz (call $wasm_module_validate (local.get $store) (i32.const 16)))
2568 (then (call $proc_exit (i32.const 12))))
2569
2570 (local.set $module (call $wasm_module_new (local.get $store) (i32.const 16)))
2571 (if (i32.eqz (local.get $module))
2572 (then (call $proc_exit (i32.const 13))))
2573 )
2574 )"#,
2575 )
2576 .expect("guest wat parses");
2577 let store = Store::default();
2578 let module = Module::new(&store, &wasm).expect("guest module compiles");
2579
2580 let tokio_runtime = tokio::runtime::Builder::new_multi_thread()
2581 .enable_all()
2582 .build()
2583 .expect("tokio runtime starts");
2584 let _guard = tokio_runtime.enter();
2585 let mut runtime = PluggableRuntime::new(Arc::new(TokioTaskManager::new(
2586 tokio_runtime.handle().clone(),
2587 )));
2588 runtime.set_engine(store.engine().clone());
2589
2590 let resolve_calls = Arc::new(AtomicUsize::new(0));
2591 let resolve_engine = store.engine().clone();
2592 let hooks = WasmCapiRuntimeHooks::new().with_resolve_module_sync({
2593 let resolve_calls = resolve_calls.clone();
2594 move |bytes| {
2595 resolve_calls.fetch_add(1, Ordering::SeqCst);
2596 Ok(Module::new(&resolve_engine, bytes)?)
2597 }
2598 });
2599 runtime.with_instantiation_hook(hooks);
2600
2601 let result = WasiRunner::new().run_wasm(
2602 RuntimeOrEngine::Runtime(Arc::new(runtime)),
2603 "wasm-c-api-smoke",
2604 module,
2605 ModuleHash::new(&wasm),
2606 );
2607
2608 match result {
2609 Ok(()) => {}
2610 Err(err) => {
2611 if let Some(WasiError::Exit(code)) = err.downcast_ref::<WasiError>() {
2612 panic!("guest exited with status {code}");
2613 }
2614 panic!("guest failed: {err:?}");
2615 }
2616 }
2617 assert_eq!(resolve_calls.load(Ordering::SeqCst), 1);
2618 }
2619
2620 #[test]
2621 fn configure_instance_pairs_sessions_by_state() {
2622 let mut store_a = Store::default();
2623 let mut store_b = Store::new(store_a.engine().clone());
2624 let module = compile_wat(
2625 &store_a,
2626 r#"(module
2627 (import "wasm_c_api_v0" "wasm_engine_new" (func $wasm_engine_new (result i32)))
2628 (memory (export "memory") 1)
2629 )"#,
2630 );
2631
2632 let hooks = WasmCapiRuntimeHooks::new();
2633 let (imports_a, state_a) = hooks
2634 .additional_imports(&module, &mut store_a.as_store_mut())
2635 .expect("imports register for store A");
2636 assert!(
2637 state_a.session.is_some(),
2638 "session state travels with the caller"
2639 );
2640 let (imports_b, state_b) = hooks
2641 .additional_imports(&module, &mut store_b.as_store_mut())
2642 .expect("imports register for store B");
2643
2644 let instance_b = Instance::new(&mut store_b, &module, &imports_b).expect("instance B");
2650 hooks
2651 .configure_instance(
2652 &module,
2653 &mut store_b.as_store_mut(),
2654 &instance_b,
2655 None,
2656 state_b,
2657 )
2658 .expect("configure instance B");
2659 let instance_a = Instance::new(&mut store_a, &module, &imports_a).expect("instance A");
2660 hooks
2661 .configure_instance(
2662 &module,
2663 &mut store_a.as_store_mut(),
2664 &instance_a,
2665 None,
2666 state_a,
2667 )
2668 .expect("configure instance A");
2669 }
2670
2671 #[test]
2672 fn configure_instance_rejects_missing_state() {
2673 let mut store = Store::default();
2674 let module = compile_wat(
2675 &store,
2676 r#"(module
2677 (import "wasm_c_api_v0" "wasm_engine_new" (func $wasm_engine_new (result i32)))
2678 )"#,
2679 );
2680
2681 let hooks = WasmCapiRuntimeHooks::new();
2682 let (imports, _state) = hooks
2683 .additional_imports(&module, &mut store.as_store_mut())
2684 .expect("imports register");
2685 let instance = Instance::new(&mut store, &module, &imports).expect("instance");
2686
2687 let empty_state = WasmCapiInstantiationState { session: None };
2688 let err = hooks
2689 .configure_instance(
2690 &module,
2691 &mut store.as_store_mut(),
2692 &instance,
2693 None,
2694 empty_state,
2695 )
2696 .expect_err("configuring a Wasm C API instance without its session must fail");
2697 assert!(err.to_string().contains("missing Wasm C API session"));
2698 }
2699
2700 #[test]
2701 fn wasm_capi_state_allocates_positive_non_reused_handles() {
2702 let mut state = WasmCapiState::default();
2703
2704 let first = state.insert(WasmObject::Engine);
2705 assert_eq!(first, 1);
2706 assert!(state.get(first).is_some());
2707
2708 let _ = state.remove(first);
2709 assert!(state.get(first).is_none());
2710
2711 let second = state.insert(WasmObject::Store);
2712 assert_eq!(second, 2);
2713 assert!(state.get(INVALID_HANDLE).is_none());
2714 assert!(state.get(-1).is_none());
2715 }
2716
2717 #[test]
2718 fn wasm_capi_state_fails_closed_on_handle_exhaustion() {
2719 let mut state = WasmCapiState {
2720 next_handle: i32::MAX,
2721 ..WasmCapiState::default()
2722 };
2723
2724 assert_eq!(state.insert(WasmObject::Engine), i32::MAX);
2725 assert_eq!(state.insert(WasmObject::Store), INVALID_HANDLE);
2726 }
2727
2728 #[test]
2729 fn guest_pointer_helpers_reject_invalid_values() {
2730 assert_eq!(guest_memory_offset(-1), None);
2731 assert_eq!(guest_memory_offset(0), Some(0));
2732 assert_eq!(guest_byte_ptr(-1), None);
2733 assert_eq!(guest_byte_ptr(1).map(|ptr| ptr.offset()), Some(1));
2734 assert_eq!(non_null_guest_ptr(0), None);
2735 assert_eq!(non_null_guest_ptr(1).map(|ptr| ptr.get()), Some(1));
2736 assert_eq!(guest_ptr_with_offset(i32::MAX, 4), None);
2737 }
2738
2739 #[test]
2740 fn size_apis_distinguish_invalid_from_zero() {
2741 let mut store = Store::default();
2742 let memory = Memory::new(&mut store, MemoryType::new(Pages(0), Some(Pages(1)), false))
2743 .expect("zero-page memory can be created");
2744 let table = Table::new(
2745 &mut store,
2746 TableType::new(Type::FuncRef, 0, Some(1)),
2747 Value::FuncRef(None),
2748 )
2749 .expect("zero-sized table can be created");
2750 let func_env = FunctionEnv::new(&mut store, WasmCapiEnv::default());
2751 let (memory_handle, table_handle) = {
2752 let env = func_env.as_mut(&mut store);
2753 let memory_handle = env.state.insert(WasmObject::Memory(memory));
2754 let table_handle = env.state.insert(WasmObject::Table(table));
2755 (memory_handle, table_handle)
2756 };
2757
2758 assert_eq!(
2759 wasm_memory_size(func_env.clone().into_mut(&mut store), INVALID_HANDLE),
2760 INVALID_SIZE
2761 );
2762 assert_eq!(
2763 wasm_memory_data_size(func_env.clone().into_mut(&mut store), INVALID_HANDLE),
2764 INVALID_SIZE
2765 );
2766 assert_eq!(
2767 wasm_table_size(func_env.clone().into_mut(&mut store), INVALID_HANDLE),
2768 INVALID_SIZE
2769 );
2770 assert_eq!(
2771 wasm_memory_size(func_env.clone().into_mut(&mut store), memory_handle),
2772 0
2773 );
2774 assert_eq!(
2775 wasm_memory_data_size(func_env.clone().into_mut(&mut store), memory_handle),
2776 0
2777 );
2778 assert_eq!(
2779 wasm_table_size(func_env.into_mut(&mut store), table_handle),
2780 0
2781 );
2782 }
2783
2784 #[test]
2785 fn shared_memory_data_shadow_fails_closed() {
2786 let mut store = Store::default();
2787 let memory = Memory::new(&mut store, MemoryType::new(Pages(1), Some(Pages(2)), true))
2788 .expect("shared memory can be created");
2789 let func_env = FunctionEnv::new(&mut store, WasmCapiEnv::default());
2790 let memory_handle = func_env
2791 .as_mut(&mut store)
2792 .state
2793 .insert(WasmObject::Memory(memory));
2794
2795 assert_eq!(
2796 wasm_memory_data_size(func_env.clone().into_mut(&mut store), memory_handle),
2797 INVALID_SIZE
2798 );
2799 assert_eq!(
2800 wasm_memory_data(func_env.into_mut(&mut store), memory_handle),
2801 INVALID_HANDLE
2802 );
2803 }
2804
2805 #[test]
2806 fn memory_shadow_copy_rejects_same_memory() {
2807 let mut store = Store::default();
2808 let memory = Memory::new(&mut store, MemoryType::new(Pages(1), Some(Pages(1)), false))
2809 .expect("memory can be created");
2810 let func_env = FunctionEnv::new(
2811 &mut store,
2812 WasmCapiEnv {
2813 memory: Some(memory.clone()),
2814 ..WasmCapiEnv::default()
2815 },
2816 );
2817 let mut env = func_env.into_mut(&mut store);
2818
2819 assert!(!copy_wasmer_memory_to_guest(&mut env, &memory, 16, 4));
2820 assert!(!copy_guest_memory_to_wasmer(&mut env, 16, &memory, 4));
2821 }
2822
2823 #[test]
2824 fn memory_shadow_sync_preserves_registered_shadows() {
2825 let mut store = Store::default();
2826 let guest_memory =
2827 Memory::new(&mut store, MemoryType::new(Pages(1), Some(Pages(1)), false))
2828 .expect("guest memory can be created");
2829 let memory = Memory::new(&mut store, MemoryType::new(Pages(1), Some(Pages(1)), false))
2830 .expect("memory can be created");
2831 guest_memory
2832 .view(&store)
2833 .write(16, &[1, 2, 3, 4])
2834 .expect("guest shadow write succeeds");
2835 let func_env = FunctionEnv::new(
2836 &mut store,
2837 WasmCapiEnv {
2838 memory: Some(guest_memory.clone()),
2839 ..WasmCapiEnv::default()
2840 },
2841 );
2842 let memory_handle = {
2843 let env = func_env.as_mut(&mut store);
2844 let memory_handle = env.state.insert(WasmObject::Memory(memory.clone()));
2845 env.state.memory_shadows.insert(
2846 memory_handle,
2847 MemoryShadow {
2848 guest_ptr: 16,
2849 len: 4,
2850 },
2851 );
2852 memory_handle
2853 };
2854
2855 {
2856 let mut env = func_env.clone().into_mut(&mut store);
2857 sync_memory_shadows_to_wasmer(&mut env);
2858 assert!(env.data().state.memory_shadows.contains_key(&memory_handle));
2859 }
2860 let mut bytes = [0; 4];
2861 memory
2862 .view(&store)
2863 .read(0, &mut bytes)
2864 .expect("memory read succeeds");
2865 assert_eq!(bytes, [1, 2, 3, 4]);
2866
2867 memory
2868 .view(&store)
2869 .write(0, &[5, 6, 7, 8])
2870 .expect("memory write succeeds");
2871 {
2872 let mut env = func_env.into_mut(&mut store);
2873 refresh_memory_shadows_from_wasmer(&mut env);
2874 assert!(env.data().state.memory_shadows.contains_key(&memory_handle));
2875 }
2876 guest_memory
2877 .view(&store)
2878 .read(16, &mut bytes)
2879 .expect("guest shadow read succeeds");
2880 assert_eq!(bytes, [5, 6, 7, 8]);
2881 }
2882
2883 #[test]
2884 fn wasm_kind_conversion_is_explicit_for_supported_types() {
2885 assert_eq!(type_to_wasm_kind(Type::I32), Some(WASM_I32));
2886 assert_eq!(type_to_wasm_kind(Type::F64), Some(WASM_F64));
2887 assert_eq!(type_to_wasm_kind(Type::ExternRef), Some(WASM_EXTERNREF));
2888 assert_eq!(type_to_wasm_kind(Type::FuncRef), Some(WASM_FUNCREF));
2889 assert_eq!(type_to_wasm_kind(Type::V128), None);
2890 assert_eq!(type_to_wasm_kind(Type::ExceptionRef), None);
2891
2892 assert_eq!(wasm_kind_to_type(i32::from(WASM_I32)), Some(Type::I32));
2893 assert_eq!(
2894 wasm_kind_to_type(i32::from(WASM_EXTERNREF)),
2895 Some(Type::ExternRef)
2896 );
2897 assert_eq!(wasm_kind_to_type(-1), None);
2898 }
2899
2900 #[test]
2901 fn null_ref_values_marshal_to_a_null_handle() {
2902 let mut store = Store::default();
2903 let memory = Memory::new(&mut store, MemoryType::new(Pages(1), Some(Pages(1)), false))
2904 .expect("memory can be created");
2905 let func_env = FunctionEnv::new(
2906 &mut store,
2907 WasmCapiEnv {
2908 memory: Some(memory.clone()),
2909 ..WasmCapiEnv::default()
2910 },
2911 );
2912
2913 for (value, kind) in [
2915 (Value::FuncRef(None), WASM_FUNCREF),
2916 (Value::ExternRef(None), WASM_EXTERNREF),
2917 ] {
2918 let val_ptr = 16;
2919 assert!(write_wasm_val(
2920 &mut func_env.clone().into_mut(&mut store),
2921 val_ptr,
2922 &value
2923 ));
2924 let mut kind_byte = [0u8; 1];
2925 memory
2926 .view(&store)
2927 .read(val_ptr as u64, &mut kind_byte)
2928 .expect("kind read succeeds");
2929 assert_eq!(kind_byte[0], kind);
2930 let mut handle = [0u8; 4];
2931 memory
2932 .view(&store)
2933 .read((val_ptr + WASM_VAL_PAYLOAD_OFFSET) as u64, &mut handle)
2934 .expect("handle read succeeds");
2935 assert_eq!(i32::from_le_bytes(handle), INVALID_HANDLE);
2936 }
2937 }
2938
2939 #[test]
2940 fn ref_values_same_matches_identity_and_kind() {
2941 assert!(ref_values_same(
2942 &Value::ExternRef(None),
2943 &Value::ExternRef(None)
2944 ));
2945 assert!(ref_values_same(
2946 &Value::FuncRef(None),
2947 &Value::FuncRef(None)
2948 ));
2949 assert!(!ref_values_same(
2951 &Value::ExternRef(None),
2952 &Value::FuncRef(None)
2953 ));
2954 }
2955
2956 #[cfg(feature = "sys")]
2958 #[test]
2959 fn foreign_ref_host_info_and_copy_share_the_payload() {
2960 let mut store = Store::default();
2961 let func_env = FunctionEnv::new(&mut store, WasmCapiEnv::default());
2962
2963 let ref_handle = wasm_foreign_new(func_env.clone().into_mut(&mut store), 0);
2964 assert!(ref_handle > INVALID_HANDLE);
2965 assert_eq!(
2967 wasm_ref_get_host_info(func_env.clone().into_mut(&mut store), ref_handle),
2968 0
2969 );
2970 wasm_ref_set_host_info(func_env.clone().into_mut(&mut store), ref_handle, 42);
2972 assert_eq!(
2973 wasm_ref_get_host_info(func_env.clone().into_mut(&mut store), ref_handle),
2974 42
2975 );
2976
2977 let copy = wasm_ref_copy(func_env.clone().into_mut(&mut store), ref_handle);
2980 assert!(copy > INVALID_HANDLE && copy != ref_handle);
2981 assert_eq!(
2982 wasm_ref_same(func_env.clone().into_mut(&mut store), ref_handle, copy),
2983 BOOL_TRUE
2984 );
2985 assert_eq!(
2986 wasm_ref_get_host_info(func_env.clone().into_mut(&mut store), copy),
2987 42
2988 );
2989
2990 let other = wasm_foreign_new(func_env.clone().into_mut(&mut store), 0);
2992 assert_eq!(
2993 wasm_ref_same(func_env.clone().into_mut(&mut store), ref_handle, other),
2994 BOOL_FALSE
2995 );
2996 }
2997
2998 #[cfg(feature = "sys")]
2999 #[test]
3000 fn externref_table_get_set_grow_via_handles() {
3001 let mut store = Store::default();
3002 let table = Table::new(
3003 &mut store,
3004 TableType::new(Type::ExternRef, 2, Some(10)),
3005 Value::ExternRef(None),
3006 )
3007 .expect("externref table can be created");
3008 let func_env = FunctionEnv::new(&mut store, WasmCapiEnv::default());
3009 let table_handle = {
3010 let mut env = func_env.clone().into_mut(&mut store);
3011 env.data_mut().state.insert(WasmObject::Table(table))
3012 };
3013
3014 assert_eq!(
3016 wasm_table_get(func_env.clone().into_mut(&mut store), table_handle, 0),
3017 INVALID_HANDLE
3018 );
3019
3020 let ref_handle = wasm_foreign_new(func_env.clone().into_mut(&mut store), 0);
3021 assert_eq!(
3022 wasm_table_set(
3023 func_env.clone().into_mut(&mut store),
3024 table_handle,
3025 0,
3026 ref_handle
3027 ),
3028 BOOL_TRUE
3029 );
3030
3031 let got = wasm_table_get(func_env.clone().into_mut(&mut store), table_handle, 0);
3033 assert!(got > INVALID_HANDLE);
3034 assert_eq!(
3035 wasm_ref_same(func_env.clone().into_mut(&mut store), got, ref_handle),
3036 BOOL_TRUE
3037 );
3038
3039 assert_eq!(
3041 wasm_table_set(
3042 func_env.clone().into_mut(&mut store),
3043 table_handle,
3044 5,
3045 ref_handle
3046 ),
3047 BOOL_FALSE
3048 );
3049
3050 assert_eq!(
3052 wasm_table_grow(
3053 func_env.clone().into_mut(&mut store),
3054 table_handle,
3055 3,
3056 INVALID_HANDLE
3057 ),
3058 BOOL_TRUE
3059 );
3060 assert_eq!(
3061 wasm_table_size(func_env.clone().into_mut(&mut store), table_handle),
3062 5
3063 );
3064 }
3065
3066 #[cfg(feature = "sys")]
3067 #[test]
3068 fn func_as_ref_and_back_roundtrips() {
3069 let mut store = Store::default();
3070 let func = Function::new_typed(&mut store, |x: i32| x);
3071 let func_env = FunctionEnv::new(&mut store, WasmCapiEnv::default());
3072 let func_handle = {
3073 let mut env = func_env.clone().into_mut(&mut store);
3074 env.data_mut().state.insert(WasmObject::Func(func))
3075 };
3076
3077 let ref_handle = wasm_func_as_ref(func_env.clone().into_mut(&mut store), func_handle);
3078 assert!(ref_handle > INVALID_HANDLE);
3079
3080 let back = wasm_ref_as_func(func_env.clone().into_mut(&mut store), ref_handle);
3081 assert!(back > INVALID_HANDLE);
3082 let env = func_env.into_mut(&mut store);
3084 assert!(matches!(
3085 env.data().state.get(back),
3086 Some(WasmObject::Func(_))
3087 ));
3088 }
3089
3090 #[cfg(feature = "sys")]
3091 #[test]
3092 fn externref_marshals_through_guest_memory() {
3093 let mut store = Store::default();
3094 let memory = Memory::new(&mut store, MemoryType::new(Pages(1), Some(Pages(1)), false))
3095 .expect("memory can be created");
3096 let func_env = FunctionEnv::new(
3097 &mut store,
3098 WasmCapiEnv {
3099 memory: Some(memory.clone()),
3100 ..WasmCapiEnv::default()
3101 },
3102 );
3103
3104 let ref_handle = wasm_foreign_new(func_env.clone().into_mut(&mut store), 0);
3106 let value = {
3107 let env = func_env.clone().into_mut(&mut store);
3108 match env.data().state.get(ref_handle) {
3109 Some(WasmObject::Ref(value)) => value.clone(),
3110 _ => panic!("expected a Ref object"),
3111 }
3112 };
3113
3114 let val_ptr = 32;
3117 assert!(write_wasm_val(
3118 &mut func_env.clone().into_mut(&mut store),
3119 val_ptr,
3120 &value
3121 ));
3122 let roundtripped = read_wasm_val(
3123 &mut func_env.clone().into_mut(&mut store),
3124 val_ptr,
3125 Type::ExternRef,
3126 )
3127 .expect("externref reads back");
3128 assert!(ref_values_same(&value, &roundtripped));
3129 }
3130}