wasmer_c_api_imports/
lib.rs

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