Skip to main content

wasmer_vm/instance/
mod.rs

1// This file contains code from external sources.
2// Attributions: https://github.com/wasmerio/wasmer/blob/main/docs/ATTRIBUTIONS.md
3
4//! An `Instance` contains all the runtime state used by execution of
5//! a WebAssembly module (except its callstack and register state). An
6//! `VMInstance` is a wrapper around `Instance` that manages
7//! how it is allocated and deallocated.
8
9mod allocator;
10
11use crate::LinearMemory;
12use crate::imports::Imports;
13use crate::store::{InternalStoreHandle, StoreObjects};
14use crate::table::TableElement;
15use crate::trap::{Trap, TrapCode};
16use crate::vmcontext::{
17    VMBuiltinFunctionsArray, VMCallerCheckedAnyfunc, VMContext, VMFunctionContext,
18    VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport, VMMemoryDefinition,
19    VMMemoryImport, VMSharedTagIndex, VMSignatureHash, VMTableDefinition, VMTableImport,
20    VMTrampoline, memory_copy, memory_fill, memory32_atomic_check_notify, memory32_atomic_check32,
21    memory32_atomic_check64,
22};
23use crate::{FunctionBodyPtr, MaybeInstanceOwned, TrapHandlerFn, VMTag, wasmer_call_trampoline};
24use crate::{VMConfig, VMFuncRef, VMFunction, VMGlobal, VMMemory, VMTable};
25use crate::{export::VMExtern, threadconditions::ExpectedValue};
26pub use allocator::InstanceAllocator;
27use core::mem::offset_of;
28use itertools::Itertools;
29use more_asserts::assert_lt;
30use std::alloc::Layout;
31use std::cell::RefCell;
32use std::collections::HashMap;
33use std::convert::TryFrom;
34use std::fmt;
35use std::mem;
36use std::ptr::{self, NonNull};
37use std::slice;
38use std::sync::Arc;
39use wasmer_types::entity::{BoxedSlice, EntityRef, PrimaryMap, packed_option::ReservedValue};
40use wasmer_types::{
41    DataIndex, DataInitializer, ElemIndex, ExportIndex, FunctionIndex, GlobalIndex, GlobalInit,
42    InitExpr, InitExprOp, LocalFunctionIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex,
43    MemoryError, MemoryIndex, ModuleInfo, Pages, RawValue, SignatureIndex, TableIndex, TagIndex,
44    VMOffsets,
45};
46
47/// A WebAssembly instance.
48///
49/// The type is dynamically-sized. Indeed, the `vmctx` field can
50/// contain various data. That's why the type has a C representation
51/// to ensure that the `vmctx` field is last. See the documentation of
52/// the `vmctx` field to learn more.
53#[repr(C)]
54#[allow(clippy::type_complexity)]
55pub(crate) struct Instance {
56    /// The `ModuleInfo` this `Instance` was instantiated from.
57    module: Arc<ModuleInfo>,
58
59    /// Pointer to the object store of the context owning this instance.
60    context: *mut StoreObjects,
61
62    /// Offsets in the `vmctx` region.
63    offsets: VMOffsets,
64
65    /// WebAssembly linear memory data.
66    memories: BoxedSlice<LocalMemoryIndex, InternalStoreHandle<VMMemory>>,
67
68    /// WebAssembly table data.
69    tables: BoxedSlice<LocalTableIndex, InternalStoreHandle<VMTable>>,
70
71    /// WebAssembly global data.
72    globals: BoxedSlice<LocalGlobalIndex, InternalStoreHandle<VMGlobal>>,
73
74    /// WebAssembly tag data. Notably, this stores *all* tags, not just local ones.
75    tags: BoxedSlice<TagIndex, InternalStoreHandle<VMTag>>,
76
77    /// Pointers to functions in executable memory.
78    functions: BoxedSlice<LocalFunctionIndex, FunctionBodyPtr>,
79
80    /// Pointers to function call trampolines in executable memory.
81    function_call_trampolines: BoxedSlice<SignatureIndex, VMTrampoline>,
82
83    /// Passive elements in this instantiation. As `elem.drop`s happen, these
84    /// entries get removed.
85    passive_elements: RefCell<HashMap<ElemIndex, Box<[Option<VMFuncRef>]>>>,
86
87    /// Per-instance view of the module's passive data segments.
88    ///
89    /// A `None` entry (dropped) or a missing entry is treated as an empty slice.
90    ///
91    /// The bytes are shared with the module via `Arc` (no per-instance copy).
92    /// `data.drop` replaces an entry's value with `None` to mark the segment
93    /// unusable for subsequent `memory.init` on this instance, without
94    /// affecting the shared module bytes or any other instance.
95    passive_data: RefCell<HashMap<DataIndex, Option<Arc<[u8]>>>>,
96
97    /// Mapping of function indices to their func ref backing data. `VMFuncRef`s
98    /// will point to elements here for functions defined by this instance.
99    funcrefs: BoxedSlice<LocalFunctionIndex, VMCallerCheckedAnyfunc>,
100
101    /// Mapping of function indices to their func ref backing data. `VMFuncRef`s
102    /// will point to elements here for functions imported by this instance.
103    imported_funcrefs: BoxedSlice<FunctionIndex, NonNull<VMCallerCheckedAnyfunc>>,
104
105    /// Additional context used by compiled WebAssembly code. This
106    /// field is last, and represents a dynamically-sized array that
107    /// extends beyond the nominal end of the struct (similar to a
108    /// flexible array member).
109    vmctx: VMContext,
110}
111
112impl fmt::Debug for Instance {
113    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
114        formatter.debug_struct("Instance").finish()
115    }
116}
117
118#[allow(clippy::cast_ptr_alignment)]
119impl Instance {
120    /// Helper function to access various locations offset from our `*mut
121    /// VMContext` object.
122    unsafe fn vmctx_plus_offset<T>(&self, offset: u32) -> *mut T {
123        unsafe {
124            (self.vmctx_ptr() as *mut u8)
125                .add(usize::try_from(offset).unwrap())
126                .cast()
127        }
128    }
129
130    fn module(&self) -> &Arc<ModuleInfo> {
131        &self.module
132    }
133
134    pub(crate) fn module_ref(&self) -> &ModuleInfo {
135        &self.module
136    }
137
138    pub(crate) fn context(&self) -> &StoreObjects {
139        unsafe { &*self.context }
140    }
141
142    pub(crate) fn context_mut(&mut self) -> &mut StoreObjects {
143        unsafe { &mut *self.context }
144    }
145
146    /// Offsets in the `vmctx` region.
147    fn offsets(&self) -> &VMOffsets {
148        &self.offsets
149    }
150
151    /// Return the indexed `VMFunctionImport`.
152    fn imported_function(&self, index: FunctionIndex) -> &VMFunctionImport {
153        let index = usize::try_from(index.as_u32()).unwrap();
154        unsafe { &*self.imported_functions_ptr().add(index) }
155    }
156
157    /// Return a pointer to the `VMFunctionImport`s.
158    fn imported_functions_ptr(&self) -> *mut VMFunctionImport {
159        unsafe { self.vmctx_plus_offset(self.offsets.vmctx_imported_functions_begin()) }
160    }
161
162    /// Return the index `VMTableImport`.
163    fn imported_table(&self, index: TableIndex) -> &VMTableImport {
164        let index = usize::try_from(index.as_u32()).unwrap();
165        unsafe { &*self.imported_tables_ptr().add(index) }
166    }
167
168    /// Return a pointer to the `VMTableImports`s.
169    fn imported_tables_ptr(&self) -> *mut VMTableImport {
170        unsafe { self.vmctx_plus_offset(self.offsets.vmctx_imported_tables_begin()) }
171    }
172
173    /// Return the indexed `VMMemoryImport`.
174    fn imported_memory(&self, index: MemoryIndex) -> &VMMemoryImport {
175        let index = usize::try_from(index.as_u32()).unwrap();
176        unsafe { &*self.imported_memories_ptr().add(index) }
177    }
178
179    /// Return a pointer to the `VMMemoryImport`s.
180    fn imported_memories_ptr(&self) -> *mut VMMemoryImport {
181        unsafe { self.vmctx_plus_offset(self.offsets.vmctx_imported_memories_begin()) }
182    }
183
184    /// Return the indexed `VMGlobalImport`.
185    fn imported_global(&self, index: GlobalIndex) -> &VMGlobalImport {
186        let index = usize::try_from(index.as_u32()).unwrap();
187        unsafe { &*self.imported_globals_ptr().add(index) }
188    }
189
190    /// Return a pointer to the `VMGlobalImport`s.
191    fn imported_globals_ptr(&self) -> *mut VMGlobalImport {
192        unsafe { self.vmctx_plus_offset(self.offsets.vmctx_imported_globals_begin()) }
193    }
194
195    /// Return the indexed `VMSharedTagIndex`.
196    #[cfg_attr(target_os = "windows", allow(dead_code))]
197    pub(crate) fn shared_tag_ptr(&self, index: TagIndex) -> &VMSharedTagIndex {
198        let index = usize::try_from(index.as_u32()).unwrap();
199        unsafe { &*self.shared_tags_ptr().add(index) }
200    }
201
202    /// Return a pointer to the `VMSharedTagIndex`s.
203    pub(crate) fn shared_tags_ptr(&self) -> *mut VMSharedTagIndex {
204        unsafe { self.vmctx_plus_offset(self.offsets.vmctx_tag_ids_begin()) }
205    }
206
207    /// Return the indexed `VMTableDefinition`.
208    #[allow(dead_code)]
209    fn table(&self, index: LocalTableIndex) -> VMTableDefinition {
210        unsafe { *self.table_ptr(index).as_ref() }
211    }
212
213    #[allow(dead_code)]
214    /// Updates the value for a defined table to `VMTableDefinition`.
215    fn set_table(&self, index: LocalTableIndex, table: &VMTableDefinition) {
216        unsafe {
217            *self.table_ptr(index).as_ptr() = *table;
218        }
219    }
220
221    /// Return the indexed `VMTableDefinition`.
222    fn table_ptr(&self, index: LocalTableIndex) -> NonNull<VMTableDefinition> {
223        let index = usize::try_from(index.as_u32()).unwrap();
224        NonNull::new(unsafe { self.tables_ptr().add(index) }).unwrap()
225    }
226
227    /// Return a pointer to the `VMTableDefinition`s.
228    fn tables_ptr(&self) -> *mut VMTableDefinition {
229        unsafe { self.vmctx_plus_offset(self.offsets.vmctx_tables_begin()) }
230    }
231
232    fn fixed_funcref_table_ptr(
233        &self,
234        index: LocalTableIndex,
235    ) -> Option<NonNull<VMCallerCheckedAnyfunc>> {
236        let offset = self.offsets.vmctx_fixed_funcref_table_anyfuncs(index)?;
237        Some(NonNull::new(unsafe { self.vmctx_plus_offset(offset) }).unwrap())
238    }
239
240    fn sync_fixed_funcref_table_element(
241        &self,
242        table_index: LocalTableIndex,
243        index: u32,
244        funcref: Option<VMFuncRef>,
245    ) {
246        let Some(base) = self.fixed_funcref_table_ptr(table_index) else {
247            return;
248        };
249        unsafe {
250            *base.as_ptr().add(index as usize) = anyfunc_from_funcref(funcref);
251        }
252    }
253
254    fn sync_fixed_funcref_table(&self, table_index: LocalTableIndex) {
255        let Some(base) = self.fixed_funcref_table_ptr(table_index) else {
256            return;
257        };
258        let table = self.tables[table_index].get(self.context());
259        for index in 0..table.size() {
260            let TableElement::FuncRef(funcref) = table.get(index).unwrap() else {
261                unreachable!("fixed funcref tables cannot contain externrefs");
262            };
263            unsafe {
264                *base.as_ptr().add(index as usize) = anyfunc_from_funcref(funcref);
265            }
266        }
267    }
268
269    fn sync_fixed_funcref_table_by_index(&self, table_index: TableIndex) {
270        if let Some(local_table_index) = self.module.local_table_index(table_index) {
271            self.sync_fixed_funcref_table(local_table_index);
272        }
273    }
274
275    /// Get a locally defined or imported memory.
276    fn get_memory(&self, index: MemoryIndex) -> VMMemoryDefinition {
277        if let Some(local_index) = self.module.local_memory_index(index) {
278            self.memory(local_index)
279        } else {
280            let import = self.imported_memory(index);
281            unsafe { *import.definition.as_ref() }
282        }
283    }
284
285    /// Return the indexed `VMMemoryDefinition`.
286    fn memory(&self, index: LocalMemoryIndex) -> VMMemoryDefinition {
287        unsafe { *self.memory_ptr(index).as_ref() }
288    }
289
290    #[allow(dead_code)]
291    /// Set the indexed memory to `VMMemoryDefinition`.
292    fn set_memory(&self, index: LocalMemoryIndex, mem: &VMMemoryDefinition) {
293        unsafe {
294            *self.memory_ptr(index).as_ptr() = *mem;
295        }
296    }
297
298    /// Return the indexed `VMMemoryDefinition`.
299    fn memory_ptr(&self, index: LocalMemoryIndex) -> NonNull<VMMemoryDefinition> {
300        let index = usize::try_from(index.as_u32()).unwrap();
301        NonNull::new(unsafe { self.memories_ptr().add(index) }).unwrap()
302    }
303
304    /// Return a pointer to the `VMMemoryDefinition`s.
305    fn memories_ptr(&self) -> *mut VMMemoryDefinition {
306        unsafe { self.vmctx_plus_offset(self.offsets.vmctx_memories_begin()) }
307    }
308
309    /// Get a locally defined or imported memory.
310    fn get_vmmemory(&self, index: MemoryIndex) -> &VMMemory {
311        if let Some(local_index) = self.module.local_memory_index(index) {
312            unsafe {
313                self.memories
314                    .get(local_index)
315                    .unwrap()
316                    .get(self.context.as_ref().unwrap())
317            }
318        } else {
319            let import = self.imported_memory(index);
320            unsafe { import.handle.get(self.context.as_ref().unwrap()) }
321        }
322    }
323
324    /// Get a locally defined or imported memory.
325    fn get_vmmemory_mut(&mut self, index: MemoryIndex) -> &mut VMMemory {
326        if let Some(local_index) = self.module.local_memory_index(index) {
327            unsafe {
328                self.memories
329                    .get_mut(local_index)
330                    .unwrap()
331                    .get_mut(self.context.as_mut().unwrap())
332            }
333        } else {
334            let import = self.imported_memory(index);
335            unsafe { import.handle.get_mut(self.context.as_mut().unwrap()) }
336        }
337    }
338
339    /// Get a locally defined memory as mutable.
340    fn get_local_vmmemory_mut(&mut self, local_index: LocalMemoryIndex) -> &mut VMMemory {
341        unsafe {
342            self.memories
343                .get_mut(local_index)
344                .unwrap()
345                .get_mut(self.context.as_mut().unwrap())
346        }
347    }
348
349    /// Return the indexed `VMGlobalDefinition`.
350    fn global(&self, index: LocalGlobalIndex) -> VMGlobalDefinition {
351        unsafe { self.global_ptr(index).as_ref().clone() }
352    }
353
354    /// Set the indexed global to `VMGlobalDefinition`.
355    #[allow(dead_code)]
356    fn set_global(&self, index: LocalGlobalIndex, global: &VMGlobalDefinition) {
357        unsafe {
358            *self.global_ptr(index).as_ptr() = global.clone();
359        }
360    }
361
362    /// Return the indexed `VMGlobalDefinition`.
363    fn global_ptr(&self, index: LocalGlobalIndex) -> NonNull<VMGlobalDefinition> {
364        let index = usize::try_from(index.as_u32()).unwrap();
365        NonNull::new(unsafe { self.globals_ptr().add(index) }).unwrap()
366    }
367
368    /// Return a pointer to the `VMGlobalDefinition`s.
369    fn globals_ptr(&self) -> *mut VMGlobalDefinition {
370        unsafe { self.vmctx_plus_offset(self.offsets.vmctx_globals_begin()) }
371    }
372
373    /// Return a pointer to the `VMBuiltinFunctionsArray`.
374    fn builtin_functions_ptr(&self) -> *mut VMBuiltinFunctionsArray {
375        unsafe { self.vmctx_plus_offset(self.offsets.vmctx_builtin_functions_begin()) }
376    }
377
378    /// Return a reference to the vmctx used by compiled wasm code.
379    fn vmctx(&self) -> &VMContext {
380        &self.vmctx
381    }
382
383    /// Return a raw pointer to the vmctx used by compiled wasm code.
384    fn vmctx_ptr(&self) -> *mut VMContext {
385        self.vmctx() as *const VMContext as *mut VMContext
386    }
387
388    /// Invoke the WebAssembly start function of the instance, if one is present.
389    fn invoke_start_function(
390        &self,
391        config: &VMConfig,
392        trap_handler: Option<*const TrapHandlerFn<'static>>,
393    ) -> Result<(), Trap> {
394        let start_index = match self.module.start_function {
395            Some(idx) => idx,
396            None => return Ok(()),
397        };
398
399        let (callee_address, callee_vmctx) = match self.module.local_func_index(start_index) {
400            Some(local_index) => {
401                let body = self
402                    .functions
403                    .get(local_index)
404                    .expect("function index is out of bounds")
405                    .0;
406                (
407                    body as *const _,
408                    VMFunctionContext {
409                        vmctx: self.vmctx_ptr(),
410                    },
411                )
412            }
413            None => {
414                assert_lt!(start_index.index(), self.module.num_imported_functions);
415                let import = self.imported_function(start_index);
416                (import.body, import.environment)
417            }
418        };
419
420        let sig = self.module.functions[start_index];
421        let trampoline = self.function_call_trampolines[sig];
422        let mut values_vec = vec![];
423
424        unsafe {
425            // Even though we already know the type of the function we need to call, in certain
426            // specific cases trampoline prepare callee arguments for specific optimizations, such
427            // as passing g0 and m0_base_ptr as parameters.
428            wasmer_call_trampoline(
429                trap_handler,
430                config,
431                callee_vmctx,
432                trampoline,
433                callee_address,
434                values_vec.as_mut_ptr(),
435            )
436        }
437    }
438
439    /// Return the offset from the vmctx pointer to its containing `Instance`.
440    #[inline]
441    pub(crate) fn vmctx_offset() -> isize {
442        offset_of!(Self, vmctx) as isize
443    }
444
445    /// Return the table index for the given `VMTableDefinition`.
446    pub(crate) fn table_index(&self, table: &VMTableDefinition) -> LocalTableIndex {
447        let begin: *const VMTableDefinition = self.tables_ptr() as *const _;
448        let end: *const VMTableDefinition = table;
449        // TODO: Use `offset_from` once it stabilizes.
450        let index = LocalTableIndex::new(
451            (end as usize - begin as usize) / mem::size_of::<VMTableDefinition>(),
452        );
453        assert_lt!(index.index(), self.tables.len());
454        index
455    }
456
457    /// Return the memory index for the given `VMMemoryDefinition`.
458    pub(crate) fn memory_index(&self, memory: &VMMemoryDefinition) -> LocalMemoryIndex {
459        let begin: *const VMMemoryDefinition = self.memories_ptr() as *const _;
460        let end: *const VMMemoryDefinition = memory;
461        // TODO: Use `offset_from` once it stabilizes.
462        let index = LocalMemoryIndex::new(
463            (end as usize - begin as usize) / mem::size_of::<VMMemoryDefinition>(),
464        );
465        assert_lt!(index.index(), self.memories.len());
466        index
467    }
468
469    /// Grow memory by the specified amount of pages.
470    ///
471    /// Returns `None` if memory can't be grown by the specified amount
472    /// of pages.
473    pub(crate) fn memory_grow<IntoPages>(
474        &mut self,
475        memory_index: LocalMemoryIndex,
476        delta: IntoPages,
477    ) -> Result<Pages, MemoryError>
478    where
479        IntoPages: Into<Pages>,
480    {
481        let mem = *self
482            .memories
483            .get(memory_index)
484            .unwrap_or_else(|| panic!("no memory for index {}", memory_index.index()));
485        mem.get_mut(self.context_mut()).grow(delta.into())
486    }
487
488    /// Grow imported memory by the specified amount of pages.
489    ///
490    /// Returns `None` if memory can't be grown by the specified amount
491    /// of pages.
492    ///
493    /// # Safety
494    /// This and `imported_memory_size` are currently unsafe because they
495    /// dereference the memory import's pointers.
496    pub(crate) unsafe fn imported_memory_grow<IntoPages>(
497        &mut self,
498        memory_index: MemoryIndex,
499        delta: IntoPages,
500    ) -> Result<Pages, MemoryError>
501    where
502        IntoPages: Into<Pages>,
503    {
504        let import = self.imported_memory(memory_index);
505        let mem = import.handle;
506        mem.get_mut(self.context_mut()).grow(delta.into())
507    }
508
509    /// Returns the number of allocated wasm pages.
510    pub(crate) fn memory_size(&self, memory_index: LocalMemoryIndex) -> Pages {
511        let mem = *self
512            .memories
513            .get(memory_index)
514            .unwrap_or_else(|| panic!("no memory for index {}", memory_index.index()));
515        mem.get(self.context()).size()
516    }
517
518    /// Returns the number of allocated wasm pages in an imported memory.
519    ///
520    /// # Safety
521    /// This and `imported_memory_grow` are currently unsafe because they
522    /// dereference the memory import's pointers.
523    pub(crate) unsafe fn imported_memory_size(&self, memory_index: MemoryIndex) -> Pages {
524        let import = self.imported_memory(memory_index);
525        let mem = import.handle;
526        mem.get(self.context()).size()
527    }
528
529    /// Returns the number of elements in a given table.
530    pub(crate) fn table_size(&self, table_index: LocalTableIndex) -> u32 {
531        let table = self
532            .tables
533            .get(table_index)
534            .unwrap_or_else(|| panic!("no table for index {}", table_index.index()));
535        table.get(self.context()).size()
536    }
537
538    /// Returns the number of elements in a given imported table.
539    ///
540    /// # Safety
541    /// `table_index` must be a valid, imported table index.
542    pub(crate) unsafe fn imported_table_size(&self, table_index: TableIndex) -> u32 {
543        let import = self.imported_table(table_index);
544        let table = import.handle;
545        table.get(self.context()).size()
546    }
547
548    /// Grow table by the specified amount of elements.
549    ///
550    /// Returns `None` if table can't be grown by the specified amount
551    /// of elements.
552    pub(crate) fn table_grow(
553        &mut self,
554        table_index: LocalTableIndex,
555        delta: u32,
556        init_value: TableElement,
557    ) -> Option<u32> {
558        let table = *self
559            .tables
560            .get(table_index)
561            .unwrap_or_else(|| panic!("no table for index {}", table_index.index()));
562        table.get_mut(self.context_mut()).grow(delta, init_value)
563    }
564
565    /// Grow table by the specified amount of elements.
566    ///
567    /// # Safety
568    /// `table_index` must be a valid, imported table index.
569    pub(crate) unsafe fn imported_table_grow(
570        &mut self,
571        table_index: TableIndex,
572        delta: u32,
573        init_value: TableElement,
574    ) -> Option<u32> {
575        let import = self.imported_table(table_index);
576        let table = import.handle;
577        table.get_mut(self.context_mut()).grow(delta, init_value)
578    }
579
580    /// Get table element by index.
581    pub(crate) fn table_get(
582        &self,
583        table_index: LocalTableIndex,
584        index: u32,
585    ) -> Option<TableElement> {
586        let table = self
587            .tables
588            .get(table_index)
589            .unwrap_or_else(|| panic!("no table for index {}", table_index.index()));
590        table.get(self.context()).get(index)
591    }
592
593    /// Returns the element at the given index.
594    ///
595    /// # Safety
596    /// `table_index` must be a valid, imported table index.
597    pub(crate) unsafe fn imported_table_get(
598        &self,
599        table_index: TableIndex,
600        index: u32,
601    ) -> Option<TableElement> {
602        let import = self.imported_table(table_index);
603        let table = import.handle;
604        table.get(self.context()).get(index)
605    }
606
607    /// Set table element by index.
608    pub(crate) fn table_set(
609        &mut self,
610        table_index: LocalTableIndex,
611        index: u32,
612        val: TableElement,
613    ) -> Result<(), Trap> {
614        let table = *self
615            .tables
616            .get(table_index)
617            .unwrap_or_else(|| panic!("no table for index {}", table_index.index()));
618        let funcref = match &val {
619            TableElement::FuncRef(funcref) => Some(*funcref),
620            TableElement::ExternRef(_) => None,
621        };
622        table.get_mut(self.context_mut()).set(index, val)?;
623        if let Some(funcref) = funcref {
624            self.sync_fixed_funcref_table_element(table_index, index, funcref);
625        }
626        Ok(())
627    }
628
629    /// Set table element by index for an imported table.
630    ///
631    /// # Safety
632    /// `table_index` must be a valid, imported table index.
633    pub(crate) unsafe fn imported_table_set(
634        &mut self,
635        table_index: TableIndex,
636        index: u32,
637        val: TableElement,
638    ) -> Result<(), Trap> {
639        let import = self.imported_table(table_index);
640        let table = import.handle;
641        table.get_mut(self.context_mut()).set(index, val)
642    }
643
644    /// Get a `VMFuncRef` for the given `FunctionIndex`.
645    pub(crate) fn func_ref(&self, function_index: FunctionIndex) -> Option<VMFuncRef> {
646        if function_index == FunctionIndex::reserved_value() {
647            None
648        } else if let Some(local_function_index) = self.module.local_func_index(function_index) {
649            Some(VMFuncRef(NonNull::from(
650                &self.funcrefs[local_function_index],
651            )))
652        } else {
653            Some(VMFuncRef(self.imported_funcrefs[function_index]))
654        }
655    }
656
657    /// The `table.init` operation: initializes a portion of a table with a
658    /// passive element.
659    ///
660    /// # Errors
661    ///
662    /// Returns a `Trap` error when the range within the table is out of bounds
663    /// or the range within the passive element is out of bounds.
664    pub(crate) fn table_init(
665        &mut self,
666        table_index: TableIndex,
667        elem_index: ElemIndex,
668        dst: u32,
669        src: u32,
670        len: u32,
671    ) -> Result<(), Trap> {
672        // https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-init
673
674        let table = self.get_table_handle(table_index);
675        let table = unsafe { table.get_mut(&mut *self.context) };
676        let passive_elements = self.passive_elements.borrow();
677        let elem = passive_elements
678            .get(&elem_index)
679            .map_or::<&[Option<VMFuncRef>], _>(&[], |e| &**e);
680
681        if src.checked_add(len).is_none_or(|n| n as usize > elem.len())
682            || dst.checked_add(len).is_none_or(|m| m > table.size())
683        {
684            return Err(Trap::lib(TrapCode::TableAccessOutOfBounds));
685        }
686
687        for (dst, src) in (dst..dst + len).zip(src..src + len) {
688            table
689                .set(dst, TableElement::FuncRef(elem[src as usize]))
690                .expect("should never panic because we already did the bounds check above");
691        }
692
693        self.sync_fixed_funcref_table_by_index(table_index);
694
695        Ok(())
696    }
697
698    /// The `table.fill` operation: fills a portion of a table with a given value.
699    ///
700    /// # Errors
701    ///
702    /// Returns a `Trap` error when the range within the table is out of bounds
703    pub(crate) fn table_fill(
704        &mut self,
705        table_index: TableIndex,
706        start_index: u32,
707        item: TableElement,
708        len: u32,
709    ) -> Result<(), Trap> {
710        // https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-init
711
712        let table = self.get_table(table_index);
713        let table_size = table.size() as usize;
714
715        if start_index
716            .checked_add(len)
717            .is_none_or(|n| n as usize > table_size)
718        {
719            return Err(Trap::lib(TrapCode::TableAccessOutOfBounds));
720        }
721
722        for i in start_index..(start_index + len) {
723            table
724                .set(i, item.clone())
725                .expect("should never panic because we already did the bounds check above");
726        }
727
728        self.sync_fixed_funcref_table_by_index(table_index);
729
730        Ok(())
731    }
732
733    /// The `table.copy` operation.
734    pub(crate) fn table_copy(
735        &mut self,
736        dst_table_index: TableIndex,
737        src_table_index: TableIndex,
738        dst: u32,
739        src: u32,
740        len: u32,
741    ) -> Result<(), Trap> {
742        let result = if dst_table_index == src_table_index {
743            let table = self.get_table(dst_table_index);
744            table.copy_within(dst, src, len)
745        } else {
746            let dst_table = self.get_table_handle(dst_table_index);
747            let src_table = self.get_table_handle(src_table_index);
748            if dst_table == src_table {
749                unsafe {
750                    dst_table
751                        .get_mut(&mut *self.context)
752                        .copy_within(dst, src, len)
753                }
754            } else {
755                unsafe {
756                    dst_table.get_mut(&mut *self.context).copy(
757                        src_table.get(&*self.context),
758                        dst,
759                        src,
760                        len,
761                    )
762                }
763            }
764        };
765        result?;
766        self.sync_fixed_funcref_table_by_index(dst_table_index);
767
768        Ok(())
769    }
770
771    /// Drop an element.
772    pub(crate) fn elem_drop(&self, elem_index: ElemIndex) {
773        // https://webassembly.github.io/reference-types/core/exec/instructions.html#exec-elem-drop
774
775        let mut passive_elements = self.passive_elements.borrow_mut();
776        passive_elements.remove(&elem_index);
777        // Note that we don't check that we actually removed an element because
778        // dropping a non-passive element is a no-op (not a trap).
779    }
780
781    /// Perform a `memory.copy` between two memories.
782    ///
783    /// # Errors
784    ///
785    /// Returns a `Trap` error when the source or destination range is out of
786    /// bounds.
787    pub(crate) fn memory_copy(
788        &self,
789        dst_memory_index: MemoryIndex,
790        src_memory_index: MemoryIndex,
791        dst: u32,
792        src: u32,
793        len: u32,
794    ) -> Result<(), Trap> {
795        let dst_memory = self.get_memory(dst_memory_index);
796        let src_memory = self.get_memory(src_memory_index);
797        // The following memory copy is not synchronized and is not atomic.
798        unsafe { memory_copy(&dst_memory, &src_memory, dst, src, len) }
799    }
800
801    /// Perform the `memory.fill` operation on a locally defined memory.
802    ///
803    /// # Errors
804    ///
805    /// Returns a `Trap` error if the memory range is out of bounds.
806    pub(crate) fn local_memory_fill(
807        &self,
808        memory_index: LocalMemoryIndex,
809        dst: u32,
810        val: u32,
811        len: u32,
812    ) -> Result<(), Trap> {
813        let memory = self.memory(memory_index);
814        // The following memory fill is not synchronized and is not atomic:
815        unsafe { memory_fill(&memory, dst, val, len) }
816    }
817
818    /// Perform the `memory.fill` operation on an imported memory.
819    ///
820    /// # Errors
821    ///
822    /// Returns a `Trap` error if the memory range is out of bounds.
823    pub(crate) fn imported_memory_fill(
824        &self,
825        memory_index: MemoryIndex,
826        dst: u32,
827        val: u32,
828        len: u32,
829    ) -> Result<(), Trap> {
830        let import = self.imported_memory(memory_index);
831        let memory = unsafe { import.definition.as_ref() };
832        // The following memory fill is not synchronized and is not atomic:
833        unsafe { memory_fill(memory, dst, val, len) }
834    }
835
836    /// Performs the `memory.init` operation.
837    ///
838    /// # Errors
839    ///
840    /// Returns a `Trap` error if the destination range is out of this module's
841    /// memory's bounds or if the source range is outside the data segment's
842    /// bounds.
843    pub(crate) fn memory_init(
844        &self,
845        memory_index: MemoryIndex,
846        data_index: DataIndex,
847        dst: u32,
848        src: u32,
849        len: u32,
850    ) -> Result<(), Trap> {
851        // https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-memory-init
852
853        let memory = self.get_vmmemory(memory_index);
854        let passive_data = self.passive_data.borrow();
855        // A missing entry (never existed) or a dropped one (`None`) both behave
856        // as a zero-length segment, so an in-bounds `memory.init` of non-zero
857        // length traps below.
858        let data = passive_data
859            .get(&data_index)
860            .and_then(|d| d.as_deref())
861            .unwrap_or(&[]);
862
863        let current_length = unsafe { memory.vmmemory().as_ref().current_length };
864        if src.checked_add(len).is_none_or(|n| n as usize > data.len())
865            || dst
866                .checked_add(len)
867                .is_none_or(|m| usize::try_from(m).unwrap() > current_length)
868        {
869            return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds));
870        }
871        let src_slice = &data[src as usize..(src + len) as usize];
872        unsafe { memory.initialize_with_data(dst as usize, src_slice) }
873    }
874
875    /// Drop the given data segment, truncating its length to zero.
876    pub(crate) fn data_drop(&self, data_index: DataIndex) {
877        let mut passive_data = self.passive_data.borrow_mut();
878        // Release this instance's reference to the shared bytes and mark the
879        // segment unusable. Other instances (and the module) are unaffected.
880        if let Some(slot) = passive_data.get_mut(&data_index) {
881            *slot = None;
882        }
883    }
884
885    /// Get a table by index regardless of whether it is locally-defined or an
886    /// imported, foreign table.
887    pub(crate) fn get_table(&mut self, table_index: TableIndex) -> &mut VMTable {
888        if let Some(local_table_index) = self.module.local_table_index(table_index) {
889            self.get_local_table(local_table_index)
890        } else {
891            self.get_foreign_table(table_index)
892        }
893    }
894
895    /// Get a locally-defined table.
896    pub(crate) fn get_local_table(&mut self, index: LocalTableIndex) -> &mut VMTable {
897        let table = self.tables[index];
898        table.get_mut(self.context_mut())
899    }
900
901    /// Get an imported, foreign table.
902    pub(crate) fn get_foreign_table(&mut self, index: TableIndex) -> &mut VMTable {
903        let import = self.imported_table(index);
904        let table = import.handle;
905        table.get_mut(self.context_mut())
906    }
907
908    /// Get a table handle by index regardless of whether it is locally-defined
909    /// or an imported, foreign table.
910    pub(crate) fn get_table_handle(
911        &mut self,
912        table_index: TableIndex,
913    ) -> InternalStoreHandle<VMTable> {
914        if let Some(local_table_index) = self.module.local_table_index(table_index) {
915            self.tables[local_table_index]
916        } else {
917            self.imported_table(table_index).handle
918        }
919    }
920
921    /// # Safety
922    /// See [`LinearMemory::do_wait`].
923    unsafe fn memory_wait(
924        memory: &mut VMMemory,
925        dst: u32,
926        expected: ExpectedValue,
927        timeout: i64,
928    ) -> Result<u32, Trap> {
929        let timeout = if timeout < 0 {
930            None
931        } else {
932            Some(std::time::Duration::from_nanos(timeout as u64))
933        };
934        match unsafe { memory.do_wait(dst, expected, timeout) } {
935            Ok(count) => Ok(count),
936            Err(_err) => Err(Trap::lib(TrapCode::HostInterrupt)),
937        }
938    }
939
940    /// Perform an Atomic.Wait32
941    pub(crate) fn local_memory_wait32(
942        &mut self,
943        memory_index: LocalMemoryIndex,
944        dst: u32,
945        val: u32,
946        timeout: i64,
947    ) -> Result<u32, Trap> {
948        let memory = self.memory(memory_index);
949        //if ! memory.shared {
950        // We should trap according to spec, but official test rely on not trapping...
951        //}
952
953        // Do a fast-path check of the expected value, and also ensure proper alignment
954        let ret = unsafe { memory32_atomic_check32(&memory, dst, val) };
955
956        if let Ok(mut ret) = ret {
957            if ret == 0 {
958                let memory = self.get_local_vmmemory_mut(memory_index);
959                // Safety: we have already checked alignment and bounds in memory32_atomic_check32
960                ret = unsafe { Self::memory_wait(memory, dst, ExpectedValue::U32(val), timeout)? };
961            }
962            Ok(ret)
963        } else {
964            ret
965        }
966    }
967
968    /// Perform an Atomic.Wait32
969    pub(crate) fn imported_memory_wait32(
970        &mut self,
971        memory_index: MemoryIndex,
972        dst: u32,
973        val: u32,
974        timeout: i64,
975    ) -> Result<u32, Trap> {
976        let import = self.imported_memory(memory_index);
977        let memory = unsafe { import.definition.as_ref() };
978        //if ! memory.shared {
979        // We should trap according to spec, but official test rely on not trapping...
980        //}
981
982        // Do a fast-path check of the expected value, and also ensure proper alignment
983        let ret = unsafe { memory32_atomic_check32(memory, dst, val) };
984
985        if let Ok(mut ret) = ret {
986            if ret == 0 {
987                let memory = self.get_vmmemory_mut(memory_index);
988                // Safety: we have already checked alignment and bounds in memory32_atomic_check32
989                ret = unsafe { Self::memory_wait(memory, dst, ExpectedValue::U32(val), timeout)? };
990            }
991            Ok(ret)
992        } else {
993            ret
994        }
995    }
996
997    /// Perform an Atomic.Wait64
998    pub(crate) fn local_memory_wait64(
999        &mut self,
1000        memory_index: LocalMemoryIndex,
1001        dst: u32,
1002        val: u64,
1003        timeout: i64,
1004    ) -> Result<u32, Trap> {
1005        let memory = self.memory(memory_index);
1006        //if ! memory.shared {
1007        // We should trap according to spec, but official test rely on not trapping...
1008        //}
1009
1010        // Do a fast-path check of the expected value, and also ensure proper alignment
1011        let ret = unsafe { memory32_atomic_check64(&memory, dst, val) };
1012
1013        if let Ok(mut ret) = ret {
1014            if ret == 0 {
1015                let memory = self.get_local_vmmemory_mut(memory_index);
1016                // Safety: we have already checked alignment and bounds in memory32_atomic_check64
1017                ret = unsafe { Self::memory_wait(memory, dst, ExpectedValue::U64(val), timeout)? };
1018            }
1019            Ok(ret)
1020        } else {
1021            ret
1022        }
1023    }
1024
1025    /// Perform an Atomic.Wait64
1026    pub(crate) fn imported_memory_wait64(
1027        &mut self,
1028        memory_index: MemoryIndex,
1029        dst: u32,
1030        val: u64,
1031        timeout: i64,
1032    ) -> Result<u32, Trap> {
1033        let import = self.imported_memory(memory_index);
1034        let memory = unsafe { import.definition.as_ref() };
1035        //if ! memory.shared {
1036        // We should trap according to spec, but official test rely on not trapping...
1037        //}
1038
1039        // Do a fast-path check of the expected value, and also ensure proper alignment
1040        let ret = unsafe { memory32_atomic_check64(memory, dst, val) };
1041
1042        if let Ok(mut ret) = ret {
1043            if ret == 0 {
1044                let memory = self.get_vmmemory_mut(memory_index);
1045                // Safety: we have already checked alignment and bounds in memory32_atomic_check64
1046                ret = unsafe { Self::memory_wait(memory, dst, ExpectedValue::U64(val), timeout)? };
1047            }
1048            Ok(ret)
1049        } else {
1050            ret
1051        }
1052    }
1053
1054    /// Perform an Atomic.Notify
1055    pub(crate) fn local_memory_notify(
1056        &mut self,
1057        memory_index: LocalMemoryIndex,
1058        dst: u32,
1059        count: u32,
1060    ) -> Result<u32, Trap> {
1061        let memory = self.memory(memory_index);
1062        memory32_atomic_check_notify(&memory, dst)?;
1063        let memory = self.get_local_vmmemory_mut(memory_index);
1064        Ok(memory.do_notify(dst, count))
1065    }
1066
1067    /// Perform an Atomic.Notify
1068    pub(crate) fn imported_memory_notify(
1069        &mut self,
1070        memory_index: MemoryIndex,
1071        dst: u32,
1072        count: u32,
1073    ) -> Result<u32, Trap> {
1074        let import = self.imported_memory(memory_index);
1075        let memory = unsafe { import.definition.as_ref() };
1076        memory32_atomic_check_notify(memory, dst)?;
1077        let memory = self.get_vmmemory_mut(memory_index);
1078        Ok(memory.do_notify(dst, count))
1079    }
1080}
1081
1082/// A handle holding an `Instance` of a WebAssembly module.
1083///
1084/// This is more or less a public facade of the private `Instance`,
1085/// providing useful higher-level API.
1086#[derive(Debug, Eq, PartialEq)]
1087pub struct VMInstance {
1088    /// The layout of `Instance` (which can vary).
1089    instance_layout: Layout,
1090
1091    /// The `Instance` itself.
1092    ///
1093    /// `Instance` must not be dropped manually by Rust, because it's
1094    /// allocated manually with `alloc` and a specific layout (Rust
1095    /// would be able to drop `Instance` itself but it will imply a
1096    /// memory leak because of `alloc`).
1097    ///
1098    /// No one in the code has a copy of the `Instance`'s
1099    /// pointer. `Self` is the only one.
1100    instance: NonNull<Instance>,
1101}
1102
1103/// VMInstance are created with an InstanceAllocator
1104/// and it will "consume" the memory
1105/// So the Drop here actually free it (else it would be leaked)
1106impl Drop for VMInstance {
1107    fn drop(&mut self) {
1108        let instance_ptr = self.instance.as_ptr();
1109
1110        unsafe {
1111            // Need to drop all the actual Instance members
1112            instance_ptr.drop_in_place();
1113            // And then free the memory allocated for the Instance itself
1114            std::alloc::dealloc(instance_ptr as *mut u8, self.instance_layout);
1115        }
1116    }
1117}
1118
1119impl VMInstance {
1120    /// Create a new `VMInstance` pointing at freshly allocated instance data.
1121    ///
1122    /// # Safety
1123    ///
1124    /// This method is not necessarily inherently unsafe to call, but in general
1125    /// the APIs of an `Instance` are quite unsafe and have not been really
1126    /// audited for safety that much. As a result the unsafety here on this
1127    /// method is a low-overhead way of saying “this is an extremely unsafe type
1128    /// to work with”.
1129    ///
1130    /// Extreme care must be taken when working with `VMInstance` and it's
1131    /// recommended to have relatively intimate knowledge of how it works
1132    /// internally if you'd like to do so. If possible it's recommended to use
1133    /// the `wasmer` crate API rather than this type since that is vetted for
1134    /// safety.
1135    ///
1136    /// However the following must be taken care of before calling this function:
1137    /// - The memory at `instance.tables_ptr()` must be initialized with data for
1138    ///   all the local tables.
1139    /// - The memory at `instance.memories_ptr()` must be initialized with data for
1140    ///   all the local memories.
1141    #[allow(clippy::too_many_arguments)]
1142    pub unsafe fn new(
1143        allocator: InstanceAllocator,
1144        module: Arc<ModuleInfo>,
1145        context: &mut StoreObjects,
1146        finished_functions: BoxedSlice<LocalFunctionIndex, FunctionBodyPtr>,
1147        finished_function_call_trampolines: BoxedSlice<SignatureIndex, VMTrampoline>,
1148        finished_memories: BoxedSlice<LocalMemoryIndex, InternalStoreHandle<VMMemory>>,
1149        finished_tables: BoxedSlice<LocalTableIndex, InternalStoreHandle<VMTable>>,
1150        finished_globals: BoxedSlice<LocalGlobalIndex, InternalStoreHandle<VMGlobal>>,
1151        tags: BoxedSlice<TagIndex, InternalStoreHandle<VMTag>>,
1152        imports: Imports,
1153        vmshared_signatures: BoxedSlice<SignatureIndex, VMSignatureHash>,
1154    ) -> Result<Self, Trap> {
1155        unsafe {
1156            let vmctx_tags = tags
1157                .values()
1158                .map(|m: &InternalStoreHandle<VMTag>| VMSharedTagIndex::new(m.index() as u32))
1159                .collect::<PrimaryMap<TagIndex, VMSharedTagIndex>>()
1160                .into_boxed_slice();
1161            // Share the module's passive data bytes via `Arc` (refcount bump)
1162            // rather than deep-copying them into every instance.
1163            let passive_data = RefCell::new(
1164                module
1165                    .passive_data
1166                    .iter()
1167                    .map(|(&idx, bytes)| (idx, Some(Arc::clone(bytes))))
1168                    .collect::<HashMap<_, _>>(),
1169            );
1170
1171            let handle = {
1172                let offsets = allocator.offsets().clone();
1173                // use dummy value to create an instance so we can get the vmctx pointer
1174                let funcrefs = PrimaryMap::new().into_boxed_slice();
1175                let imported_funcrefs = PrimaryMap::new().into_boxed_slice();
1176                // Create the `Instance`. The unique, the One.
1177                let instance = Instance {
1178                    module,
1179                    context,
1180                    offsets,
1181                    memories: finished_memories,
1182                    tables: finished_tables,
1183                    tags,
1184                    globals: finished_globals,
1185                    functions: finished_functions,
1186                    function_call_trampolines: finished_function_call_trampolines,
1187                    passive_elements: Default::default(),
1188                    passive_data,
1189                    funcrefs,
1190                    imported_funcrefs,
1191                    vmctx: VMContext {},
1192                };
1193
1194                let mut instance_handle = allocator.into_vminstance(instance);
1195
1196                // Set the funcrefs after we've built the instance
1197                {
1198                    let instance = instance_handle.instance_mut();
1199                    let vmctx_ptr = instance.vmctx_ptr();
1200                    (instance.funcrefs, instance.imported_funcrefs) = build_funcrefs(
1201                        &instance.module,
1202                        context,
1203                        &imports,
1204                        &instance.functions,
1205                        &vmshared_signatures,
1206                        &instance.function_call_trampolines,
1207                        vmctx_ptr,
1208                    );
1209                    for local_table_index in instance.tables.keys() {
1210                        instance.sync_fixed_funcref_table(local_table_index);
1211                    }
1212                }
1213
1214                instance_handle
1215            };
1216            let instance = handle.instance();
1217
1218            ptr::copy(
1219                vmctx_tags.values().as_slice().as_ptr(),
1220                instance.shared_tags_ptr(),
1221                vmctx_tags.len(),
1222            );
1223            ptr::copy(
1224                imports.functions.values().as_slice().as_ptr(),
1225                instance.imported_functions_ptr(),
1226                imports.functions.len(),
1227            );
1228            ptr::copy(
1229                imports.tables.values().as_slice().as_ptr(),
1230                instance.imported_tables_ptr(),
1231                imports.tables.len(),
1232            );
1233            ptr::copy(
1234                imports.memories.values().as_slice().as_ptr(),
1235                instance.imported_memories_ptr(),
1236                imports.memories.len(),
1237            );
1238            ptr::copy(
1239                imports.globals.values().as_slice().as_ptr(),
1240                instance.imported_globals_ptr(),
1241                imports.globals.len(),
1242            );
1243            // these should already be set, add asserts here? for:
1244            // - instance.tables_ptr() as *mut VMTableDefinition
1245            // - instance.memories_ptr() as *mut VMMemoryDefinition
1246            ptr::write(
1247                instance.builtin_functions_ptr(),
1248                VMBuiltinFunctionsArray::initialized(),
1249            );
1250
1251            // Perform infallible initialization in this constructor, while fallible
1252            // initialization is deferred to the `initialize` method.
1253            initialize_passive_elements(instance);
1254            initialize_globals(instance);
1255
1256            Ok(handle)
1257        }
1258    }
1259
1260    /// Return a reference to the contained `Instance`.
1261    pub(crate) fn instance(&self) -> &Instance {
1262        unsafe { self.instance.as_ref() }
1263    }
1264
1265    /// Return a mutable reference to the contained `Instance`.
1266    pub(crate) fn instance_mut(&mut self) -> &mut Instance {
1267        unsafe { self.instance.as_mut() }
1268    }
1269
1270    /// Finishes the instantiation process started by `Instance::new`.
1271    ///
1272    /// # Safety
1273    ///
1274    /// Only safe to call immediately after instantiation.
1275    pub unsafe fn finish_instantiation(
1276        &mut self,
1277        config: &VMConfig,
1278        trap_handler: Option<*const TrapHandlerFn<'static>>,
1279        data_initializers: &[DataInitializer<'_>],
1280    ) -> Result<(), Trap> {
1281        let instance = self.instance_mut();
1282
1283        // Apply the initializers.
1284        initialize_tables(instance)?;
1285        initialize_memories(instance, data_initializers)?;
1286
1287        // The WebAssembly spec specifies that the start function is
1288        // invoked automatically at instantiation time.
1289        instance.invoke_start_function(config, trap_handler)?;
1290        Ok(())
1291    }
1292
1293    /// Return a reference to the vmctx used by compiled wasm code.
1294    pub fn vmctx(&self) -> &VMContext {
1295        self.instance().vmctx()
1296    }
1297
1298    /// Return a raw pointer to the vmctx used by compiled wasm code.
1299    pub fn vmctx_ptr(&self) -> *mut VMContext {
1300        self.instance().vmctx_ptr()
1301    }
1302
1303    /// Return a reference to the `VMOffsets` to get offsets in the
1304    /// `Self::vmctx_ptr` region. Be careful when doing pointer
1305    /// arithmetic!
1306    pub fn vmoffsets(&self) -> &VMOffsets {
1307        self.instance().offsets()
1308    }
1309
1310    /// Return a reference-counting pointer to a module.
1311    pub fn module(&self) -> &Arc<ModuleInfo> {
1312        self.instance().module()
1313    }
1314
1315    /// Return a reference to a module.
1316    pub fn module_ref(&self) -> &ModuleInfo {
1317        self.instance().module_ref()
1318    }
1319
1320    /// Lookup an export with the given name.
1321    pub fn lookup(&mut self, field: &str) -> Option<VMExtern> {
1322        let export = *self.module_ref().exports.get(field)?;
1323
1324        Some(self.lookup_by_declaration(export))
1325    }
1326
1327    /// Lookup an export with the given export declaration.
1328    pub fn lookup_by_declaration(&mut self, export: ExportIndex) -> VMExtern {
1329        let instance = self.instance();
1330
1331        match export {
1332            ExportIndex::Function(index) => {
1333                let sig_index = &instance.module.functions[index];
1334                let handle = if let Some(def_index) = instance.module.local_func_index(index) {
1335                    // A VMFunction is lazily created only for functions that are
1336                    // exported.
1337                    let signature = instance.module.signatures[*sig_index].clone();
1338                    let vm_function = VMFunction {
1339                        anyfunc: MaybeInstanceOwned::Instance(NonNull::from(
1340                            &instance.funcrefs[def_index],
1341                        )),
1342                        signature,
1343                        // Any function received is already static at this point as:
1344                        // 1. All locally defined functions in the Wasm have a static signature.
1345                        // 2. All the imported functions are already static (because
1346                        //    they point to the trampolines rather than the dynamic addresses).
1347                        kind: VMFunctionKind::Static,
1348                        host_data: Box::new(()),
1349                    };
1350                    InternalStoreHandle::new(self.instance_mut().context_mut(), vm_function)
1351                } else {
1352                    let import = instance.imported_function(index);
1353                    import.handle
1354                };
1355
1356                VMExtern::Function(handle)
1357            }
1358            ExportIndex::Table(index) => {
1359                let handle = if let Some(def_index) = instance.module.local_table_index(index) {
1360                    instance.tables[def_index]
1361                } else {
1362                    let import = instance.imported_table(index);
1363                    import.handle
1364                };
1365                VMExtern::Table(handle)
1366            }
1367            ExportIndex::Memory(index) => {
1368                let handle = if let Some(def_index) = instance.module.local_memory_index(index) {
1369                    instance.memories[def_index]
1370                } else {
1371                    let import = instance.imported_memory(index);
1372                    import.handle
1373                };
1374                VMExtern::Memory(handle)
1375            }
1376            ExportIndex::Global(index) => {
1377                let handle = if let Some(def_index) = instance.module.local_global_index(index) {
1378                    instance.globals[def_index]
1379                } else {
1380                    let import = instance.imported_global(index);
1381                    import.handle
1382                };
1383                VMExtern::Global(handle)
1384            }
1385
1386            ExportIndex::Tag(index) => {
1387                let handle = instance.tags[index];
1388                VMExtern::Tag(handle)
1389            }
1390        }
1391    }
1392
1393    /// Return an iterator over the exports of this instance.
1394    ///
1395    /// Specifically, it provides access to the key-value pairs, where the keys
1396    /// are export names, and the values are export declarations which can be
1397    /// resolved `lookup_by_declaration`.
1398    pub fn exports(&self) -> indexmap::map::Iter<'_, String, ExportIndex> {
1399        self.module().exports.iter()
1400    }
1401
1402    /// Return the memory index for the given `VMMemoryDefinition` in this instance.
1403    pub fn memory_index(&self, memory: &VMMemoryDefinition) -> LocalMemoryIndex {
1404        self.instance().memory_index(memory)
1405    }
1406
1407    /// Grow memory in this instance by the specified amount of pages.
1408    ///
1409    /// Returns `None` if memory can't be grown by the specified amount
1410    /// of pages.
1411    pub fn memory_grow<IntoPages>(
1412        &mut self,
1413        memory_index: LocalMemoryIndex,
1414        delta: IntoPages,
1415    ) -> Result<Pages, MemoryError>
1416    where
1417        IntoPages: Into<Pages>,
1418    {
1419        self.instance_mut().memory_grow(memory_index, delta)
1420    }
1421
1422    /// Return the table index for the given `VMTableDefinition` in this instance.
1423    pub fn table_index(&self, table: &VMTableDefinition) -> LocalTableIndex {
1424        self.instance().table_index(table)
1425    }
1426
1427    /// Grow table in this instance by the specified amount of pages.
1428    ///
1429    /// Returns `None` if memory can't be grown by the specified amount
1430    /// of pages.
1431    pub fn table_grow(
1432        &mut self,
1433        table_index: LocalTableIndex,
1434        delta: u32,
1435        init_value: TableElement,
1436    ) -> Option<u32> {
1437        self.instance_mut()
1438            .table_grow(table_index, delta, init_value)
1439    }
1440
1441    /// Get table element reference.
1442    ///
1443    /// Returns `None` if index is out of bounds.
1444    pub fn table_get(&self, table_index: LocalTableIndex, index: u32) -> Option<TableElement> {
1445        self.instance().table_get(table_index, index)
1446    }
1447
1448    /// Set table element reference.
1449    ///
1450    /// Returns an error if the index is out of bounds
1451    pub fn table_set(
1452        &mut self,
1453        table_index: LocalTableIndex,
1454        index: u32,
1455        val: TableElement,
1456    ) -> Result<(), Trap> {
1457        self.instance_mut().table_set(table_index, index, val)
1458    }
1459
1460    /// Get a table defined locally within this module.
1461    pub fn get_local_table(&mut self, index: LocalTableIndex) -> &mut VMTable {
1462        self.instance_mut().get_local_table(index)
1463    }
1464}
1465
1466#[allow(clippy::mut_from_ref)]
1467#[allow(dead_code)]
1468/// Return a byte-slice view of a memory's data.
1469unsafe fn get_memory_slice<'instance>(
1470    init: &DataInitializer<'_>,
1471    instance: &'instance Instance,
1472) -> &'instance mut [u8] {
1473    unsafe {
1474        let memory = if let Some(local_memory_index) = instance
1475            .module
1476            .local_memory_index(init.location.memory_index)
1477        {
1478            instance.memory(local_memory_index)
1479        } else {
1480            let import = instance.imported_memory(init.location.memory_index);
1481            *import.definition.as_ref()
1482        };
1483        slice::from_raw_parts_mut(memory.base, memory.current_length)
1484    }
1485}
1486
1487fn get_global(index: GlobalIndex, instance: &Instance) -> RawValue {
1488    unsafe {
1489        if let Some(local_global_index) = instance.module.local_global_index(index) {
1490            instance.global(local_global_index).val
1491        } else {
1492            instance.imported_global(index).definition.as_ref().val
1493        }
1494    }
1495}
1496
1497enum EvaluatedInitExpr {
1498    I32(i32),
1499    I64(i64),
1500}
1501
1502fn eval_init_expr(expr: &InitExpr, instance: &Instance) -> EvaluatedInitExpr {
1503    if expr
1504        .ops()
1505        .first()
1506        .expect("missing expression")
1507        .is_32bit_expression()
1508    {
1509        let mut stack = Vec::with_capacity(expr.ops().len());
1510        for op in expr.ops() {
1511            match *op {
1512                InitExprOp::I32Const(value) => stack.push(value),
1513                InitExprOp::GlobalGetI32(global) => {
1514                    stack.push(unsafe { get_global(global, instance).i32 })
1515                }
1516                InitExprOp::I32Add => {
1517                    let rhs = stack.pop().expect("invalid init expr stack for i32.add");
1518                    let lhs = stack.pop().expect("invalid init expr stack for i32.add");
1519                    stack.push(lhs.wrapping_add(rhs));
1520                }
1521                InitExprOp::I32Sub => {
1522                    let rhs = stack.pop().expect("invalid init expr stack for i32.sub");
1523                    let lhs = stack.pop().expect("invalid init expr stack for i32.sub");
1524                    stack.push(lhs.wrapping_sub(rhs));
1525                }
1526                InitExprOp::I32Mul => {
1527                    let rhs = stack.pop().expect("invalid init expr stack for i32.mul");
1528                    let lhs = stack.pop().expect("invalid init expr stack for i32.mul");
1529                    stack.push(lhs.wrapping_mul(rhs));
1530                }
1531                _ => {
1532                    panic!("unexpected init expr statement: {op:?}");
1533                }
1534            }
1535        }
1536        EvaluatedInitExpr::I32(
1537            stack
1538                .into_iter()
1539                .exactly_one()
1540                .expect("invalid init expr stack shape"),
1541        )
1542    } else {
1543        let mut stack = Vec::with_capacity(expr.ops().len());
1544        for op in expr.ops() {
1545            match *op {
1546                InitExprOp::I64Const(value) => stack.push(value),
1547                InitExprOp::GlobalGetI64(global) => {
1548                    stack.push(unsafe { get_global(global, instance).i64 })
1549                }
1550                InitExprOp::I64Add => {
1551                    let rhs = stack.pop().expect("invalid init expr stack for i64.add");
1552                    let lhs = stack.pop().expect("invalid init expr stack for i64.add");
1553                    stack.push(lhs.wrapping_add(rhs));
1554                }
1555                InitExprOp::I64Sub => {
1556                    let rhs = stack.pop().expect("invalid init expr stack for i64.sub");
1557                    let lhs = stack.pop().expect("invalid init expr stack for i64.sub");
1558                    stack.push(lhs.wrapping_sub(rhs));
1559                }
1560                InitExprOp::I64Mul => {
1561                    let rhs = stack.pop().expect("invalid init expr stack for i64.mul");
1562                    let lhs = stack.pop().expect("invalid init expr stack for i64.mul");
1563                    stack.push(lhs.wrapping_mul(rhs));
1564                }
1565                _ => {
1566                    panic!("unexpected init expr statement: {op:?}");
1567                }
1568            }
1569        }
1570        EvaluatedInitExpr::I64(
1571            stack
1572                .into_iter()
1573                .exactly_one()
1574                .expect("invalid init expr stack shape"),
1575        )
1576    }
1577}
1578
1579/// Initialize the table memory from the provided initializers.
1580fn initialize_tables(instance: &mut Instance) -> Result<(), Trap> {
1581    let module = Arc::clone(&instance.module);
1582    for init in &module.table_initializers {
1583        let EvaluatedInitExpr::I32(start) = eval_init_expr(&init.offset_expr, instance) else {
1584            panic!("unexpected expression type, expected i32");
1585        };
1586        if start < 0 {
1587            return Err(Trap::lib(TrapCode::TableAccessOutOfBounds));
1588        }
1589        let start = start as usize;
1590        let table = instance.get_table_handle(init.table_index);
1591        let table = unsafe { table.get_mut(&mut *instance.context) };
1592
1593        if start
1594            .checked_add(init.elements.len())
1595            .is_none_or(|end| end > table.size() as usize)
1596        {
1597            return Err(Trap::lib(TrapCode::TableAccessOutOfBounds));
1598        }
1599
1600        if let wasmer_types::Type::FuncRef = table.ty().ty {
1601            for (i, func_idx) in init.elements.iter().enumerate() {
1602                let anyfunc = instance.func_ref(*func_idx);
1603                table
1604                    .set_with_construction(
1605                        u32::try_from(start + i).unwrap(),
1606                        TableElement::FuncRef(anyfunc),
1607                        true,
1608                    )
1609                    .unwrap();
1610            }
1611        } else {
1612            for i in 0..init.elements.len() {
1613                table
1614                    .set_with_construction(
1615                        u32::try_from(start + i).unwrap(),
1616                        TableElement::ExternRef(None),
1617                        true,
1618                    )
1619                    .unwrap();
1620            }
1621        }
1622
1623        instance.sync_fixed_funcref_table_by_index(init.table_index);
1624    }
1625
1626    Ok(())
1627}
1628
1629/// Initialize the `Instance::passive_elements` map by resolving the
1630/// `ModuleInfo::passive_elements`'s `FunctionIndex`s into `VMCallerCheckedAnyfunc`s for
1631/// this instance.
1632fn initialize_passive_elements(instance: &Instance) {
1633    let mut passive_elements = instance.passive_elements.borrow_mut();
1634    debug_assert!(
1635        passive_elements.is_empty(),
1636        "should only be called once, at initialization time"
1637    );
1638
1639    passive_elements.extend(instance.module.passive_elements.iter().filter_map(
1640        |(&idx, segments)| -> Option<(ElemIndex, Box<[Option<VMFuncRef>]>)> {
1641            if segments.is_empty() {
1642                None
1643            } else {
1644                Some((
1645                    idx,
1646                    segments
1647                        .iter()
1648                        .map(|s| instance.func_ref(*s))
1649                        .collect::<Box<[Option<VMFuncRef>]>>(),
1650                ))
1651            }
1652        },
1653    ));
1654}
1655
1656/// Initialize the table memory from the provided initializers.
1657fn initialize_memories(
1658    instance: &mut Instance,
1659    data_initializers: &[DataInitializer<'_>],
1660) -> Result<(), Trap> {
1661    for init in data_initializers {
1662        let memory = instance.get_vmmemory(init.location.memory_index);
1663
1664        let EvaluatedInitExpr::I32(start) = eval_init_expr(&init.location.offset_expr, instance)
1665        else {
1666            panic!("unexpected expression type, expected i32");
1667        };
1668        if start < 0 {
1669            return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds));
1670        }
1671        let start = start as usize;
1672        unsafe {
1673            let current_length = memory.vmmemory().as_ref().current_length;
1674            if start
1675                .checked_add(init.data.len())
1676                .is_none_or(|end| end > current_length)
1677            {
1678                return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds));
1679            }
1680            memory.initialize_with_data(start, init.data)?;
1681        }
1682    }
1683
1684    Ok(())
1685}
1686
1687fn initialize_globals(instance: &Instance) {
1688    let module = Arc::clone(&instance.module);
1689    for (index, initializer) in module.global_initializers.iter() {
1690        unsafe {
1691            let to = instance.global_ptr(index).as_ptr();
1692            match initializer {
1693                GlobalInit::I32Const(x) => (*to).val.i32 = *x,
1694                GlobalInit::I64Const(x) => (*to).val.i64 = *x,
1695                GlobalInit::F32Const(x) => (*to).val.f32 = *x,
1696                GlobalInit::F64Const(x) => (*to).val.f64 = *x,
1697                GlobalInit::V128Const(x) => (*to).val.bytes = *x.bytes(),
1698                GlobalInit::GetGlobal(x) => {
1699                    let from: VMGlobalDefinition =
1700                        if let Some(def_x) = module.local_global_index(*x) {
1701                            instance.global(def_x)
1702                        } else {
1703                            instance.imported_global(*x).definition.as_ref().clone()
1704                        };
1705                    *to = from;
1706                }
1707                GlobalInit::RefNullConst => (*to).val.funcref = 0,
1708                GlobalInit::RefFunc(func_idx) => {
1709                    let funcref = instance.func_ref(*func_idx).unwrap();
1710                    (*to).val = funcref.into_raw();
1711                }
1712                GlobalInit::Expr(expr) => match eval_init_expr(expr, instance) {
1713                    EvaluatedInitExpr::I32(value) => (*to).val.i32 = value,
1714                    EvaluatedInitExpr::I64(value) => (*to).val.i64 = value,
1715                },
1716            }
1717        }
1718    }
1719}
1720
1721fn anyfunc_from_funcref(funcref: Option<VMFuncRef>) -> VMCallerCheckedAnyfunc {
1722    match funcref {
1723        Some(funcref) => unsafe { *funcref.0.as_ptr() },
1724        None => VMCallerCheckedAnyfunc::null(),
1725    }
1726}
1727
1728/// Eagerly builds all the `VMFuncRef`s for imported and local functions so that all
1729/// future funcref operations are just looking up this data.
1730fn build_funcrefs(
1731    module_info: &ModuleInfo,
1732    ctx: &StoreObjects,
1733    imports: &Imports,
1734    finished_functions: &BoxedSlice<LocalFunctionIndex, FunctionBodyPtr>,
1735    vmshared_signatures: &BoxedSlice<SignatureIndex, VMSignatureHash>,
1736    function_call_trampolines: &BoxedSlice<SignatureIndex, VMTrampoline>,
1737    vmctx_ptr: *mut VMContext,
1738) -> (
1739    BoxedSlice<LocalFunctionIndex, VMCallerCheckedAnyfunc>,
1740    BoxedSlice<FunctionIndex, NonNull<VMCallerCheckedAnyfunc>>,
1741) {
1742    let mut func_refs =
1743        PrimaryMap::with_capacity(module_info.functions.len() - module_info.num_imported_functions);
1744    let mut imported_func_refs = PrimaryMap::with_capacity(module_info.num_imported_functions);
1745
1746    // do imported functions
1747    for import in imports.functions.values() {
1748        imported_func_refs.push(import.handle.get(ctx).anyfunc.as_ptr());
1749    }
1750
1751    // do local functions
1752    for (local_index, func_ptr) in finished_functions.iter() {
1753        let index = module_info.func_index(local_index);
1754        let sig_index = module_info.functions[index];
1755        let type_signature_hash = vmshared_signatures[sig_index];
1756        let call_trampoline = function_call_trampolines[sig_index];
1757        let anyfunc = VMCallerCheckedAnyfunc {
1758            func_ptr: func_ptr.0,
1759            type_signature_hash,
1760            vmctx: VMFunctionContext { vmctx: vmctx_ptr },
1761            call_trampoline,
1762        };
1763        func_refs.push(anyfunc);
1764    }
1765    (
1766        func_refs.into_boxed_slice(),
1767        imported_func_refs.into_boxed_slice(),
1768    )
1769}