gwasmi/
linker.rs

1use crate::{
2    core::Trap,
3    func::{FuncEntity, HostFuncEntity, HostFuncTrampolineEntity},
4    module::{ImportName, ImportType},
5    AsContext,
6    AsContextMut,
7    Caller,
8    Engine,
9    Error,
10    Extern,
11    ExternType,
12    Func,
13    FuncType,
14    GlobalType,
15    InstancePre,
16    IntoFunc,
17    MemoryType,
18    Module,
19    TableType,
20    Value,
21};
22use alloc::{
23    collections::{btree_map::Entry, BTreeMap},
24    sync::Arc,
25    vec::Vec,
26};
27use core::{
28    fmt,
29    fmt::{Debug, Display},
30    num::NonZeroUsize,
31    ops::Deref,
32};
33
34/// An error that may occur upon operating with [`Linker`] instances.
35#[derive(Debug)]
36pub enum LinkerError {
37    /// Encountered duplicate definitions for the same name.
38    DuplicateDefinition {
39        /// The duplicate import name of the definition.
40        import_name: ImportName,
41    },
42    /// Encountered when no definition for an import is found.
43    MissingDefinition {
44        /// The name of the import for which no definition was found.
45        name: ImportName,
46        /// The type of the import for which no definition has been found.
47        ty: ExternType,
48    },
49    /// Encountered when a definition with invalid type is found.
50    InvalidTypeDefinition {
51        /// The name of the import for which no definition was found.
52        name: ImportName,
53        /// The expected import type.
54        expected: ExternType,
55        /// The found definition type.
56        found: ExternType,
57    },
58    /// Encountered when a [`FuncType`] does not match the expected [`FuncType`].
59    FuncTypeMismatch {
60        /// The name of the import with the mismatched type.
61        name: ImportName,
62        /// The expected [`FuncType`].
63        expected: FuncType,
64        /// The mismatching [`FuncType`] found.
65        found: FuncType,
66    },
67    /// Encountered when a [`TableType`] does not match the expected [`TableType`].
68    InvalidTableSubtype {
69        /// The name of the import with the invalid [`TableType`].
70        name: ImportName,
71        /// The [`TableType`] that is supposed to be a subtype of `other`.
72        ty: TableType,
73        /// The [`TableType`] this is supposed to be a supertype of `ty`.
74        other: TableType,
75    },
76    /// Encountered when a [`MemoryType`] does not match the expected [`MemoryType`].
77    InvalidMemorySubtype {
78        /// The name of the import with the invalid [`MemoryType`].
79        name: ImportName,
80        /// The [`MemoryType`] that is supposed to be a subtype of `other`.
81        ty: MemoryType,
82        /// The [`MemoryType`] this is supposed to be a supertype of `ty`.
83        other: MemoryType,
84    },
85    /// Encountered when a [`GlobalType`] does not match the expected [`GlobalType`].
86    GlobalTypeMismatch {
87        /// The name of the import with the mismatched type.
88        name: ImportName,
89        /// The expected [`GlobalType`].
90        expected: GlobalType,
91        /// The mismatching [`GlobalType`] found.
92        found: GlobalType,
93    },
94}
95
96impl LinkerError {
97    /// Creates a new [`LinkerError`] for when an imported definition was not found.
98    fn missing_definition(import: &ImportType) -> Self {
99        Self::MissingDefinition {
100            name: import.import_name().clone(),
101            ty: import.ty().clone(),
102        }
103    }
104
105    /// Creates a new [`LinkerError`] for when an imported definition has an invalid type.
106    fn invalid_type_definition(import: &ImportType, found: &ExternType) -> Self {
107        Self::InvalidTypeDefinition {
108            name: import.import_name().clone(),
109            expected: import.ty().clone(),
110            found: found.clone(),
111        }
112    }
113
114    /// Create a new [`LinkerError`] for when a [`FuncType`] mismatched.
115    fn func_type_mismatch(name: &ImportName, expected: &FuncType, found: &FuncType) -> Self {
116        Self::FuncTypeMismatch {
117            name: name.clone(),
118            expected: expected.clone(),
119            found: found.clone(),
120        }
121    }
122
123    /// Create a new [`LinkerError`] for when a [`TableType`] `ty` unexpectedly is not a subtype of `other`.
124    fn table_type_mismatch(name: &ImportName, ty: &TableType, other: &TableType) -> Self {
125        Self::InvalidTableSubtype {
126            name: name.clone(),
127            ty: *ty,
128            other: *other,
129        }
130    }
131
132    /// Create a new [`LinkerError`] for when a [`MemoryType`] `ty` unexpectedly is not a subtype of `other`.
133    fn invalid_memory_subtype(name: &ImportName, ty: &MemoryType, other: &MemoryType) -> Self {
134        Self::InvalidMemorySubtype {
135            name: name.clone(),
136            ty: *ty,
137            other: *other,
138        }
139    }
140
141    /// Create a new [`LinkerError`] for when a [`GlobalType`] mismatched.
142    fn global_type_mismatch(name: &ImportName, expected: &GlobalType, found: &GlobalType) -> Self {
143        Self::GlobalTypeMismatch {
144            name: name.clone(),
145            expected: *expected,
146            found: *found,
147        }
148    }
149}
150
151#[cfg(feature = "std")]
152impl std::error::Error for LinkerError {}
153
154impl Display for LinkerError {
155    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
156        match self {
157            Self::DuplicateDefinition { import_name } => {
158                write!(
159                    f,
160                    "encountered duplicate definition with name `{import_name}`",
161                )
162            }
163            Self::MissingDefinition { name, ty } => {
164                write!(
165                    f,
166                    "cannot find definition for import {name} with type {ty:?}",
167                )
168            }
169            Self::InvalidTypeDefinition {
170                name,
171                expected,
172                found,
173            } => {
174                write!(f, "found definition for import {name} with type {expected:?} but found type {found:?}")
175            }
176            Self::FuncTypeMismatch {
177                name,
178                expected,
179                found,
180            } => {
181                write!(
182                    f,
183                    "function type mismatch for import {name}: \
184                    expected {expected:?} but found {found:?}",
185                )
186            }
187            Self::InvalidTableSubtype { name, ty, other } => {
188                write!(
189                    f,
190                    "import {name}: table type {ty:?} is not a subtype of {other:?}"
191                )
192            }
193            Self::InvalidMemorySubtype { name, ty, other } => {
194                write!(
195                    f,
196                    "import {name}: memory type {ty:?} is not a subtype of {other:?}"
197                )
198            }
199            Self::GlobalTypeMismatch {
200                name,
201                expected,
202                found,
203            } => {
204                write!(
205                    f,
206                    "global variable type mismatch for import {name}: \
207                    expected {expected:?} but found {found:?}",
208                )
209            }
210        }
211    }
212}
213
214/// A symbol representing an interned string.
215///
216/// # Note
217///
218/// Comparing symbols for equality is equal to comparing their respective
219/// interned strings for equality given that both symbol are coming from
220/// the same string interner instance.
221///
222/// # Dev. Note
223///
224/// Internally we use [`NonZeroUsize`] so that `Option<Symbol>` can
225/// be space optimized easily by the compiler. This is important since
226/// in [`ImportKey`] we are making extensive use of `Option<Symbol>`.
227#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
228#[repr(transparent)]
229pub struct Symbol(NonZeroUsize);
230
231impl Symbol {
232    /// Creates a new symbol.
233    ///
234    /// # Panics
235    ///
236    /// If the `value` is equal to `usize::MAX`.
237    pub fn from_usize(value: usize) -> Self {
238        NonZeroUsize::new(value.wrapping_add(1))
239            .map(Symbol)
240            .expect("encountered invalid symbol value")
241    }
242
243    /// Returns the underlying `usize` value of the [`Symbol`].
244    pub fn into_usize(self) -> usize {
245        self.0.get().wrapping_sub(1)
246    }
247}
248
249/// A string interner.
250///
251/// Efficiently interns strings and distributes symbols.
252#[derive(Debug, Default, Clone)]
253pub struct StringInterner {
254    string2idx: BTreeMap<Arc<str>, Symbol>,
255    strings: Vec<Arc<str>>,
256}
257
258impl StringInterner {
259    /// Returns the next symbol.
260    fn next_symbol(&self) -> Symbol {
261        Symbol::from_usize(self.strings.len())
262    }
263
264    /// Returns the symbol of the string and interns it if necessary.
265    pub fn get_or_intern(&mut self, string: &str) -> Symbol {
266        match self.string2idx.get(string) {
267            Some(symbol) => *symbol,
268            None => {
269                let symbol = self.next_symbol();
270                let rc_string: Arc<str> = Arc::from(string);
271                self.string2idx.insert(rc_string.clone(), symbol);
272                self.strings.push(rc_string);
273                symbol
274            }
275        }
276    }
277
278    /// Returns the symbol for the string if interned.
279    pub fn get(&self, string: &str) -> Option<Symbol> {
280        self.string2idx.get(string).copied()
281    }
282
283    /// Resolves the symbol to the underlying string.
284    pub fn resolve(&self, symbol: Symbol) -> Option<&str> {
285        self.strings.get(symbol.into_usize()).map(Deref::deref)
286    }
287}
288
289/// Wasm import keys.
290#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
291struct ImportKey {
292    /// The name of the module for the definition.
293    module: Symbol,
294    /// The name of the definition within the module scope.
295    name: Symbol,
296}
297
298/// A [`Linker`] definition.
299enum Definition<T> {
300    /// An external item from an [`Instance`](crate::Instance).
301    Extern(Extern),
302    /// A [`Linker`] internal host function.
303    HostFunc(HostFuncTrampolineEntity<T>),
304}
305
306impl<T> Clone for Definition<T> {
307    fn clone(&self) -> Self {
308        match self {
309            Self::Extern(definition) => Self::Extern(*definition),
310            Self::HostFunc(host_func) => Self::HostFunc(host_func.clone()),
311        }
312    }
313}
314
315impl<T> Definition<T> {
316    /// Returns the [`Extern`] item if this [`Definition`] is [`Definition::Extern`].
317    ///
318    /// Otherwise returns `None`.
319    fn as_extern(&self) -> Option<&Extern> {
320        match self {
321            Definition::Extern(item) => Some(item),
322            Definition::HostFunc(_) => None,
323        }
324    }
325
326    /// Returns the [`ExternType`] of the [`Definition`].
327    pub fn ty(&self, ctx: impl AsContext) -> ExternType {
328        match self {
329            Definition::Extern(item) => item.ty(ctx),
330            Definition::HostFunc(host_func) => {
331                let func_type = ctx
332                    .as_context()
333                    .store
334                    .engine()
335                    .resolve_func_type(host_func.ty_dedup(), FuncType::clone);
336                ExternType::Func(func_type)
337            }
338        }
339    }
340
341    /// Returns the [`Func`] of the [`Definition`] if it is a function.
342    ///
343    /// Returns `None` otherwise.
344    ///
345    /// # Note
346    ///
347    /// - This allocates a new [`Func`] on the `ctx` if it is a [`Linker`]
348    ///   defined host function.
349    /// - This unifies handling of [`Definition::Extern(Extern::Func)`] and
350    ///   [`Definition::HostFunc`].
351    pub fn as_func(&self, mut ctx: impl AsContextMut<UserState = T>) -> Option<Func> {
352        match self {
353            Definition::Extern(Extern::Func(func)) => Some(*func),
354            Definition::HostFunc(host_func) => {
355                let trampoline = ctx
356                    .as_context_mut()
357                    .store
358                    .alloc_trampoline(host_func.trampoline().clone());
359                let ty_dedup = host_func.ty_dedup();
360                let entity = HostFuncEntity::new(*ty_dedup, trampoline);
361                let func = ctx
362                    .as_context_mut()
363                    .store
364                    .inner
365                    .alloc_func(FuncEntity::Host(entity));
366                Some(func)
367            }
368            _ => None,
369        }
370    }
371}
372
373/// [`Debug`]-wrapper for the definitions of a [`Linker`].
374pub struct DebugDefinitions<'a, T> {
375    /// The [`Engine`] of the [`Linker`].
376    engine: &'a Engine,
377    /// The definitions of the [`Linker`].
378    definitions: &'a BTreeMap<ImportKey, Definition<T>>,
379}
380
381impl<'a, T> DebugDefinitions<'a, T> {
382    /// Create a new [`Debug`]-wrapper for the [`Linker`] definitions.
383    fn new(linker: &'a Linker<T>) -> Self {
384        Self {
385            engine: linker.engine(),
386            definitions: &linker.definitions,
387        }
388    }
389}
390
391impl<'a, T> Debug for DebugDefinitions<'a, T> {
392    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
393        let mut map = f.debug_map();
394        for (name, definition) in self.definitions {
395            match definition {
396                Definition::Extern(definition) => {
397                    map.entry(name, definition);
398                }
399                Definition::HostFunc(definition) => {
400                    map.entry(name, &DebugHostFuncEntity::new(self.engine, definition));
401                }
402            }
403        }
404        map.finish()
405    }
406}
407
408/// [`Debug`]-wrapper for [`HostFuncTrampolineEntity`] in the [`Linker`].
409pub struct DebugHostFuncEntity<'a, T> {
410    /// The [`Engine`] of the [`Linker`].
411    engine: &'a Engine,
412    /// The host function to be [`Debug`] formatted.
413    host_func: &'a HostFuncTrampolineEntity<T>,
414}
415
416impl<'a, T> DebugHostFuncEntity<'a, T> {
417    /// Create a new [`Debug`]-wrapper for the [`HostFuncTrampolineEntity`].
418    fn new(engine: &'a Engine, host_func: &'a HostFuncTrampolineEntity<T>) -> Self {
419        Self { engine, host_func }
420    }
421}
422
423impl<'a, T> Debug for DebugHostFuncEntity<'a, T> {
424    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
425        self.engine
426            .resolve_func_type(self.host_func.ty_dedup(), |func_type| {
427                f.debug_struct("HostFunc").field("ty", func_type).finish()
428            })
429    }
430}
431
432/// A linker used to define module imports and instantiate module instances.
433pub struct Linker<T> {
434    /// The underlying [`Engine`] for the [`Linker`].
435    ///
436    /// # Note
437    ///
438    /// Primarily required to define [`Linker`] owned host functions
439    //  using [`Linker::func_wrap`] and [`Linker::func_new`]. TODO: implement methods
440    engine: Engine,
441    /// Allows to efficiently store strings and deduplicate them..
442    strings: StringInterner,
443    /// Stores the definitions given their names.
444    definitions: BTreeMap<ImportKey, Definition<T>>,
445}
446
447impl<T> Debug for Linker<T> {
448    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
449        f.debug_struct("Linker")
450            .field("strings", &self.strings)
451            .field("definitions", &DebugDefinitions::new(self))
452            .finish()
453    }
454}
455
456impl<T> Clone for Linker<T> {
457    fn clone(&self) -> Linker<T> {
458        Self {
459            engine: self.engine.clone(),
460            strings: self.strings.clone(),
461            definitions: self.definitions.clone(),
462        }
463    }
464}
465
466impl<T> Default for Linker<T> {
467    fn default() -> Self {
468        Self::new(&Engine::default())
469    }
470}
471
472impl<T> Linker<T> {
473    /// Creates a new linker.
474    pub fn new(engine: &Engine) -> Self {
475        Self {
476            engine: engine.clone(),
477            strings: StringInterner::default(),
478            definitions: BTreeMap::default(),
479        }
480    }
481
482    /// Returns the underlying [`Engine`] of the [`Linker`].
483    pub fn engine(&self) -> &Engine {
484        &self.engine
485    }
486
487    /// Define a new item in this [`Linker`].
488    ///
489    /// # Errors
490    ///
491    /// If there already is a definition under the same name for this [`Linker`].
492    pub fn define(
493        &mut self,
494        module: &str,
495        name: &str,
496        item: impl Into<Extern>,
497    ) -> Result<&mut Self, LinkerError> {
498        let key = self.import_key(module, name);
499        self.insert(key, Definition::Extern(item.into()))?;
500        Ok(self)
501    }
502
503    /// Creates a new named [`Func::new`]-style host [`Func`] for this [`Linker`].
504    ///
505    /// For more information see [`Linker::func_wrap`].
506    ///
507    /// # Errors
508    ///
509    /// If there already is a definition under the same name for this [`Linker`].
510    pub fn func_new(
511        &mut self,
512        module: &str,
513        name: &str,
514        ty: FuncType,
515        func: impl Fn(Caller<'_, T>, &[Value], &mut [Value]) -> Result<(), Trap> + Send + Sync + 'static,
516    ) -> Result<&mut Self, LinkerError> {
517        let func = HostFuncTrampolineEntity::new(&self.engine, ty, func);
518        let key = self.import_key(module, name);
519        self.insert(key, Definition::HostFunc(func))?;
520        Ok(self)
521    }
522
523    /// Creates a new named [`Func::new`]-style host [`Func`] for this [`Linker`].
524    ///
525    /// For information how to use this API see [`Func::wrap`].
526    ///
527    /// This method creates a host function for this [`Linker`] under the given name.
528    /// It is distint in its ability to create a [`Store`] independent
529    /// host function. Host functions defined this way can be used to instantiate
530    /// instances in multiple different [`Store`] entities.
531    ///
532    /// The same applies to other [`Linker`] methods to define new [`Func`] instances
533    /// such as [`Linker::func_new`].
534    ///
535    /// In a concurrently running program, this means that these host functions
536    /// could be called concurrently if different [`Store`] entities are executing on
537    /// different threads.
538    ///
539    /// # Errors
540    ///
541    /// If there already is a definition under the same name for this [`Linker`].
542    ///
543    /// [`Store`]: crate::Store
544    pub fn func_wrap<Params, Args>(
545        &mut self,
546        module: &str,
547        name: &str,
548        func: impl IntoFunc<T, Params, Args>,
549    ) -> Result<&mut Self, LinkerError> {
550        let func = HostFuncTrampolineEntity::wrap(&self.engine, func);
551        let key = self.import_key(module, name);
552        self.insert(key, Definition::HostFunc(func))?;
553        Ok(self)
554    }
555
556    /// Returns the import key for the module name and item name.
557    fn import_key(&mut self, module: &str, name: &str) -> ImportKey {
558        ImportKey {
559            module: self.strings.get_or_intern(module),
560            name: self.strings.get_or_intern(name),
561        }
562    }
563
564    /// Resolves the module and item name of the import key if any.
565    fn resolve_import_key(&self, key: ImportKey) -> Option<(&str, &str)> {
566        let module_name = self.strings.resolve(key.module)?;
567        let item_name = self.strings.resolve(key.name)?;
568        Some((module_name, item_name))
569    }
570
571    /// Inserts the extern item under the import key.
572    ///
573    /// # Errors
574    ///
575    /// If there already is a definition for the import key for this [`Linker`].
576    fn insert(&mut self, key: ImportKey, item: Definition<T>) -> Result<(), LinkerError> {
577        match self.definitions.entry(key) {
578            Entry::Occupied(_) => {
579                let (module_name, field_name) = self
580                    .resolve_import_key(key)
581                    .unwrap_or_else(|| panic!("encountered missing import names for key {key:?}"));
582                let import_name = ImportName::new(module_name, field_name);
583                return Err(LinkerError::DuplicateDefinition { import_name });
584            }
585            Entry::Vacant(v) => {
586                v.insert(item);
587            }
588        }
589        Ok(())
590    }
591
592    /// Looks up a defined [`Extern`] by name in this [`Linker`].
593    ///
594    /// - Returns `None` if this name was not previously defined in this [`Linker`].
595    /// - Returns `None` if the definition is a [`Linker`] defined host function.
596    ///
597    /// # Panics
598    ///
599    /// If the [`Engine`] of this [`Linker`] and the [`Engine`] of `context` are not the same.
600    pub fn get(
601        &self,
602        context: impl AsContext<UserState = T>,
603        module: &str,
604        name: &str,
605    ) -> Option<Extern> {
606        match self.get_definition(context, module, name) {
607            Some(Definition::Extern(item)) => Some(*item),
608            _ => None,
609        }
610    }
611
612    /// Looks up a [`Definition`] by name in this [`Linker`].
613    ///
614    /// Returns `None` if this name was not previously defined in this [`Linker`].
615    ///
616    /// # Panics
617    ///
618    /// If the [`Engine`] of this [`Linker`] and the [`Engine`] of `context` are not the same.
619    fn get_definition(
620        &self,
621        context: impl AsContext<UserState = T>,
622        module: &str,
623        name: &str,
624    ) -> Option<&Definition<T>> {
625        assert!(Engine::same(
626            context.as_context().store.engine(),
627            self.engine()
628        ));
629        let key = ImportKey {
630            module: self.strings.get(module)?,
631            name: self.strings.get(name)?,
632        };
633        self.definitions.get(&key)
634    }
635
636    /// Instantiates the given [`Module`] using the definitions in the [`Linker`].
637    ///
638    /// # Panics
639    ///
640    /// If the [`Engine`] of the [`Linker`] and `context` are not the same.
641    ///
642    /// # Errors
643    ///
644    /// - If the linker does not define imports of the instantiated [`Module`].
645    /// - If any imported item does not satisfy its type requirements.
646    pub fn instantiate(
647        &self,
648        mut context: impl AsContextMut<UserState = T>,
649        module: &Module,
650    ) -> Result<InstancePre, Error> {
651        assert!(Engine::same(self.engine(), context.as_context().engine()));
652        let externals = module
653            .imports()
654            .map(|import| self.process_import(&mut context, import))
655            .collect::<Result<Vec<Extern>, Error>>()?;
656        module.instantiate(context, externals)
657    }
658
659    /// Processes a single [`Module`] import.
660    ///
661    /// # Panics
662    ///
663    /// If the [`Engine`] of the [`Linker`] and `context` are not the same.
664    ///
665    /// # Errors
666    ///
667    /// If the imported item does not satisfy constraints set by the [`Module`].
668    fn process_import(
669        &self,
670        mut context: impl AsContextMut<UserState = T>,
671        import: ImportType,
672    ) -> Result<Extern, Error> {
673        assert!(Engine::same(self.engine(), context.as_context().engine()));
674        let import_name = import.import_name();
675        let module_name = import.module();
676        let field_name = import.name();
677        let resolved = self
678            .get_definition(context.as_context(), module_name, field_name)
679            .ok_or_else(|| LinkerError::missing_definition(&import))?;
680        let invalid_type = || LinkerError::invalid_type_definition(&import, &resolved.ty(&context));
681        match import.ty() {
682            ExternType::Func(expected_type) => {
683                let found_type = resolved
684                    .ty(&context)
685                    .func()
686                    .cloned()
687                    .ok_or_else(invalid_type)?;
688                if &found_type != expected_type {
689                    return Err(LinkerError::func_type_mismatch(
690                        import_name,
691                        expected_type,
692                        &found_type,
693                    ))
694                    .map_err(Into::into);
695                }
696                let func = resolved
697                    .as_func(&mut context)
698                    .expect("already asserted that `resolved` is a function");
699                Ok(Extern::Func(func))
700            }
701            ExternType::Table(expected_type) => {
702                let table = resolved
703                    .as_extern()
704                    .copied()
705                    .and_then(Extern::into_table)
706                    .ok_or_else(invalid_type)?;
707                let found_type = table.dynamic_ty(context);
708                found_type.is_subtype_or_err(expected_type).map_err(|_| {
709                    LinkerError::table_type_mismatch(import_name, expected_type, &found_type)
710                })?;
711                Ok(Extern::Table(table))
712            }
713            ExternType::Memory(expected_type) => {
714                let memory = resolved
715                    .as_extern()
716                    .copied()
717                    .and_then(Extern::into_memory)
718                    .ok_or_else(invalid_type)?;
719                let found_type = memory.dynamic_ty(context);
720                found_type.is_subtype_or_err(expected_type).map_err(|_| {
721                    LinkerError::invalid_memory_subtype(import_name, expected_type, &found_type)
722                })?;
723                Ok(Extern::Memory(memory))
724            }
725            ExternType::Global(expected_type) => {
726                let global = resolved
727                    .as_extern()
728                    .copied()
729                    .and_then(Extern::into_global)
730                    .ok_or_else(invalid_type)?;
731                let found_type = global.ty(context);
732                if &found_type != expected_type {
733                    return Err(LinkerError::global_type_mismatch(
734                        import_name,
735                        expected_type,
736                        &found_type,
737                    ))
738                    .map_err(Into::into);
739                }
740                Ok(Extern::Global(global))
741            }
742        }
743    }
744}
745
746#[cfg(test)]
747mod tests {
748    use wasmi_core::ValueType;
749
750    use super::*;
751    use crate::Store;
752
753    struct HostState {
754        a: i32,
755        b: i64,
756    }
757
758    #[test]
759    fn linker_funcs_work() {
760        let engine = Engine::default();
761        let mut linker = <Linker<HostState>>::new(&engine);
762        linker
763            .func_new(
764                "host",
765                "get_a",
766                FuncType::new([], [ValueType::I32]),
767                |ctx: Caller<HostState>, _params: &[Value], results: &mut [Value]| {
768                    results[0] = Value::from(ctx.data().a);
769                    Ok(())
770                },
771            )
772            .unwrap();
773        linker
774            .func_new(
775                "host",
776                "set_a",
777                FuncType::new([ValueType::I32], []),
778                |mut ctx: Caller<HostState>, params: &[Value], _results: &mut [Value]| {
779                    ctx.data_mut().a = params[0].i32().unwrap();
780                    Ok(())
781                },
782            )
783            .unwrap();
784        linker
785            .func_wrap("host", "get_b", |ctx: Caller<HostState>| ctx.data().b)
786            .unwrap();
787        linker
788            .func_wrap("host", "set_b", |mut ctx: Caller<HostState>, value: i64| {
789                ctx.data_mut().b = value
790            })
791            .unwrap();
792        let a_init = 42;
793        let b_init = 77;
794        let mut store = <Store<HostState>>::new(
795            &engine,
796            HostState {
797                a: a_init,
798                b: b_init,
799            },
800        );
801        let wat = r#"
802                (module
803                    (import "host" "get_a" (func $host_get_a (result i32)))
804                    (import "host" "set_a" (func $host_set_a (param i32)))
805                    (import "host" "get_b" (func $host_get_b (result i64)))
806                    (import "host" "set_b" (func $host_set_b (param i64)))
807
808                    (func (export "wasm_get_a") (result i32)
809                        (call $host_get_a)
810                    )
811                    (func (export "wasm_set_a") (param $param i32)
812                        (call $host_set_a (local.get $param))
813                    )
814
815                    (func (export "wasm_get_b") (result i64)
816                        (call $host_get_b)
817                    )
818                    (func (export "wasm_set_b") (param $param i64)
819                        (call $host_set_b (local.get $param))
820                    )
821                )
822            "#;
823        let wasm = wat::parse_str(wat).unwrap();
824        let module = Module::new(&engine, &mut &wasm[..]).unwrap();
825        let instance = linker
826            .instantiate(&mut store, &module)
827            .unwrap()
828            .start(&mut store)
829            .unwrap();
830
831        let wasm_get_a = instance
832            .get_typed_func::<(), i32>(&store, "wasm_get_a")
833            .unwrap();
834        let wasm_set_a = instance
835            .get_typed_func::<i32, ()>(&store, "wasm_set_a")
836            .unwrap();
837        let wasm_get_b = instance
838            .get_typed_func::<(), i64>(&store, "wasm_get_b")
839            .unwrap();
840        let wasm_set_b = instance
841            .get_typed_func::<i64, ()>(&store, "wasm_set_b")
842            .unwrap();
843
844        assert_eq!(wasm_get_a.call(&mut store, ()).unwrap(), a_init);
845        wasm_set_a.call(&mut store, 100).unwrap();
846        assert_eq!(wasm_get_a.call(&mut store, ()).unwrap(), 100);
847
848        assert_eq!(wasm_get_b.call(&mut store, ()).unwrap(), b_init);
849        wasm_set_b.call(&mut store, 200).unwrap();
850        assert_eq!(wasm_get_b.call(&mut store, ()).unwrap(), 200);
851    }
852}