wasmer_c_api_imports/
lib.rs

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