wasmer_runtime_core_fl/
backing.rs

1use crate::{
2    error::{CreationError, LinkError, LinkResult},
3    export::{Context, Export},
4    global::Global,
5    import::ImportObject,
6    memory::Memory,
7    module::{ImportName, ModuleInfo, ModuleInner},
8    sig_registry::SigRegistry,
9    structures::{BoxedMap, Map, SliceMap, TypedIndex},
10    table::Table,
11    typed_func::{always_trap, Func},
12    types::{
13        ImportedFuncIndex, ImportedGlobalIndex, ImportedMemoryIndex, ImportedTableIndex,
14        Initializer, LocalFuncIndex, LocalGlobalIndex, LocalMemoryIndex, LocalOrImport,
15        LocalTableIndex, SigIndex, Value,
16    },
17    vm,
18};
19use std::{fmt::Debug, ptr::NonNull, slice};
20
21/// Size of the array for internal instance usage
22pub const INTERNALS_SIZE: usize = 256;
23
24pub(crate) struct Internals(pub(crate) [u64; INTERNALS_SIZE]);
25
26impl Debug for Internals {
27    fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
28        write!(formatter, "Internals({:?})", &self.0[..])
29    }
30}
31
32/// The `LocalBacking` "owns" the memory used by all the local resources of an Instance.
33/// That is, local memories, tables, and globals (as well as some additional
34/// data for the virtual call machinery).
35#[derive(Debug)]
36pub struct LocalBacking {
37    /// This is a map from the local resource index to actual memory,
38    /// table, and globals.
39    pub(crate) memories: BoxedMap<LocalMemoryIndex, Memory>,
40    pub(crate) tables: BoxedMap<LocalTableIndex, Table>,
41    pub(crate) globals: BoxedMap<LocalGlobalIndex, Global>,
42
43    /// This own the memory containing the pointers to the local memories.
44    /// While simplifying implementation, this adds indirection and may hurt
45    /// performance, especially on cache-starved systems.
46    pub(crate) vm_memories: BoxedMap<LocalMemoryIndex, *mut vm::LocalMemory>,
47    pub(crate) vm_tables: BoxedMap<LocalTableIndex, *mut vm::LocalTable>,
48    pub(crate) vm_globals: BoxedMap<LocalGlobalIndex, *mut vm::LocalGlobal>,
49
50    /// The dynamic sigindices are used to efficiently support caching and
51    /// the `call_indirect` wasm instruction. This field (and local_functions
52    /// as well) are subject to change.
53    pub(crate) dynamic_sigindices: BoxedMap<SigIndex, vm::SigId>,
54    pub(crate) local_functions: BoxedMap<LocalFuncIndex, *const vm::Func>,
55
56    pub(crate) internals: Internals,
57}
58
59// Manually implemented because LocalBacking contains raw pointers directly
60unsafe impl Send for LocalBacking {}
61
62impl LocalBacking {
63    pub(crate) fn new(
64        module: &ModuleInner,
65        imports: &ImportBacking,
66        vmctx: *mut vm::Ctx,
67    ) -> LinkResult<Self> {
68        let mut memories = match Self::generate_memories(module) {
69            Ok(m) => m,
70            Err(e) => {
71                return Err(vec![LinkError::Generic {
72                    message: format!("unable to create memory: {:?}", e),
73                }]);
74            }
75        };
76        let mut tables = Self::generate_tables(module);
77        let mut globals = Self::generate_globals(module, imports)?;
78
79        // Ensure all initializers are valid before running finalizers
80        Self::validate_memories(module, imports)?;
81        Self::validate_tables(module, imports, &mut tables)?;
82
83        let vm_memories = Self::finalize_memories(module, imports, &mut memories)?;
84        let vm_tables = Self::finalize_tables(module, imports, &mut tables, vmctx)?;
85        let vm_globals = Self::finalize_globals(&mut globals);
86
87        let dynamic_sigindices = Self::generate_sigindices(&module.info);
88        let local_functions = Self::generate_local_functions(module);
89
90        Ok(Self {
91            memories,
92            tables,
93            globals,
94
95            vm_memories,
96            vm_tables,
97            vm_globals,
98
99            dynamic_sigindices,
100            local_functions,
101
102            internals: Internals([0; INTERNALS_SIZE]),
103        })
104    }
105
106    fn generate_local_functions(module: &ModuleInner) -> BoxedMap<LocalFuncIndex, *const vm::Func> {
107        (0..module.info.func_assoc.len() - module.info.imported_functions.len())
108            .map(|index| {
109                module
110                    .runnable_module
111                    .get_func(&module.info, LocalFuncIndex::new(index))
112                    .unwrap()
113                    .as_ptr() as *const _
114            })
115            .collect::<Map<_, _>>()
116            .into_boxed_map()
117    }
118
119    fn generate_sigindices(info: &ModuleInfo) -> BoxedMap<SigIndex, vm::SigId> {
120        info.signatures
121            .iter()
122            .map(|(_, signature)| {
123                let signature = SigRegistry.lookup_signature_ref(signature);
124                let sig_index = SigRegistry.lookup_sig_index(signature);
125                vm::SigId(sig_index.index() as u32)
126            })
127            .collect::<Map<_, _>>()
128            .into_boxed_map()
129    }
130
131    fn generate_memories(
132        module: &ModuleInner,
133    ) -> Result<BoxedMap<LocalMemoryIndex, Memory>, CreationError> {
134        let mut memories = Map::with_capacity(module.info.memories.len());
135        for (_, &desc) in &module.info.memories {
136            let memory = Memory::new(desc)?;
137            memories.push(memory);
138        }
139
140        Ok(memories.into_boxed_map())
141    }
142
143    /// Validate each locally-defined memory in the Module.
144    ///
145    /// This involves copying in the data initializers.
146    fn validate_memories(module: &ModuleInner, imports: &ImportBacking) -> LinkResult<()> {
147        // Validate data size fits
148        for init in module.info.data_initializers.iter() {
149            let init_base = match init.base {
150                Initializer::Const(Value::I32(offset)) => offset as u32,
151                Initializer::Const(_) => {
152                    return Err(vec![LinkError::Generic {
153                        message: "a const initializer must be an i32".to_string(),
154                    }]);
155                }
156                Initializer::GetGlobal(import_global_index) => {
157                    if import_global_index.index() >= imports.globals.len() {
158                        return Err(vec![LinkError::Generic {
159                            message: "incorrect global index for initializer".to_string(),
160                        }]);
161                    }
162                    if let Value::I32(x) = imports.globals[import_global_index].get() {
163                        x as u32
164                    } else {
165                        return Err(vec![LinkError::Generic {
166                            message: "unsupported global type for initializer".to_string(),
167                        }]);
168                    }
169                }
170            } as usize;
171
172            // Validate data size fits
173            match init.memory_index.local_or_import(&module.info) {
174                LocalOrImport::Local(local_memory_index) => {
175                    let memory_desc = module.info.memories[local_memory_index];
176                    let data_top = init_base + init.data.len();
177                    if memory_desc.minimum.bytes().0 < data_top || data_top < init_base {
178                        return Err(vec![LinkError::Generic {
179                            message: "data segment does not fit".to_string(),
180                        }]);
181                    }
182                }
183                LocalOrImport::Import(imported_memory_index) => {
184                    // Write the initialization data to the memory that
185                    // we think the imported memory is.
186                    let local_memory = unsafe { &*imports.vm_memories[imported_memory_index] };
187                    let data_top = init_base + init.data.len();
188                    if local_memory.bound < data_top || data_top < init_base {
189                        return Err(vec![LinkError::Generic {
190                            message: "data segment does not fit".to_string(),
191                        }]);
192                    }
193                }
194            }
195        }
196        Ok(())
197    }
198
199    /// Initialize each locally-defined memory in the Module.
200    ///
201    /// This involves copying in the data initializers.
202    fn finalize_memories(
203        module: &ModuleInner,
204        imports: &ImportBacking,
205        memories: &mut SliceMap<LocalMemoryIndex, Memory>,
206    ) -> LinkResult<BoxedMap<LocalMemoryIndex, *mut vm::LocalMemory>> {
207        // For each init that has some data...
208        // Initialize data
209        for init in module.info.data_initializers.iter() {
210            let init_base = match init.base {
211                Initializer::Const(Value::I32(offset)) => offset as u32,
212                Initializer::Const(_) => {
213                    return Err(vec![LinkError::Generic {
214                        message: "a const initializer must be an i32".to_string(),
215                    }]);
216                }
217                Initializer::GetGlobal(import_global_index) => {
218                    if import_global_index.index() >= imports.globals.len() {
219                        return Err(vec![LinkError::Generic {
220                            message: "incorrect global index for initializer".to_string(),
221                        }]);
222                    }
223                    if let Value::I32(x) = imports.globals[import_global_index].get() {
224                        x as u32
225                    } else {
226                        return Err(vec![LinkError::Generic {
227                            message: "unsupported global type for initializer".to_string(),
228                        }]);
229                    }
230                }
231            } as usize;
232
233            match init.memory_index.local_or_import(&module.info) {
234                LocalOrImport::Local(local_memory_index) => {
235                    let mem = &memories[local_memory_index];
236                    for (mem_byte, data_byte) in mem.view()[init_base..init_base + init.data.len()]
237                        .iter()
238                        .zip(init.data.iter())
239                    {
240                        mem_byte.set(*data_byte);
241                    }
242                }
243                LocalOrImport::Import(imported_memory_index) => {
244                    // Write the initialization data to the memory that
245                    // we think the imported memory is.
246                    let memory_slice = unsafe {
247                        let local_memory = &*imports.vm_memories[imported_memory_index];
248                        slice::from_raw_parts_mut(local_memory.base, local_memory.bound)
249                    };
250
251                    let mem_init_view = &mut memory_slice[init_base..init_base + init.data.len()];
252                    mem_init_view.copy_from_slice(&init.data);
253                }
254            }
255        }
256
257        Ok(memories
258            .iter_mut()
259            .map(|(_, mem)| mem.vm_local_memory())
260            .collect::<Map<_, _>>()
261            .into_boxed_map())
262    }
263
264    fn generate_tables(module: &ModuleInner) -> BoxedMap<LocalTableIndex, Table> {
265        let mut tables = Map::with_capacity(module.info.tables.len());
266
267        for (_, &table_desc) in module.info.tables.iter() {
268            let table = Table::new(table_desc).unwrap();
269            tables.push(table);
270        }
271
272        tables.into_boxed_map()
273    }
274
275    /// This validates all of the locally-defined tables in the Module.
276    #[allow(clippy::cast_ptr_alignment)]
277    fn validate_tables(
278        module: &ModuleInner,
279        imports: &ImportBacking,
280        tables: &mut SliceMap<LocalTableIndex, Table>,
281    ) -> LinkResult<()> {
282        for init in &module.info.elem_initializers {
283            let init_base = match init.base {
284                Initializer::Const(Value::I32(offset)) => offset as u32,
285                Initializer::Const(_) => {
286                    return Err(vec![LinkError::Generic {
287                        message: "a const initializer must be an i32".to_string(),
288                    }]);
289                }
290                Initializer::GetGlobal(import_global_index) => {
291                    if import_global_index.index() >= imports.globals.len() {
292                        return Err(vec![LinkError::Generic {
293                            message: "incorrect global index for initializer".to_string(),
294                        }]);
295                    }
296                    if let Value::I32(x) = imports.globals[import_global_index].get() {
297                        x as u32
298                    } else {
299                        return Err(vec![LinkError::Generic {
300                            message: "unsupported global type for initializer".to_string(),
301                        }]);
302                    }
303                }
304            } as usize;
305
306            match init.table_index.local_or_import(&module.info) {
307                LocalOrImport::Local(local_table_index) => {
308                    let table = &tables[local_table_index];
309
310                    if (table.size() as usize) < init_base + init.elements.len() {
311                        return Err(vec![LinkError::Generic {
312                            message: "elements segment does not fit".to_string(),
313                        }]);
314                    }
315                }
316                LocalOrImport::Import(import_table_index) => {
317                    let table = &imports.tables[import_table_index];
318
319                    if (table.size() as usize) < init_base + init.elements.len() {
320                        return Err(vec![LinkError::Generic {
321                            message: "elements segment does not fit".to_string(),
322                        }]);
323                    }
324                }
325            }
326        }
327        Ok(())
328    }
329
330    /// This initializes all of the locally-defined tables in the Module, e.g.
331    /// putting all the table elements (function pointers)
332    /// in the right places.
333    #[allow(clippy::cast_ptr_alignment)]
334    fn finalize_tables(
335        module: &ModuleInner,
336        imports: &ImportBacking,
337        tables: &mut SliceMap<LocalTableIndex, Table>,
338        vmctx: *mut vm::Ctx,
339    ) -> LinkResult<BoxedMap<LocalTableIndex, *mut vm::LocalTable>> {
340        for init in &module.info.elem_initializers {
341            let init_base = match init.base {
342                Initializer::Const(Value::I32(offset)) => offset as u32,
343                Initializer::Const(_) => {
344                    return Err(vec![LinkError::Generic {
345                        message: "a const initializer be an i32".to_string(),
346                    }]);
347                }
348                Initializer::GetGlobal(import_global_index) => {
349                    if import_global_index.index() >= imports.globals.len() {
350                        return Err(vec![LinkError::Generic {
351                            message: "incorrect global index for initializer".to_string(),
352                        }]);
353                    }
354                    if let Value::I32(x) = imports.globals[import_global_index].get() {
355                        x as u32
356                    } else {
357                        return Err(vec![LinkError::Generic {
358                            message: "unsupported global type for initializer".to_string(),
359                        }]);
360                    }
361                }
362            } as usize;
363
364            match init.table_index.local_or_import(&module.info) {
365                LocalOrImport::Local(local_table_index) => {
366                    let table = &tables[local_table_index];
367                    table.anyfunc_direct_access_mut(|elements| {
368                        for (i, &func_index) in init.elements.iter().enumerate() {
369                            let sig_index = module.info.func_assoc[func_index];
370                            // let signature = &module.info.signatures[sig_index];
371                            let signature = SigRegistry
372                                .lookup_signature_ref(&module.info.signatures[sig_index]);
373                            let sig_id =
374                                vm::SigId(SigRegistry.lookup_sig_index(signature).index() as u32);
375
376                            let (func, ctx) = match func_index.local_or_import(&module.info) {
377                                LocalOrImport::Local(local_func_index) => (
378                                    module
379                                        .runnable_module
380                                        .get_func(&module.info, local_func_index)
381                                        .unwrap()
382                                        .as_ptr()
383                                        as *const vm::Func,
384                                    vmctx,
385                                ),
386                                LocalOrImport::Import(imported_func_index) => {
387                                    let vm::ImportedFunc { func, func_ctx } =
388                                        imports.vm_functions[imported_func_index];
389                                    (func, unsafe { func_ctx.as_ref() }.vmctx.as_ptr())
390                                }
391                            };
392
393                            elements[init_base + i] = vm::Anyfunc { func, ctx, sig_id };
394                        }
395                    });
396                }
397                LocalOrImport::Import(import_table_index) => {
398                    let table = &imports.tables[import_table_index];
399
400                    table.anyfunc_direct_access_mut(|elements| {
401                        for (i, &func_index) in init.elements.iter().enumerate() {
402                            let sig_index = module.info.func_assoc[func_index];
403                            let signature = SigRegistry
404                                .lookup_signature_ref(&module.info.signatures[sig_index]);
405                            // let signature = &module.info.signatures[sig_index];
406                            let sig_id =
407                                vm::SigId(SigRegistry.lookup_sig_index(signature).index() as u32);
408
409                            let (func, ctx) = match func_index.local_or_import(&module.info) {
410                                LocalOrImport::Local(local_func_index) => (
411                                    module
412                                        .runnable_module
413                                        .get_func(&module.info, local_func_index)
414                                        .unwrap()
415                                        .as_ptr()
416                                        as *const vm::Func,
417                                    vmctx,
418                                ),
419                                LocalOrImport::Import(imported_func_index) => {
420                                    let vm::ImportedFunc { func, func_ctx } =
421                                        imports.vm_functions[imported_func_index];
422                                    (func, unsafe { func_ctx.as_ref() }.vmctx.as_ptr())
423                                }
424                            };
425
426                            elements[init_base + i] = vm::Anyfunc { func, ctx, sig_id };
427                        }
428                    });
429                }
430            }
431        }
432
433        Ok(tables
434            .iter_mut()
435            .map(|(_, table)| table.vm_local_table())
436            .collect::<Map<_, _>>()
437            .into_boxed_map())
438    }
439
440    fn generate_globals(
441        module: &ModuleInner,
442        imports: &ImportBacking,
443    ) -> LinkResult<BoxedMap<LocalGlobalIndex, Global>> {
444        let mut globals = Map::with_capacity(module.info.globals.len());
445
446        for (_, global_init) in module.info.globals.iter() {
447            let value = match &global_init.init {
448                Initializer::Const(value) => value.clone(),
449                Initializer::GetGlobal(import_global_index) => {
450                    if imports.globals.len() <= import_global_index.index() {
451                        return Err(vec![LinkError::Generic {
452                            message: format!(
453                                "Trying to read the `{:?}` global that is not properly initialized.",
454                                import_global_index.index()
455                            ),
456                        }]);
457                    }
458
459                    imports.globals[*import_global_index].get()
460                }
461            };
462
463            let global = if global_init.desc.mutable {
464                Global::new_mutable(value)
465            } else {
466                Global::new(value)
467            };
468
469            globals.push(global);
470        }
471
472        Ok(globals.into_boxed_map())
473    }
474
475    fn finalize_globals(
476        globals: &mut SliceMap<LocalGlobalIndex, Global>,
477    ) -> BoxedMap<LocalGlobalIndex, *mut vm::LocalGlobal> {
478        globals
479            .iter_mut()
480            .map(|(_, global)| global.vm_local_global())
481            .collect::<Map<_, _>>()
482            .into_boxed_map()
483    }
484}
485
486/// The `ImportBacking` stores references to the imported resources of an Instance. This includes
487/// imported memories, tables, globals and functions.
488#[derive(Debug)]
489pub struct ImportBacking {
490    pub(crate) memories: BoxedMap<ImportedMemoryIndex, Memory>,
491    pub(crate) tables: BoxedMap<ImportedTableIndex, Table>,
492    pub(crate) globals: BoxedMap<ImportedGlobalIndex, Global>,
493
494    pub(crate) vm_functions: BoxedMap<ImportedFuncIndex, vm::ImportedFunc>,
495    pub(crate) vm_memories: BoxedMap<ImportedMemoryIndex, *mut vm::LocalMemory>,
496    pub(crate) vm_tables: BoxedMap<ImportedTableIndex, *mut vm::LocalTable>,
497    pub(crate) vm_globals: BoxedMap<ImportedGlobalIndex, *mut vm::LocalGlobal>,
498}
499
500// manually implemented because ImportBacking contains raw pointers directly
501unsafe impl Send for ImportBacking {}
502
503impl ImportBacking {
504    /// Creates a new `ImportBacking` from the given `ModuleInner`, `ImportObject`, and `Ctx`.
505    pub fn new(
506        module: &ModuleInner,
507        imports: &ImportObject,
508        vmctx: *mut vm::Ctx,
509    ) -> LinkResult<Self> {
510        let mut failed = false;
511        let mut link_errors = vec![];
512
513        let vm_functions = import_functions(module, imports, vmctx).unwrap_or_else(|le| {
514            failed = true;
515            link_errors.extend(le);
516            Map::new().into_boxed_map()
517        });
518
519        let (memories, vm_memories) = import_memories(module, imports).unwrap_or_else(|le| {
520            failed = true;
521            link_errors.extend(le);
522            (Map::new().into_boxed_map(), Map::new().into_boxed_map())
523        });
524
525        let (tables, vm_tables) = import_tables(module, imports).unwrap_or_else(|le| {
526            failed = true;
527            link_errors.extend(le);
528            (Map::new().into_boxed_map(), Map::new().into_boxed_map())
529        });
530
531        let (globals, vm_globals) = import_globals(module, imports).unwrap_or_else(|le| {
532            failed = true;
533            link_errors.extend(le);
534            (Map::new().into_boxed_map(), Map::new().into_boxed_map())
535        });
536
537        if failed {
538            Err(link_errors)
539        } else {
540            Ok(ImportBacking {
541                memories,
542                tables,
543                globals,
544
545                vm_functions,
546                vm_memories,
547                vm_tables,
548                vm_globals,
549            })
550        }
551    }
552
553    /// Gets a `ImportedFunc` from the given `ImportedFuncIndex`.
554    pub fn imported_func(&self, index: ImportedFuncIndex) -> vm::ImportedFunc {
555        self.vm_functions[index].clone()
556    }
557}
558
559impl Drop for ImportBacking {
560    fn drop(&mut self) {
561        // Properly drop the `vm::FuncCtx` in `vm::ImportedFunc`.
562        for (_imported_func_index, imported_func) in (*self.vm_functions).iter_mut() {
563            let func_ctx_ptr = imported_func.func_ctx.as_ptr();
564
565            if !func_ctx_ptr.is_null() {
566                let _: Box<vm::FuncCtx> = unsafe { Box::from_raw(func_ctx_ptr) };
567            }
568        }
569    }
570}
571
572fn import_functions(
573    module: &ModuleInner,
574    imports: &ImportObject,
575    vmctx: *mut vm::Ctx,
576) -> LinkResult<BoxedMap<ImportedFuncIndex, vm::ImportedFunc>> {
577    let mut link_errors = vec![];
578    let mut functions = Map::with_capacity(module.info.imported_functions.len());
579    for (
580        index,
581        ImportName {
582            namespace_index,
583            name_index,
584        },
585    ) in &module.info.imported_functions
586    {
587        let sig_index = module.info.func_assoc[index.convert_up(&module.info)];
588        let expected_sig = &module.info.signatures[sig_index];
589
590        let namespace = module.info.namespace_table.get(*namespace_index);
591        let name = module.info.name_table.get(*name_index);
592
593        let import =
594            imports.maybe_with_namespace(namespace, |namespace| namespace.get_export(name));
595
596        match import {
597            Some(Export::Function {
598                func,
599                ctx,
600                signature,
601            }) => {
602                if *expected_sig == *signature {
603                    functions.push(vm::ImportedFunc {
604                        func: func.inner(),
605                        func_ctx: NonNull::new(Box::into_raw(Box::new(vm::FuncCtx {
606                            //                      ^^^^^^^^ `vm::FuncCtx` is purposely leaked.
607                            //                               It is dropped by the specific `Drop`
608                            //                               implementation of `ImportBacking`.
609                            vmctx: NonNull::new(match ctx {
610                                Context::External(vmctx) => vmctx,
611                                Context::ExternalWithEnv(vmctx_, _) => {
612                                    if vmctx_.is_null() {
613                                        vmctx
614                                    } else {
615                                        vmctx_
616                                    }
617                                }
618                                Context::Internal => vmctx,
619                            })
620                            .expect("`vmctx` must not be null."),
621                            func_env: match ctx {
622                                Context::ExternalWithEnv(_, func_env) => func_env,
623                                _ => None,
624                            },
625                        })))
626                        .unwrap(),
627                    });
628                } else {
629                    link_errors.push(LinkError::IncorrectImportSignature {
630                        namespace: namespace.to_string(),
631                        name: name.to_string(),
632                        expected: (*expected_sig).clone(),
633                        found: (*signature).clone(),
634                    });
635                }
636            }
637            Some(export_type) => {
638                let export_type_name = match export_type {
639                    Export::Function { .. } => "function",
640                    Export::Memory { .. } => "memory",
641                    Export::Table { .. } => "table",
642                    Export::Global { .. } => "global",
643                }
644                .to_string();
645                link_errors.push(LinkError::IncorrectImportType {
646                    namespace: namespace.to_string(),
647                    name: name.to_string(),
648                    expected: "function".to_string(),
649                    found: export_type_name,
650                });
651            }
652            None => {
653                if imports.allow_missing_functions {
654                    let always_trap = Func::new(always_trap);
655
656                    functions.push(vm::ImportedFunc {
657                        func: always_trap.get_vm_func().as_ptr(),
658                        func_ctx: NonNull::new(Box::into_raw(Box::new(vm::FuncCtx {
659                            //                      ^^^^^^^^ `vm::FuncCtx` is purposely leaked.
660                            //                               It is dropped by the specific `Drop`
661                            //                               implementation of `ImportBacking`.
662                            vmctx: NonNull::new(vmctx).expect("`vmctx` must not be null."),
663                            func_env: None,
664                        })))
665                        .unwrap(),
666                    });
667                } else {
668                    link_errors.push(LinkError::ImportNotFound {
669                        namespace: namespace.to_string(),
670                        name: name.to_string(),
671                    });
672                }
673            }
674        }
675    }
676
677    if !link_errors.is_empty() {
678        Err(link_errors)
679    } else {
680        Ok(functions.into_boxed_map())
681    }
682}
683
684fn import_memories(
685    module: &ModuleInner,
686    imports: &ImportObject,
687) -> LinkResult<(
688    BoxedMap<ImportedMemoryIndex, Memory>,
689    BoxedMap<ImportedMemoryIndex, *mut vm::LocalMemory>,
690)> {
691    let mut link_errors = vec![];
692    let mut memories = Map::with_capacity(module.info.imported_memories.len());
693    let mut vm_memories = Map::with_capacity(module.info.imported_memories.len());
694    for (
695        _index,
696        (
697            ImportName {
698                namespace_index,
699                name_index,
700            },
701            expected_memory_desc,
702        ),
703    ) in &module.info.imported_memories
704    {
705        let namespace = module.info.namespace_table.get(*namespace_index);
706        let name = module.info.name_table.get(*name_index);
707
708        let memory_import =
709            imports.maybe_with_namespace(namespace, |namespace| namespace.get_export(name));
710        match memory_import {
711            Some(Export::Memory(memory)) => {
712                if expected_memory_desc.fits_in_imported(memory.descriptor()) {
713                    memories.push(memory.clone());
714                    vm_memories.push(memory.vm_local_memory());
715                } else {
716                    link_errors.push(LinkError::IncorrectMemoryDescriptor {
717                        namespace: namespace.to_string(),
718                        name: name.to_string(),
719                        expected: *expected_memory_desc,
720                        found: memory.descriptor(),
721                    });
722                }
723            }
724            Some(export_type) => {
725                let export_type_name = match export_type {
726                    Export::Function { .. } => "function",
727                    Export::Memory { .. } => "memory",
728                    Export::Table { .. } => "table",
729                    Export::Global { .. } => "global",
730                }
731                .to_string();
732                link_errors.push(LinkError::IncorrectImportType {
733                    namespace: namespace.to_string(),
734                    name: name.to_string(),
735                    expected: "memory".to_string(),
736                    found: export_type_name,
737                });
738            }
739            None => {
740                link_errors.push(LinkError::ImportNotFound {
741                    namespace: namespace.to_string(),
742                    name: name.to_string(),
743                });
744            }
745        }
746    }
747
748    if !link_errors.is_empty() {
749        Err(link_errors)
750    } else {
751        Ok((memories.into_boxed_map(), vm_memories.into_boxed_map()))
752    }
753}
754
755fn import_tables(
756    module: &ModuleInner,
757    imports: &ImportObject,
758) -> LinkResult<(
759    BoxedMap<ImportedTableIndex, Table>,
760    BoxedMap<ImportedTableIndex, *mut vm::LocalTable>,
761)> {
762    let mut link_errors = vec![];
763    let mut tables = Map::with_capacity(module.info.imported_tables.len());
764    let mut vm_tables = Map::with_capacity(module.info.imported_tables.len());
765    for (
766        _index,
767        (
768            ImportName {
769                namespace_index,
770                name_index,
771            },
772            expected_table_desc,
773        ),
774    ) in &module.info.imported_tables
775    {
776        let namespace = module.info.namespace_table.get(*namespace_index);
777        let name = module.info.name_table.get(*name_index);
778
779        let table_import =
780            imports.maybe_with_namespace(namespace, |namespace| namespace.get_export(name));
781        match table_import {
782            Some(Export::Table(mut table)) => {
783                if expected_table_desc.fits_in_imported(table.descriptor()) {
784                    vm_tables.push(table.vm_local_table());
785                    tables.push(table);
786                } else {
787                    link_errors.push(LinkError::IncorrectTableDescriptor {
788                        namespace: namespace.to_string(),
789                        name: name.to_string(),
790                        expected: *expected_table_desc,
791                        found: table.descriptor(),
792                    });
793                }
794            }
795            Some(export_type) => {
796                let export_type_name = match export_type {
797                    Export::Function { .. } => "function",
798                    Export::Memory { .. } => "memory",
799                    Export::Table { .. } => "table",
800                    Export::Global { .. } => "global",
801                }
802                .to_string();
803                link_errors.push(LinkError::IncorrectImportType {
804                    namespace: namespace.to_string(),
805                    name: name.to_string(),
806                    expected: "table".to_string(),
807                    found: export_type_name,
808                });
809            }
810            None => {
811                link_errors.push(LinkError::ImportNotFound {
812                    namespace: namespace.to_string(),
813                    name: name.to_string(),
814                });
815            }
816        }
817    }
818
819    if link_errors.len() > 0 {
820        Err(link_errors)
821    } else {
822        Ok((tables.into_boxed_map(), vm_tables.into_boxed_map()))
823    }
824}
825
826fn import_globals(
827    module: &ModuleInner,
828    imports: &ImportObject,
829) -> LinkResult<(
830    BoxedMap<ImportedGlobalIndex, Global>,
831    BoxedMap<ImportedGlobalIndex, *mut vm::LocalGlobal>,
832)> {
833    let mut link_errors = vec![];
834    let mut globals = Map::with_capacity(module.info.imported_globals.len());
835    let mut vm_globals = Map::with_capacity(module.info.imported_globals.len());
836    for (
837        _,
838        (
839            ImportName {
840                namespace_index,
841                name_index,
842            },
843            imported_global_desc,
844        ),
845    ) in &module.info.imported_globals
846    {
847        let namespace = module.info.namespace_table.get(*namespace_index);
848        let name = module.info.name_table.get(*name_index);
849        let import =
850            imports.maybe_with_namespace(namespace, |namespace| namespace.get_export(name));
851        match import {
852            Some(Export::Global(mut global)) => {
853                if global.descriptor() == *imported_global_desc {
854                    vm_globals.push(global.vm_local_global());
855                    globals.push(global);
856                } else {
857                    link_errors.push(LinkError::IncorrectGlobalDescriptor {
858                        namespace: namespace.to_string(),
859                        name: name.to_string(),
860                        expected: *imported_global_desc,
861                        found: global.descriptor(),
862                    });
863                }
864            }
865            Some(export_type) => {
866                let export_type_name = match export_type {
867                    Export::Function { .. } => "function",
868                    Export::Memory { .. } => "memory",
869                    Export::Table { .. } => "table",
870                    Export::Global { .. } => "global",
871                }
872                .to_string();
873                link_errors.push(LinkError::IncorrectImportType {
874                    namespace: namespace.to_string(),
875                    name: name.to_string(),
876                    expected: "global".to_string(),
877                    found: export_type_name,
878                });
879            }
880            None => {
881                link_errors.push(LinkError::ImportNotFound {
882                    namespace: namespace.to_string(),
883                    name: name.to_string(),
884                });
885            }
886        }
887    }
888
889    if !link_errors.is_empty() {
890        Err(link_errors)
891    } else {
892        Ok((globals.into_boxed_map(), vm_globals.into_boxed_map()))
893    }
894}