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