wasmtime_environ/
module.rs

1//! Data structures for representing decoded wasm modules.
2
3use crate::{ModuleTranslation, PrimaryMap, Tunables, WASM_PAGE_SIZE};
4use cranelift_entity::{packed_option::ReservedValue, EntityRef};
5use indexmap::IndexMap;
6use serde::{Deserialize, Serialize};
7use std::collections::BTreeMap;
8use std::convert::TryFrom;
9use std::mem;
10use std::ops::Range;
11use wasmtime_types::*;
12
13/// Implementation styles for WebAssembly linear memory.
14#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
15pub enum MemoryStyle {
16    /// The actual memory can be resized and moved.
17    Dynamic {
18        /// Extra space to reserve when a memory must be moved due to growth.
19        reserve: u64,
20    },
21    /// Address space is allocated up front.
22    Static {
23        /// The number of mapped and unmapped pages.
24        bound: u64,
25    },
26}
27
28impl MemoryStyle {
29    /// Decide on an implementation style for the given `Memory`.
30    pub fn for_memory(memory: Memory, tunables: &Tunables) -> (Self, u64) {
31        // A heap with a maximum that doesn't exceed the static memory bound specified by the
32        // tunables make it static.
33        //
34        // If the module doesn't declare an explicit maximum treat it as 4GiB when not
35        // requested to use the static memory bound itself as the maximum.
36        let absolute_max_pages = if memory.memory64 {
37            crate::WASM64_MAX_PAGES
38        } else {
39            crate::WASM32_MAX_PAGES
40        };
41        let maximum = std::cmp::min(
42            memory.maximum.unwrap_or(absolute_max_pages),
43            if tunables.static_memory_bound_is_maximum {
44                std::cmp::min(tunables.static_memory_bound, absolute_max_pages)
45            } else {
46                absolute_max_pages
47            },
48        );
49
50        // Ensure the minimum is less than the maximum; the minimum might exceed the maximum
51        // when the memory is artificially bounded via `static_memory_bound_is_maximum` above
52        if memory.minimum <= maximum && maximum <= tunables.static_memory_bound {
53            return (
54                Self::Static {
55                    bound: tunables.static_memory_bound,
56                },
57                tunables.static_memory_offset_guard_size,
58            );
59        }
60
61        // Otherwise, make it dynamic.
62        (
63            Self::Dynamic {
64                reserve: tunables.dynamic_memory_growth_reserve,
65            },
66            tunables.dynamic_memory_offset_guard_size,
67        )
68    }
69}
70
71/// A WebAssembly linear memory description along with our chosen style for
72/// implementing it.
73#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
74pub struct MemoryPlan {
75    /// The WebAssembly linear memory description.
76    pub memory: Memory,
77    /// Our chosen implementation style.
78    pub style: MemoryStyle,
79    /// Chosen size of a guard page before the linear memory allocation.
80    pub pre_guard_size: u64,
81    /// Our chosen offset-guard size.
82    pub offset_guard_size: u64,
83}
84
85impl MemoryPlan {
86    /// Draw up a plan for implementing a `Memory`.
87    pub fn for_memory(memory: Memory, tunables: &Tunables) -> Self {
88        let (style, offset_guard_size) = MemoryStyle::for_memory(memory, tunables);
89        Self {
90            memory,
91            style,
92            offset_guard_size,
93            pre_guard_size: if tunables.guard_before_linear_memory {
94                offset_guard_size
95            } else {
96                0
97            },
98        }
99    }
100}
101
102/// A WebAssembly linear memory initializer.
103#[derive(Clone, Debug, Serialize, Deserialize)]
104pub struct MemoryInitializer {
105    /// The index of a linear memory to initialize.
106    pub memory_index: MemoryIndex,
107    /// Optionally, a global variable giving a base index.
108    pub base: Option<GlobalIndex>,
109    /// The offset to add to the base.
110    pub offset: u64,
111    /// The range of the data to write within the linear memory.
112    ///
113    /// This range indexes into a separately stored data section which will be
114    /// provided with the compiled module's code as well.
115    pub data: Range<u32>,
116}
117
118/// Similar to the above `MemoryInitializer` but only used when memory
119/// initializers are statically known to be valid.
120#[derive(Clone, Debug, Serialize, Deserialize)]
121pub struct StaticMemoryInitializer {
122    /// The 64-bit offset, in bytes, of where this initializer starts.
123    pub offset: u64,
124
125    /// The range of data to write at `offset`, where these indices are indexes
126    /// into the compiled wasm module's data section.
127    pub data: Range<u32>,
128}
129
130/// The type of WebAssembly linear memory initialization to use for a module.
131#[derive(Debug, Serialize, Deserialize)]
132pub enum MemoryInitialization {
133    /// Memory initialization is segmented.
134    ///
135    /// Segmented initialization can be used for any module, but it is required
136    /// if:
137    ///
138    /// * A data segment referenced an imported memory.
139    /// * A data segment uses a global base.
140    ///
141    /// Segmented initialization is performed by processing the complete set of
142    /// data segments when the module is instantiated.
143    ///
144    /// This is the default memory initialization type.
145    Segmented(Vec<MemoryInitializer>),
146
147    /// Memory initialization is statically known and involves a single `memcpy`
148    /// or otherwise simply making the defined data visible.
149    ///
150    /// To be statically initialized everything must reference a defined memory
151    /// and all data segments have a statically known in-bounds base (no
152    /// globals).
153    ///
154    /// This form of memory initialization is a more optimized version of
155    /// `Segmented` where memory can be initialized with one of a few methods:
156    ///
157    /// * First it could be initialized with a single `memcpy` of data from the
158    ///   module to the linear memory.
159    /// * Otherwise techniques like `mmap` are also possible to make this data,
160    ///   which might reside in a compiled module on disk, available immediately
161    ///   in a linear memory's address space.
162    ///
163    /// To facilitate the latter of these techniques the `try_static_init`
164    /// function below, which creates this variant, takes a host page size
165    /// argument which can page-align everything to make mmap-ing possible.
166    Static {
167        /// The initialization contents for each linear memory.
168        ///
169        /// This array has, for each module's own linear memory, the contents
170        /// necessary to initialize it. If the memory has a `None` value then no
171        /// initialization is necessary (it's zero-filled). Otherwise with
172        /// `Some` the first element of the tuple is the offset in memory to
173        /// start the initialization and the `Range` is the range within the
174        /// final data section of the compiled module of bytes to copy into the
175        /// memory.
176        ///
177        /// The offset, range base, and range end are all guaranteed to be page
178        /// aligned to the page size passed in to `try_static_init`.
179        map: PrimaryMap<MemoryIndex, Option<StaticMemoryInitializer>>,
180    },
181}
182
183impl ModuleTranslation<'_> {
184    /// Attempts to convert segmented memory initialization into static
185    /// initialization for the module that this translation represents.
186    ///
187    /// If this module's memory initialization is not compatible with paged
188    /// initialization then this won't change anything. Otherwise if it is
189    /// compatible then the `memory_initialization` field will be updated.
190    ///
191    /// Takes a `page_size` argument in order to ensure that all
192    /// initialization is page-aligned for mmap-ability, and
193    /// `max_image_size_always_allowed` to control how we decide
194    /// whether to use static init.
195    ///
196    /// We will try to avoid generating very sparse images, which are
197    /// possible if e.g. a module has an initializer at offset 0 and a
198    /// very high offset (say, 1 GiB). To avoid this, we use a dual
199    /// condition: we always allow images less than
200    /// `max_image_size_always_allowed`, and the embedder of Wasmtime
201    /// can set this if desired to ensure that static init should
202    /// always be done if the size of the module or its heaps is
203    /// otherwise bounded by the system. We also allow images with
204    /// static init data bigger than that, but only if it is "dense",
205    /// defined as having at least half (50%) of its pages with some
206    /// data.
207    ///
208    /// We could do something slightly better by building a dense part
209    /// and keeping a sparse list of outlier/leftover segments (see
210    /// issue #3820). This would also allow mostly-static init of
211    /// modules that have some dynamically-placed data segments. But,
212    /// for now, this is sufficient to allow a system that "knows what
213    /// it's doing" to always get static init.
214    pub fn try_static_init(&mut self, page_size: u64, max_image_size_always_allowed: u64) {
215        // This method only attempts to transform a `Segmented` memory init
216        // into a `Static` one, no other state.
217        if !self.module.memory_initialization.is_segmented() {
218            return;
219        }
220
221        // First a dry run of memory initialization is performed. This
222        // collects information about the extent of memory initialized for each
223        // memory as well as the size of all data segments being copied in.
224        struct Memory {
225            data_size: u64,
226            min_addr: u64,
227            max_addr: u64,
228            // The `usize` here is a pointer into `self.data` which is the list
229            // of data segments corresponding to what was found in the original
230            // wasm module.
231            segments: Vec<(usize, StaticMemoryInitializer)>,
232        }
233        let mut info = PrimaryMap::with_capacity(self.module.memory_plans.len());
234        for _ in 0..self.module.memory_plans.len() {
235            info.push(Memory {
236                data_size: 0,
237                min_addr: u64::MAX,
238                max_addr: 0,
239                segments: Vec::new(),
240            });
241        }
242        let mut idx = 0;
243        let ok = self.module.memory_initialization.init_memory(
244            &mut (),
245            InitMemory::CompileTime(&self.module),
246            |(), memory, init| {
247                // Currently `Static` only applies to locally-defined memories,
248                // so if a data segment references an imported memory then
249                // transitioning to a `Static` memory initializer is not
250                // possible.
251                if self.module.defined_memory_index(memory).is_none() {
252                    return false;
253                };
254                let info = &mut info[memory];
255                let data_len = u64::from(init.data.end - init.data.start);
256                if data_len > 0 {
257                    info.data_size += data_len;
258                    info.min_addr = info.min_addr.min(init.offset);
259                    info.max_addr = info.max_addr.max(init.offset + data_len);
260                    info.segments.push((idx, init.clone()));
261                }
262                idx += 1;
263                true
264            },
265        );
266        if !ok {
267            return;
268        }
269
270        // Validate that the memory information collected is indeed valid for
271        // static memory initialization.
272        for info in info.values().filter(|i| i.data_size > 0) {
273            let image_size = info.max_addr - info.min_addr;
274
275            // If the range of memory being initialized is less than twice the
276            // total size of the data itself then it's assumed that static
277            // initialization is ok. This means we'll at most double memory
278            // consumption during the memory image creation process, which is
279            // currently assumed to "probably be ok" but this will likely need
280            // tweaks over time.
281            if image_size < info.data_size.saturating_mul(2) {
282                continue;
283            }
284
285            // If the memory initialization image is larger than the size of all
286            // data, then we still allow memory initialization if the image will
287            // be of a relatively modest size, such as 1MB here.
288            if image_size < max_image_size_always_allowed {
289                continue;
290            }
291
292            // At this point memory initialization is concluded to be too
293            // expensive to do at compile time so it's entirely deferred to
294            // happen at runtime.
295            return;
296        }
297
298        // Here's where we've now committed to changing to static memory. The
299        // memory initialization image is built here from the page data and then
300        // it's converted to a single initializer.
301        let data = mem::replace(&mut self.data, Vec::new());
302        let mut map = PrimaryMap::with_capacity(info.len());
303        let mut module_data_size = 0u32;
304        for (memory, info) in info.iter() {
305            // Create the in-memory `image` which is the initialized contents of
306            // this linear memory.
307            let extent = if info.segments.len() > 0 {
308                (info.max_addr - info.min_addr) as usize
309            } else {
310                0
311            };
312            let mut image = Vec::with_capacity(extent);
313            for (idx, init) in info.segments.iter() {
314                let data = &data[*idx];
315                assert_eq!(data.len(), init.data.len());
316                let offset = usize::try_from(init.offset - info.min_addr).unwrap();
317                if image.len() < offset {
318                    image.resize(offset, 0u8);
319                    image.extend_from_slice(data);
320                } else {
321                    image.splice(
322                        offset..(offset + data.len()).min(image.len()),
323                        data.iter().copied(),
324                    );
325                }
326            }
327            assert_eq!(image.len(), extent);
328            assert_eq!(image.capacity(), extent);
329            let mut offset = if info.segments.len() > 0 {
330                info.min_addr
331            } else {
332                0
333            };
334
335            // Chop off trailing zeros from the image as memory is already
336            // zero-initialized. Note that `i` is the position of a nonzero
337            // entry here, so to not lose it we truncate to `i + 1`.
338            if let Some(i) = image.iter().rposition(|i| *i != 0) {
339                image.truncate(i + 1);
340            }
341
342            // Also chop off leading zeros, if any.
343            if let Some(i) = image.iter().position(|i| *i != 0) {
344                offset += i as u64;
345                image.drain(..i);
346            }
347            let mut len = u64::try_from(image.len()).unwrap();
348
349            // The goal is to enable mapping this image directly into memory, so
350            // the offset into linear memory must be a multiple of the page
351            // size. If that's not already the case then the image is padded at
352            // the front and back with extra zeros as necessary
353            if offset % page_size != 0 {
354                let zero_padding = offset % page_size;
355                self.data.push(vec![0; zero_padding as usize].into());
356                offset -= zero_padding;
357                len += zero_padding;
358            }
359            self.data.push(image.into());
360            if len % page_size != 0 {
361                let zero_padding = page_size - (len % page_size);
362                self.data.push(vec![0; zero_padding as usize].into());
363                len += zero_padding;
364            }
365
366            // Offset/length should now always be page-aligned.
367            assert!(offset % page_size == 0);
368            assert!(len % page_size == 0);
369
370            // Create the `StaticMemoryInitializer` which describes this image,
371            // only needed if the image is actually present and has a nonzero
372            // length. The `offset` has been calculates above, originally
373            // sourced from `info.min_addr`. The `data` field is the extent
374            // within the final data segment we'll emit to an ELF image, which
375            // is the concatenation of `self.data`, so here it's the size of
376            // the section-so-far plus the current segment we're appending.
377            let len = u32::try_from(len).unwrap();
378            let init = if len > 0 {
379                Some(StaticMemoryInitializer {
380                    offset,
381                    data: module_data_size..module_data_size + len,
382                })
383            } else {
384                None
385            };
386            let idx = map.push(init);
387            assert_eq!(idx, memory);
388            module_data_size += len;
389        }
390        self.data_align = Some(page_size);
391        self.module.memory_initialization = MemoryInitialization::Static { map };
392    }
393
394    /// Attempts to convert the module's table initializers to
395    /// FuncTable form where possible. This enables lazy table
396    /// initialization later by providing a one-to-one map of initial
397    /// table values, without having to parse all segments.
398    pub fn try_func_table_init(&mut self) {
399        // This should be large enough to support very large Wasm
400        // modules with huge funcref tables, but small enough to avoid
401        // OOMs or DoS on truly sparse tables.
402        const MAX_FUNC_TABLE_SIZE: u32 = 1024 * 1024;
403
404        let segments = match &self.module.table_initialization {
405            TableInitialization::Segments { segments } => segments,
406            TableInitialization::FuncTable { .. } => {
407                // Already done!
408                return;
409            }
410        };
411
412        // Build the table arrays per-table.
413        let mut tables = PrimaryMap::with_capacity(self.module.table_plans.len());
414        // Keep the "leftovers" for eager init.
415        let mut leftovers = vec![];
416
417        for segment in segments {
418            // Skip imported tables: we can't provide a preconstructed
419            // table for them, because their values depend on the
420            // imported table overlaid with whatever segments we have.
421            if self
422                .module
423                .defined_table_index(segment.table_index)
424                .is_none()
425            {
426                leftovers.push(segment.clone());
427                continue;
428            }
429
430            // If this is not a funcref table, then we can't support a
431            // pre-computed table of function indices.
432            if self.module.table_plans[segment.table_index].table.wasm_ty != WasmType::FuncRef {
433                leftovers.push(segment.clone());
434                continue;
435            }
436
437            // If the base of this segment is dynamic, then we can't
438            // include it in the statically-built array of initial
439            // contents.
440            if segment.base.is_some() {
441                leftovers.push(segment.clone());
442                continue;
443            }
444
445            // Get the end of this segment. If out-of-bounds, or too
446            // large for our dense table representation, then skip the
447            // segment.
448            let top = match segment.offset.checked_add(segment.elements.len() as u32) {
449                Some(top) => top,
450                None => {
451                    leftovers.push(segment.clone());
452                    continue;
453                }
454            };
455            let table_size = self.module.table_plans[segment.table_index].table.minimum;
456            if top > table_size || top > MAX_FUNC_TABLE_SIZE {
457                leftovers.push(segment.clone());
458                continue;
459            }
460
461            // We can now incorporate this segment into the initializers array.
462            while tables.len() <= segment.table_index.index() {
463                tables.push(vec![]);
464            }
465            let elements = &mut tables[segment.table_index];
466            if elements.is_empty() {
467                elements.resize(table_size as usize, FuncIndex::reserved_value());
468            }
469
470            let dst = &mut elements[(segment.offset as usize)..(top as usize)];
471            dst.copy_from_slice(&segment.elements[..]);
472        }
473
474        self.module.table_initialization = TableInitialization::FuncTable {
475            tables,
476            segments: leftovers,
477        };
478    }
479}
480
481impl Default for MemoryInitialization {
482    fn default() -> Self {
483        Self::Segmented(Vec::new())
484    }
485}
486
487impl MemoryInitialization {
488    /// Returns whether this initialization is of the form
489    /// `MemoryInitialization::Segmented`.
490    pub fn is_segmented(&self) -> bool {
491        match self {
492            MemoryInitialization::Segmented(_) => true,
493            _ => false,
494        }
495    }
496
497    /// Performs the memory initialization steps for this set of initializers.
498    ///
499    /// This will perform wasm initialization in compliance with the wasm spec
500    /// and how data segments are processed. This doesn't need to necessarily
501    /// only be called as part of initialization, however, as it's structured to
502    /// allow learning about memory ahead-of-time at compile time possibly.
503    ///
504    /// The various callbacks provided here are used to drive the smaller bits
505    /// of initialization, such as:
506    ///
507    /// * `get_cur_size_in_pages` - gets the current size, in wasm pages, of the
508    ///   memory specified. For compile-time purposes this would be the memory
509    ///   type's minimum size.
510    ///
511    /// * `get_global` - gets the value of the global specified. This is
512    ///   statically, via validation, a pointer to the global of the correct
513    ///   type (either u32 or u64 depending on the memory), but the value
514    ///   returned here is `u64`. A `None` value can be returned to indicate
515    ///   that the global's value isn't known yet.
516    ///
517    /// * `write` - a callback used to actually write data. This indicates that
518    ///   the specified memory must receive the specified range of data at the
519    ///   specified offset. This can internally return an false error if it
520    ///   wants to fail.
521    ///
522    /// This function will return true if all memory initializers are processed
523    /// successfully. If any initializer hits an error or, for example, a
524    /// global value is needed but `None` is returned, then false will be
525    /// returned. At compile-time this typically means that the "error" in
526    /// question needs to be deferred to runtime, and at runtime this means
527    /// that an invalid initializer has been found and a trap should be
528    /// generated.
529    pub fn init_memory<T>(
530        &self,
531        state: &mut T,
532        init: InitMemory<'_, T>,
533        mut write: impl FnMut(&mut T, MemoryIndex, &StaticMemoryInitializer) -> bool,
534    ) -> bool {
535        let initializers = match self {
536            // Fall through below to the segmented memory one-by-one
537            // initialization.
538            MemoryInitialization::Segmented(list) => list,
539
540            // If previously switched to static initialization then pass through
541            // all those parameters here to the `write` callback.
542            //
543            // Note that existence of `Static` already guarantees that all
544            // indices are in-bounds.
545            MemoryInitialization::Static { map } => {
546                for (index, init) in map {
547                    if let Some(init) = init {
548                        let result = write(state, index, init);
549                        if !result {
550                            return result;
551                        }
552                    }
553                }
554                return true;
555            }
556        };
557
558        for initializer in initializers {
559            let MemoryInitializer {
560                memory_index,
561                base,
562                offset,
563                ref data,
564            } = *initializer;
565
566            // First up determine the start/end range and verify that they're
567            // in-bounds for the initial size of the memory at `memory_index`.
568            // Note that this can bail if we don't have access to globals yet
569            // (e.g. this is a task happening before instantiation at
570            // compile-time).
571            let base = match base {
572                Some(index) => match &init {
573                    InitMemory::Runtime {
574                        get_global_as_u64, ..
575                    } => get_global_as_u64(state, index),
576                    InitMemory::CompileTime(_) => return false,
577                },
578                None => 0,
579            };
580            let start = match base.checked_add(offset) {
581                Some(start) => start,
582                None => return false,
583            };
584            let len = u64::try_from(data.len()).unwrap();
585            let end = match start.checked_add(len) {
586                Some(end) => end,
587                None => return false,
588            };
589
590            let cur_size_in_pages = match &init {
591                InitMemory::CompileTime(module) => module.memory_plans[memory_index].memory.minimum,
592                InitMemory::Runtime {
593                    memory_size_in_pages,
594                    ..
595                } => memory_size_in_pages(state, memory_index),
596            };
597
598            // Note that this `minimum` can overflow if `minimum` is
599            // `1 << 48`, the maximum number of minimum pages for 64-bit
600            // memories. If this overflow happens, though, then there's no need
601            // to check the `end` value since `end` fits in a `u64` and it is
602            // naturally less than the overflowed value.
603            //
604            // This is a bit esoteric though because it's impossible to actually
605            // create a memory of `u64::MAX + 1` bytes, so this is largely just
606            // here to avoid having the multiplication here overflow in debug
607            // mode.
608            if let Some(max) = cur_size_in_pages.checked_mul(u64::from(WASM_PAGE_SIZE)) {
609                if end > max {
610                    return false;
611                }
612            }
613
614            // The limits of the data segment have been validated at this point
615            // so the `write` callback is called with the range of data being
616            // written. Any erroneous result is propagated upwards.
617            let init = StaticMemoryInitializer {
618                offset: start,
619                data: data.clone(),
620            };
621            let result = write(state, memory_index, &init);
622            if !result {
623                return result;
624            }
625        }
626
627        return true;
628    }
629}
630
631/// Argument to [`MemoryInitialization::init_memory`] indicating the current
632/// status of the instance.
633pub enum InitMemory<'a, T> {
634    /// This evaluation of memory initializers is happening at compile time.
635    /// This means that the current state of memories is whatever their initial
636    /// state is, and additionally globals are not available if data segments
637    /// have global offsets.
638    CompileTime(&'a Module),
639
640    /// Evaluation of memory initializers is happening at runtime when the
641    /// instance is available, and callbacks are provided to learn about the
642    /// instance's state.
643    Runtime {
644        /// Returns the size, in wasm pages, of the the memory specified.
645        memory_size_in_pages: &'a dyn Fn(&mut T, MemoryIndex) -> u64,
646        /// Returns the value of the global, as a `u64`. Note that this may
647        /// involve zero-extending a 32-bit global to a 64-bit number.
648        get_global_as_u64: &'a dyn Fn(&mut T, GlobalIndex) -> u64,
649    },
650}
651
652/// Implementation styles for WebAssembly tables.
653#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
654pub enum TableStyle {
655    /// Signatures are stored in the table and checked in the caller.
656    CallerChecksSignature,
657}
658
659impl TableStyle {
660    /// Decide on an implementation style for the given `Table`.
661    pub fn for_table(_table: Table, _tunables: &Tunables) -> Self {
662        Self::CallerChecksSignature
663    }
664}
665
666/// A WebAssembly table description along with our chosen style for
667/// implementing it.
668#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
669pub struct TablePlan {
670    /// The WebAssembly table description.
671    pub table: Table,
672    /// Our chosen implementation style.
673    pub style: TableStyle,
674}
675
676impl TablePlan {
677    /// Draw up a plan for implementing a `Table`.
678    pub fn for_table(table: Table, tunables: &Tunables) -> Self {
679        let style = TableStyle::for_table(table, tunables);
680        Self { table, style }
681    }
682}
683
684/// A WebAssembly table initializer segment.
685#[derive(Clone, Debug, Serialize, Deserialize)]
686pub struct TableInitializer {
687    /// The index of a table to initialize.
688    pub table_index: TableIndex,
689    /// Optionally, a global variable giving a base index.
690    pub base: Option<GlobalIndex>,
691    /// The offset to add to the base.
692    pub offset: u32,
693    /// The values to write into the table elements.
694    pub elements: Box<[FuncIndex]>,
695}
696
697/// Table initialization data for all tables in the module.
698#[derive(Debug, Serialize, Deserialize)]
699pub enum TableInitialization {
700    /// "Segment" mode: table initializer segments, possibly with
701    /// dynamic bases, possibly applying to an imported memory.
702    ///
703    /// Every kind of table initialization is supported by the
704    /// Segments mode.
705    Segments {
706        /// The segment initializers. All apply to the table for which
707        /// this TableInitialization is specified.
708        segments: Vec<TableInitializer>,
709    },
710
711    /// "FuncTable" mode: a single array per table, with a function
712    /// index or null per slot. This is only possible to provide for a
713    /// given table when it is defined by the module itself, and can
714    /// only include data from initializer segments that have
715    /// statically-knowable bases (i.e., not dependent on global
716    /// values).
717    ///
718    /// Any segments that are not compatible with this mode are held
719    /// in the `segments` array of "leftover segments", which are
720    /// still processed eagerly.
721    ///
722    /// This mode facilitates lazy initialization of the tables. It is
723    /// thus "nice to have", but not necessary for correctness.
724    FuncTable {
725        /// For each table, an array of function indices (or
726        /// FuncIndex::reserved_value(), meaning no initialized value,
727        /// hence null by default). Array elements correspond
728        /// one-to-one to table elements; i.e., `elements[i]` is the
729        /// initial value for `table[i]`.
730        tables: PrimaryMap<TableIndex, Vec<FuncIndex>>,
731
732        /// Leftover segments that need to be processed eagerly on
733        /// instantiation. These either apply to an imported table (so
734        /// we can't pre-build a full image of the table from this
735        /// overlay) or have dynamically (at instantiation time)
736        /// determined bases.
737        segments: Vec<TableInitializer>,
738    },
739}
740
741impl Default for TableInitialization {
742    fn default() -> Self {
743        TableInitialization::Segments { segments: vec![] }
744    }
745}
746
747/// Different types that can appear in a module.
748///
749/// Note that each of these variants are intended to index further into a
750/// separate table.
751#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
752#[allow(missing_docs)]
753pub enum ModuleType {
754    Function(SignatureIndex),
755}
756
757impl ModuleType {
758    /// Asserts this is a `ModuleType::Function`, returning the underlying
759    /// `SignatureIndex`.
760    pub fn unwrap_function(&self) -> SignatureIndex {
761        match self {
762            ModuleType::Function(f) => *f,
763        }
764    }
765}
766
767/// A translated WebAssembly module, excluding the function bodies and
768/// memory initializers.
769#[derive(Default, Debug, Serialize, Deserialize)]
770pub struct Module {
771    /// The name of this wasm module, often found in the wasm file.
772    pub name: Option<String>,
773
774    /// All import records, in the order they are declared in the module.
775    pub initializers: Vec<Initializer>,
776
777    /// Exported entities.
778    pub exports: IndexMap<String, EntityIndex>,
779
780    /// The module "start" function, if present.
781    pub start_func: Option<FuncIndex>,
782
783    /// WebAssembly table initialization data, per table.
784    pub table_initialization: TableInitialization,
785
786    /// WebAssembly linear memory initializer.
787    pub memory_initialization: MemoryInitialization,
788
789    /// WebAssembly passive elements.
790    pub passive_elements: Vec<Box<[FuncIndex]>>,
791
792    /// The map from passive element index (element segment index space) to index in `passive_elements`.
793    pub passive_elements_map: BTreeMap<ElemIndex, usize>,
794
795    /// The map from passive data index (data segment index space) to index in `passive_data`.
796    pub passive_data_map: BTreeMap<DataIndex, Range<u32>>,
797
798    /// Types declared in the wasm module.
799    pub types: PrimaryMap<TypeIndex, ModuleType>,
800
801    /// Number of imported or aliased functions in the module.
802    pub num_imported_funcs: usize,
803
804    /// Number of imported or aliased tables in the module.
805    pub num_imported_tables: usize,
806
807    /// Number of imported or aliased memories in the module.
808    pub num_imported_memories: usize,
809
810    /// Number of imported or aliased globals in the module.
811    pub num_imported_globals: usize,
812
813    /// Number of functions that "escape" from this module may need to have a
814    /// `VMCallerCheckedFuncRef` constructed for them.
815    ///
816    /// This is also the number of functions in the `functions` array below with
817    /// an `anyfunc` index (and is the maximum anyfunc index).
818    pub num_escaped_funcs: usize,
819
820    /// Types of functions, imported and local.
821    pub functions: PrimaryMap<FuncIndex, FunctionType>,
822
823    /// WebAssembly tables.
824    pub table_plans: PrimaryMap<TableIndex, TablePlan>,
825
826    /// WebAssembly linear memory plans.
827    pub memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
828
829    /// WebAssembly global variables.
830    pub globals: PrimaryMap<GlobalIndex, Global>,
831}
832
833/// Initialization routines for creating an instance, encompassing imports,
834/// modules, instances, aliases, etc.
835#[derive(Debug, Serialize, Deserialize)]
836pub enum Initializer {
837    /// An imported item is required to be provided.
838    Import {
839        /// Name of this import
840        name: String,
841        /// The field name projection of this import
842        field: String,
843        /// Where this import will be placed, which also has type information
844        /// about the import.
845        index: EntityIndex,
846    },
847}
848
849impl Module {
850    /// Allocates the module data structures.
851    pub fn new() -> Self {
852        Module::default()
853    }
854
855    /// Convert a `DefinedFuncIndex` into a `FuncIndex`.
856    #[inline]
857    pub fn func_index(&self, defined_func: DefinedFuncIndex) -> FuncIndex {
858        FuncIndex::new(self.num_imported_funcs + defined_func.index())
859    }
860
861    /// Convert a `FuncIndex` into a `DefinedFuncIndex`. Returns None if the
862    /// index is an imported function.
863    #[inline]
864    pub fn defined_func_index(&self, func: FuncIndex) -> Option<DefinedFuncIndex> {
865        if func.index() < self.num_imported_funcs {
866            None
867        } else {
868            Some(DefinedFuncIndex::new(
869                func.index() - self.num_imported_funcs,
870            ))
871        }
872    }
873
874    /// Test whether the given function index is for an imported function.
875    #[inline]
876    pub fn is_imported_function(&self, index: FuncIndex) -> bool {
877        index.index() < self.num_imported_funcs
878    }
879
880    /// Convert a `DefinedTableIndex` into a `TableIndex`.
881    #[inline]
882    pub fn table_index(&self, defined_table: DefinedTableIndex) -> TableIndex {
883        TableIndex::new(self.num_imported_tables + defined_table.index())
884    }
885
886    /// Convert a `TableIndex` into a `DefinedTableIndex`. Returns None if the
887    /// index is an imported table.
888    #[inline]
889    pub fn defined_table_index(&self, table: TableIndex) -> Option<DefinedTableIndex> {
890        if table.index() < self.num_imported_tables {
891            None
892        } else {
893            Some(DefinedTableIndex::new(
894                table.index() - self.num_imported_tables,
895            ))
896        }
897    }
898
899    /// Test whether the given table index is for an imported table.
900    #[inline]
901    pub fn is_imported_table(&self, index: TableIndex) -> bool {
902        index.index() < self.num_imported_tables
903    }
904
905    /// Convert a `DefinedMemoryIndex` into a `MemoryIndex`.
906    #[inline]
907    pub fn memory_index(&self, defined_memory: DefinedMemoryIndex) -> MemoryIndex {
908        MemoryIndex::new(self.num_imported_memories + defined_memory.index())
909    }
910
911    /// Convert a `MemoryIndex` into a `DefinedMemoryIndex`. Returns None if the
912    /// index is an imported memory.
913    #[inline]
914    pub fn defined_memory_index(&self, memory: MemoryIndex) -> Option<DefinedMemoryIndex> {
915        if memory.index() < self.num_imported_memories {
916            None
917        } else {
918            Some(DefinedMemoryIndex::new(
919                memory.index() - self.num_imported_memories,
920            ))
921        }
922    }
923
924    /// Convert a `DefinedMemoryIndex` into an `OwnedMemoryIndex`. Returns None
925    /// if the index is an imported memory.
926    #[inline]
927    pub fn owned_memory_index(&self, memory: DefinedMemoryIndex) -> OwnedMemoryIndex {
928        assert!(
929            memory.index() < self.memory_plans.len(),
930            "non-shared memory must have an owned index"
931        );
932
933        // Once we know that the memory index is not greater than the number of
934        // plans, we can iterate through the plans up to the memory index and
935        // count how many are not shared (i.e., owned).
936        let owned_memory_index = self
937            .memory_plans
938            .iter()
939            .skip(self.num_imported_memories)
940            .take(memory.index())
941            .filter(|(_, mp)| !mp.memory.shared)
942            .count();
943        OwnedMemoryIndex::new(owned_memory_index)
944    }
945
946    /// Test whether the given memory index is for an imported memory.
947    #[inline]
948    pub fn is_imported_memory(&self, index: MemoryIndex) -> bool {
949        index.index() < self.num_imported_memories
950    }
951
952    /// Convert a `DefinedGlobalIndex` into a `GlobalIndex`.
953    #[inline]
954    pub fn global_index(&self, defined_global: DefinedGlobalIndex) -> GlobalIndex {
955        GlobalIndex::new(self.num_imported_globals + defined_global.index())
956    }
957
958    /// Convert a `GlobalIndex` into a `DefinedGlobalIndex`. Returns None if the
959    /// index is an imported global.
960    #[inline]
961    pub fn defined_global_index(&self, global: GlobalIndex) -> Option<DefinedGlobalIndex> {
962        if global.index() < self.num_imported_globals {
963            None
964        } else {
965            Some(DefinedGlobalIndex::new(
966                global.index() - self.num_imported_globals,
967            ))
968        }
969    }
970
971    /// Test whether the given global index is for an imported global.
972    #[inline]
973    pub fn is_imported_global(&self, index: GlobalIndex) -> bool {
974        index.index() < self.num_imported_globals
975    }
976
977    /// Returns an iterator of all the imports in this module, along with their
978    /// module name, field name, and type that's being imported.
979    pub fn imports(&self) -> impl ExactSizeIterator<Item = (&str, &str, EntityType)> {
980        self.initializers.iter().map(move |i| match i {
981            Initializer::Import { name, field, index } => {
982                (name.as_str(), field.as_str(), self.type_of(*index))
983            }
984        })
985    }
986
987    /// Returns the type of an item based on its index
988    pub fn type_of(&self, index: EntityIndex) -> EntityType {
989        match index {
990            EntityIndex::Global(i) => EntityType::Global(self.globals[i]),
991            EntityIndex::Table(i) => EntityType::Table(self.table_plans[i].table),
992            EntityIndex::Memory(i) => EntityType::Memory(self.memory_plans[i].memory),
993            EntityIndex::Function(i) => EntityType::Function(self.functions[i].signature),
994        }
995    }
996
997    /// Appends a new function to this module with the given type information,
998    /// used for functions that either don't escape or aren't certain whether
999    /// they escape yet.
1000    pub fn push_function(&mut self, signature: SignatureIndex) -> FuncIndex {
1001        self.functions.push(FunctionType {
1002            signature,
1003            anyfunc: AnyfuncIndex::reserved_value(),
1004        })
1005    }
1006
1007    /// Appends a new function to this module with the given type information.
1008    pub fn push_escaped_function(
1009        &mut self,
1010        signature: SignatureIndex,
1011        anyfunc: AnyfuncIndex,
1012    ) -> FuncIndex {
1013        self.functions.push(FunctionType { signature, anyfunc })
1014    }
1015}
1016
1017/// Type information about functions in a wasm module.
1018#[derive(Debug, Serialize, Deserialize)]
1019pub struct FunctionType {
1020    /// The type of this function, indexed into the module-wide type tables for
1021    /// a module compilation.
1022    pub signature: SignatureIndex,
1023    /// The index into the anyfunc table, if present. Note that this is
1024    /// `reserved_value()` if the function does not escape from a module.
1025    pub anyfunc: AnyfuncIndex,
1026}
1027
1028impl FunctionType {
1029    /// Returns whether this function's type is one that "escapes" the current
1030    /// module, meaning that the function is exported, used in `ref.func`, used
1031    /// in a table, etc.
1032    pub fn is_escaping(&self) -> bool {
1033        !self.anyfunc.is_reserved_value()
1034    }
1035}
1036
1037/// Index into the anyfunc table within a VMContext for a function.
1038#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
1039pub struct AnyfuncIndex(u32);
1040cranelift_entity::entity_impl!(AnyfuncIndex);