Skip to main content

wasmer_c_api_imports/
lib.rs

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