wasmtime_environ/module.rs
1//! Data structures for representing decoded wasm modules.
2
3use crate::prelude::*;
4use crate::{PrimaryMap, Tunables};
5use alloc::collections::BTreeMap;
6use core::ops::Range;
7use cranelift_entity::{packed_option::ReservedValue, EntityRef};
8use serde_derive::{Deserialize, Serialize};
9use wasmtime_types::*;
10
11/// Implementation styles for WebAssembly linear memory.
12#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
13pub enum MemoryStyle {
14 /// The actual memory can be resized and moved.
15 Dynamic {
16 /// Extra space to reserve when a memory must be moved due to growth.
17 reserve: u64,
18 },
19 /// Address space is allocated up front.
20 Static {
21 /// The number of bytes which are reserved for this linear memory. Only
22 /// the lower bytes which represent the actual linear memory need be
23 /// mapped, but other bytes must be guaranteed to be unmapped.
24 byte_reservation: 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 let is_static =
32 // Ideally we would compare against (an upper bound on) the target's
33 // page size, but unfortunately that is a little hard to plumb
34 // through here.
35 memory.page_size_log2 >= Memory::DEFAULT_PAGE_SIZE_LOG2
36 && match memory.maximum_byte_size() {
37 Ok(mut maximum) => {
38 if tunables.static_memory_bound_is_maximum {
39 maximum = maximum.min(tunables.static_memory_reservation);
40 }
41
42 // Ensure the minimum is less than the maximum; the minimum might exceed the maximum
43 // when the memory is artificially bounded via `static_memory_bound_is_maximum` above
44 memory.minimum_byte_size().unwrap() <= maximum
45 && maximum <= tunables.static_memory_reservation
46 }
47
48 // If the maximum size of this memory is not representable with
49 // `u64` then use the `static_memory_bound_is_maximum` to indicate
50 // whether it's a static memory or not. It should be ok to discard
51 // the linear memory's maximum size here as growth to the maximum is
52 // always fallible and never guaranteed.
53 Err(_) => tunables.static_memory_bound_is_maximum,
54 };
55
56 if is_static {
57 return (
58 Self::Static {
59 byte_reservation: tunables.static_memory_reservation,
60 },
61 tunables.static_memory_offset_guard_size,
62 );
63 }
64
65 // Otherwise, make it dynamic.
66 (
67 Self::Dynamic {
68 reserve: tunables.dynamic_memory_growth_reserve,
69 },
70 tunables.dynamic_memory_offset_guard_size,
71 )
72 }
73}
74
75/// A WebAssembly linear memory description along with our chosen style for
76/// implementing it.
77#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
78pub struct MemoryPlan {
79 /// The WebAssembly linear memory description.
80 pub memory: Memory,
81 /// Our chosen implementation style.
82 pub style: MemoryStyle,
83 /// Chosen size of a guard page before the linear memory allocation.
84 pub pre_guard_size: u64,
85 /// Our chosen offset-guard size.
86 pub offset_guard_size: u64,
87}
88
89impl MemoryPlan {
90 /// Draw up a plan for implementing a `Memory`.
91 pub fn for_memory(memory: Memory, tunables: &Tunables) -> Self {
92 let (style, offset_guard_size) = MemoryStyle::for_memory(memory, tunables);
93 Self {
94 memory,
95 style,
96 offset_guard_size,
97 pre_guard_size: if tunables.guard_before_linear_memory {
98 offset_guard_size
99 } else {
100 0
101 },
102 }
103 }
104}
105
106/// A WebAssembly linear memory initializer.
107#[derive(Clone, Debug, Serialize, Deserialize)]
108pub struct MemoryInitializer {
109 /// The index of a linear memory to initialize.
110 pub memory_index: MemoryIndex,
111 /// The base offset to start this segment at.
112 pub offset: ConstExpr,
113 /// The range of the data to write within the linear memory.
114 ///
115 /// This range indexes into a separately stored data section which will be
116 /// provided with the compiled module's code as well.
117 pub data: Range<u32>,
118}
119
120/// Similar to the above `MemoryInitializer` but only used when memory
121/// initializers are statically known to be valid.
122#[derive(Clone, Debug, Serialize, Deserialize)]
123pub struct StaticMemoryInitializer {
124 /// The 64-bit offset, in bytes, of where this initializer starts.
125 pub offset: u64,
126
127 /// The range of data to write at `offset`, where these indices are indexes
128 /// into the compiled wasm module's data section.
129 pub data: Range<u32>,
130}
131
132/// The type of WebAssembly linear memory initialization to use for a module.
133#[derive(Debug, Serialize, Deserialize)]
134pub enum MemoryInitialization {
135 /// Memory initialization is segmented.
136 ///
137 /// Segmented initialization can be used for any module, but it is required
138 /// if:
139 ///
140 /// * A data segment referenced an imported memory.
141 /// * A data segment uses a global base.
142 ///
143 /// Segmented initialization is performed by processing the complete set of
144 /// data segments when the module is instantiated.
145 ///
146 /// This is the default memory initialization type.
147 Segmented(Vec<MemoryInitializer>),
148
149 /// Memory initialization is statically known and involves a single `memcpy`
150 /// or otherwise simply making the defined data visible.
151 ///
152 /// To be statically initialized everything must reference a defined memory
153 /// and all data segments have a statically known in-bounds base (no
154 /// globals).
155 ///
156 /// This form of memory initialization is a more optimized version of
157 /// `Segmented` where memory can be initialized with one of a few methods:
158 ///
159 /// * First it could be initialized with a single `memcpy` of data from the
160 /// module to the linear memory.
161 /// * Otherwise techniques like `mmap` are also possible to make this data,
162 /// which might reside in a compiled module on disk, available immediately
163 /// in a linear memory's address space.
164 ///
165 /// To facilitate the latter of these techniques the `try_static_init`
166 /// function below, which creates this variant, takes a host page size
167 /// argument which can page-align everything to make mmap-ing possible.
168 Static {
169 /// The initialization contents for each linear memory.
170 ///
171 /// This array has, for each module's own linear memory, the contents
172 /// necessary to initialize it. If the memory has a `None` value then no
173 /// initialization is necessary (it's zero-filled). Otherwise with
174 /// `Some` the first element of the tuple is the offset in memory to
175 /// start the initialization and the `Range` is the range within the
176 /// final data section of the compiled module of bytes to copy into the
177 /// memory.
178 ///
179 /// The offset, range base, and range end are all guaranteed to be page
180 /// aligned to the page size passed in to `try_static_init`.
181 map: PrimaryMap<MemoryIndex, Option<StaticMemoryInitializer>>,
182 },
183}
184
185impl Default for MemoryInitialization {
186 fn default() -> Self {
187 Self::Segmented(Vec::new())
188 }
189}
190
191impl MemoryInitialization {
192 /// Returns whether this initialization is of the form
193 /// `MemoryInitialization::Segmented`.
194 pub fn is_segmented(&self) -> bool {
195 match self {
196 MemoryInitialization::Segmented(_) => true,
197 _ => false,
198 }
199 }
200
201 /// Performs the memory initialization steps for this set of initializers.
202 ///
203 /// This will perform wasm initialization in compliance with the wasm spec
204 /// and how data segments are processed. This doesn't need to necessarily
205 /// only be called as part of initialization, however, as it's structured to
206 /// allow learning about memory ahead-of-time at compile time possibly.
207 ///
208 /// This function will return true if all memory initializers are processed
209 /// successfully. If any initializer hits an error or, for example, a
210 /// global value is needed but `None` is returned, then false will be
211 /// returned. At compile-time this typically means that the "error" in
212 /// question needs to be deferred to runtime, and at runtime this means
213 /// that an invalid initializer has been found and a trap should be
214 /// generated.
215 pub fn init_memory(&self, state: &mut dyn InitMemory) -> bool {
216 let initializers = match self {
217 // Fall through below to the segmented memory one-by-one
218 // initialization.
219 MemoryInitialization::Segmented(list) => list,
220
221 // If previously switched to static initialization then pass through
222 // all those parameters here to the `write` callback.
223 //
224 // Note that existence of `Static` already guarantees that all
225 // indices are in-bounds.
226 MemoryInitialization::Static { map } => {
227 for (index, init) in map {
228 if let Some(init) = init {
229 let result = state.write(index, init);
230 if !result {
231 return result;
232 }
233 }
234 }
235 return true;
236 }
237 };
238
239 for initializer in initializers {
240 let &MemoryInitializer {
241 memory_index,
242 ref offset,
243 ref data,
244 } = initializer;
245
246 // First up determine the start/end range and verify that they're
247 // in-bounds for the initial size of the memory at `memory_index`.
248 // Note that this can bail if we don't have access to globals yet
249 // (e.g. this is a task happening before instantiation at
250 // compile-time).
251 let start = match state.eval_offset(memory_index, offset) {
252 Some(start) => start,
253 None => return false,
254 };
255 let len = u64::try_from(data.len()).unwrap();
256 let end = match start.checked_add(len) {
257 Some(end) => end,
258 None => return false,
259 };
260
261 match state.memory_size_in_bytes(memory_index) {
262 Ok(max) => {
263 if end > max {
264 return false;
265 }
266 }
267
268 // Note that computing the minimum can overflow if the page size
269 // is the default 64KiB and the memory's minimum size in pages
270 // is `1 << 48`, the maximum number of minimum pages for 64-bit
271 // memories. We don't return `false` to signal an error here and
272 // instead defer the error to runtime, when it will be
273 // impossible to allocate that much memory anyways.
274 Err(_) => {}
275 }
276
277 // The limits of the data segment have been validated at this point
278 // so the `write` callback is called with the range of data being
279 // written. Any erroneous result is propagated upwards.
280 let init = StaticMemoryInitializer {
281 offset: start,
282 data: data.clone(),
283 };
284 let result = state.write(memory_index, &init);
285 if !result {
286 return result;
287 }
288 }
289
290 return true;
291 }
292}
293
294/// The various callbacks provided here are used to drive the smaller bits of
295/// memory initialization.
296pub trait InitMemory {
297 /// Returns the size, in bytes, of the memory specified. For compile-time
298 /// purposes this would be the memory type's minimum size.
299 fn memory_size_in_bytes(&mut self, memory_index: MemoryIndex) -> Result<u64, SizeOverflow>;
300
301 /// Returns the value of the constant expression, as a `u64`. Note that
302 /// this may involve zero-extending a 32-bit global to a 64-bit number. May
303 /// return `None` to indicate that the expression involves a value which is
304 /// not available yet.
305 fn eval_offset(&mut self, memory_index: MemoryIndex, expr: &ConstExpr) -> Option<u64>;
306
307 /// A callback used to actually write data. This indicates that the
308 /// specified memory must receive the specified range of data at the
309 /// specified offset. This can return false on failure.
310 fn write(&mut self, memory_index: MemoryIndex, init: &StaticMemoryInitializer) -> bool;
311}
312
313/// Implementation styles for WebAssembly tables.
314#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
315pub enum TableStyle {
316 /// Signatures are stored in the table and checked in the caller.
317 CallerChecksSignature {
318 /// Whether this table is initialized lazily and requires an
319 /// initialization check on every access.
320 lazy_init: bool,
321 },
322}
323
324impl TableStyle {
325 /// Decide on an implementation style for the given `Table`.
326 pub fn for_table(_table: Table, tunables: &Tunables) -> Self {
327 Self::CallerChecksSignature {
328 lazy_init: tunables.table_lazy_init,
329 }
330 }
331}
332
333/// A WebAssembly table description along with our chosen style for
334/// implementing it.
335#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
336pub struct TablePlan {
337 /// The WebAssembly table description.
338 pub table: Table,
339 /// Our chosen implementation style.
340 pub style: TableStyle,
341}
342
343impl TablePlan {
344 /// Draw up a plan for implementing a `Table`.
345 pub fn for_table(table: Table, tunables: &Tunables) -> Self {
346 let style = TableStyle::for_table(table, tunables);
347 Self { table, style }
348 }
349}
350
351/// Table initialization data for all tables in the module.
352#[derive(Debug, Default, Serialize, Deserialize)]
353pub struct TableInitialization {
354 /// Initial values for tables defined within the module itself.
355 ///
356 /// This contains the initial values and initializers for tables defined
357 /// within a wasm, so excluding imported tables. This initializer can
358 /// represent null-initialized tables, element-initialized tables (e.g. with
359 /// the function-references proposal), or precomputed images of table
360 /// initialization. For example table initializers to a table that are all
361 /// in-bounds will get removed from `segment` and moved into
362 /// `initial_values` here.
363 pub initial_values: PrimaryMap<DefinedTableIndex, TableInitialValue>,
364
365 /// Element segments present in the initial wasm module which are executed
366 /// at instantiation time.
367 ///
368 /// These element segments are iterated over during instantiation to apply
369 /// any segments that weren't already moved into `initial_values` above.
370 pub segments: Vec<TableSegment>,
371}
372
373/// Initial value for all elements in a table.
374#[derive(Clone, Debug, Serialize, Deserialize)]
375pub enum TableInitialValue {
376 /// Initialize each table element to null, optionally setting some elements
377 /// to non-null given the precomputed image.
378 Null {
379 /// A precomputed image of table initializers for this table.
380 ///
381 /// This image is constructed during `try_func_table_init` and
382 /// null-initialized elements are represented with
383 /// `FuncIndex::reserved_value()`. Note that this image is empty by
384 /// default and may not encompass the entire span of the table in which
385 /// case the elements are initialized to null.
386 precomputed: Vec<FuncIndex>,
387 },
388 /// An arbitrary const expression.
389 Expr(ConstExpr),
390}
391
392/// A WebAssembly table initializer segment.
393#[derive(Clone, Debug, Serialize, Deserialize)]
394pub struct TableSegment {
395 /// The index of a table to initialize.
396 pub table_index: TableIndex,
397 /// The base offset to start this segment at.
398 pub offset: ConstExpr,
399 /// The values to write into the table elements.
400 pub elements: TableSegmentElements,
401}
402
403/// Elements of a table segment, either a list of functions or list of arbitrary
404/// expressions.
405#[derive(Clone, Debug, Serialize, Deserialize)]
406pub enum TableSegmentElements {
407 /// A sequential list of functions where `FuncIndex::reserved_value()`
408 /// indicates a null function.
409 Functions(Box<[FuncIndex]>),
410 /// Arbitrary expressions, aka either functions, null or a load of a global.
411 Expressions(Box<[ConstExpr]>),
412}
413
414impl TableSegmentElements {
415 /// Returns the number of elements in this segment.
416 pub fn len(&self) -> u32 {
417 match self {
418 Self::Functions(s) => s.len() as u32,
419 Self::Expressions(s) => s.len() as u32,
420 }
421 }
422}
423
424/// A translated WebAssembly module, excluding the function bodies and
425/// memory initializers.
426#[derive(Default, Debug, Serialize, Deserialize)]
427pub struct Module {
428 /// The name of this wasm module, often found in the wasm file.
429 pub name: Option<String>,
430
431 /// All import records, in the order they are declared in the module.
432 pub initializers: Vec<Initializer>,
433
434 /// Exported entities.
435 pub exports: IndexMap<String, EntityIndex>,
436
437 /// The module "start" function, if present.
438 pub start_func: Option<FuncIndex>,
439
440 /// WebAssembly table initialization data, per table.
441 pub table_initialization: TableInitialization,
442
443 /// WebAssembly linear memory initializer.
444 pub memory_initialization: MemoryInitialization,
445
446 /// WebAssembly passive elements.
447 pub passive_elements: Vec<TableSegmentElements>,
448
449 /// The map from passive element index (element segment index space) to index in `passive_elements`.
450 pub passive_elements_map: BTreeMap<ElemIndex, usize>,
451
452 /// The map from passive data index (data segment index space) to index in `passive_data`.
453 pub passive_data_map: BTreeMap<DataIndex, Range<u32>>,
454
455 /// Types declared in the wasm module.
456 pub types: PrimaryMap<TypeIndex, ModuleInternedTypeIndex>,
457
458 /// Number of imported or aliased functions in the module.
459 pub num_imported_funcs: usize,
460
461 /// Number of imported or aliased tables in the module.
462 pub num_imported_tables: usize,
463
464 /// Number of imported or aliased memories in the module.
465 pub num_imported_memories: usize,
466
467 /// Number of imported or aliased globals in the module.
468 pub num_imported_globals: usize,
469
470 /// Number of functions that "escape" from this module may need to have a
471 /// `VMFuncRef` constructed for them.
472 ///
473 /// This is also the number of functions in the `functions` array below with
474 /// an `func_ref` index (and is the maximum func_ref index).
475 pub num_escaped_funcs: usize,
476
477 /// Number of call-indirect caches.
478 pub num_call_indirect_caches: usize,
479
480 /// Types of functions, imported and local.
481 pub functions: PrimaryMap<FuncIndex, FunctionType>,
482
483 /// WebAssembly tables.
484 pub table_plans: PrimaryMap<TableIndex, TablePlan>,
485
486 /// WebAssembly linear memory plans.
487 pub memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
488
489 /// WebAssembly global variables.
490 pub globals: PrimaryMap<GlobalIndex, Global>,
491
492 /// WebAssembly global initializers for locally-defined globals.
493 pub global_initializers: PrimaryMap<DefinedGlobalIndex, ConstExpr>,
494}
495
496/// Initialization routines for creating an instance, encompassing imports,
497/// modules, instances, aliases, etc.
498#[derive(Debug, Serialize, Deserialize)]
499pub enum Initializer {
500 /// An imported item is required to be provided.
501 Import {
502 /// Name of this import
503 name: String,
504 /// The field name projection of this import
505 field: String,
506 /// Where this import will be placed, which also has type information
507 /// about the import.
508 index: EntityIndex,
509 },
510}
511
512impl Module {
513 /// Allocates the module data structures.
514 pub fn new() -> Self {
515 Module::default()
516 }
517
518 /// Convert a `DefinedFuncIndex` into a `FuncIndex`.
519 #[inline]
520 pub fn func_index(&self, defined_func: DefinedFuncIndex) -> FuncIndex {
521 FuncIndex::new(self.num_imported_funcs + defined_func.index())
522 }
523
524 /// Convert a `FuncIndex` into a `DefinedFuncIndex`. Returns None if the
525 /// index is an imported function.
526 #[inline]
527 pub fn defined_func_index(&self, func: FuncIndex) -> Option<DefinedFuncIndex> {
528 if func.index() < self.num_imported_funcs {
529 None
530 } else {
531 Some(DefinedFuncIndex::new(
532 func.index() - self.num_imported_funcs,
533 ))
534 }
535 }
536
537 /// Test whether the given function index is for an imported function.
538 #[inline]
539 pub fn is_imported_function(&self, index: FuncIndex) -> bool {
540 index.index() < self.num_imported_funcs
541 }
542
543 /// Convert a `DefinedTableIndex` into a `TableIndex`.
544 #[inline]
545 pub fn table_index(&self, defined_table: DefinedTableIndex) -> TableIndex {
546 TableIndex::new(self.num_imported_tables + defined_table.index())
547 }
548
549 /// Convert a `TableIndex` into a `DefinedTableIndex`. Returns None if the
550 /// index is an imported table.
551 #[inline]
552 pub fn defined_table_index(&self, table: TableIndex) -> Option<DefinedTableIndex> {
553 if table.index() < self.num_imported_tables {
554 None
555 } else {
556 Some(DefinedTableIndex::new(
557 table.index() - self.num_imported_tables,
558 ))
559 }
560 }
561
562 /// Test whether the given table index is for an imported table.
563 #[inline]
564 pub fn is_imported_table(&self, index: TableIndex) -> bool {
565 index.index() < self.num_imported_tables
566 }
567
568 /// Convert a `DefinedMemoryIndex` into a `MemoryIndex`.
569 #[inline]
570 pub fn memory_index(&self, defined_memory: DefinedMemoryIndex) -> MemoryIndex {
571 MemoryIndex::new(self.num_imported_memories + defined_memory.index())
572 }
573
574 /// Convert a `MemoryIndex` into a `DefinedMemoryIndex`. Returns None if the
575 /// index is an imported memory.
576 #[inline]
577 pub fn defined_memory_index(&self, memory: MemoryIndex) -> Option<DefinedMemoryIndex> {
578 if memory.index() < self.num_imported_memories {
579 None
580 } else {
581 Some(DefinedMemoryIndex::new(
582 memory.index() - self.num_imported_memories,
583 ))
584 }
585 }
586
587 /// Convert a `DefinedMemoryIndex` into an `OwnedMemoryIndex`. Returns None
588 /// if the index is an imported memory.
589 #[inline]
590 pub fn owned_memory_index(&self, memory: DefinedMemoryIndex) -> OwnedMemoryIndex {
591 assert!(
592 memory.index() < self.memory_plans.len(),
593 "non-shared memory must have an owned index"
594 );
595
596 // Once we know that the memory index is not greater than the number of
597 // plans, we can iterate through the plans up to the memory index and
598 // count how many are not shared (i.e., owned).
599 let owned_memory_index = self
600 .memory_plans
601 .iter()
602 .skip(self.num_imported_memories)
603 .take(memory.index())
604 .filter(|(_, mp)| !mp.memory.shared)
605 .count();
606 OwnedMemoryIndex::new(owned_memory_index)
607 }
608
609 /// Test whether the given memory index is for an imported memory.
610 #[inline]
611 pub fn is_imported_memory(&self, index: MemoryIndex) -> bool {
612 index.index() < self.num_imported_memories
613 }
614
615 /// Convert a `DefinedGlobalIndex` into a `GlobalIndex`.
616 #[inline]
617 pub fn global_index(&self, defined_global: DefinedGlobalIndex) -> GlobalIndex {
618 GlobalIndex::new(self.num_imported_globals + defined_global.index())
619 }
620
621 /// Convert a `GlobalIndex` into a `DefinedGlobalIndex`. Returns None if the
622 /// index is an imported global.
623 #[inline]
624 pub fn defined_global_index(&self, global: GlobalIndex) -> Option<DefinedGlobalIndex> {
625 if global.index() < self.num_imported_globals {
626 None
627 } else {
628 Some(DefinedGlobalIndex::new(
629 global.index() - self.num_imported_globals,
630 ))
631 }
632 }
633
634 /// Test whether the given global index is for an imported global.
635 #[inline]
636 pub fn is_imported_global(&self, index: GlobalIndex) -> bool {
637 index.index() < self.num_imported_globals
638 }
639
640 /// Returns an iterator of all the imports in this module, along with their
641 /// module name, field name, and type that's being imported.
642 pub fn imports(&self) -> impl ExactSizeIterator<Item = (&str, &str, EntityType)> {
643 self.initializers.iter().map(move |i| match i {
644 Initializer::Import { name, field, index } => {
645 (name.as_str(), field.as_str(), self.type_of(*index))
646 }
647 })
648 }
649
650 /// Returns the type of an item based on its index
651 pub fn type_of(&self, index: EntityIndex) -> EntityType {
652 match index {
653 EntityIndex::Global(i) => EntityType::Global(self.globals[i]),
654 EntityIndex::Table(i) => EntityType::Table(self.table_plans[i].table),
655 EntityIndex::Memory(i) => EntityType::Memory(self.memory_plans[i].memory),
656 EntityIndex::Function(i) => {
657 EntityType::Function(EngineOrModuleTypeIndex::Module(self.functions[i].signature))
658 }
659 }
660 }
661
662 /// Appends a new function to this module with the given type information,
663 /// used for functions that either don't escape or aren't certain whether
664 /// they escape yet.
665 pub fn push_function(&mut self, signature: ModuleInternedTypeIndex) -> FuncIndex {
666 self.functions.push(FunctionType {
667 signature,
668 func_ref: FuncRefIndex::reserved_value(),
669 })
670 }
671
672 /// Returns an iterator over all of the defined function indices in this
673 /// module.
674 pub fn defined_func_indices(&self) -> impl Iterator<Item = DefinedFuncIndex> {
675 (0..self.functions.len() - self.num_imported_funcs).map(|i| DefinedFuncIndex::new(i))
676 }
677}
678
679/// Type information about functions in a wasm module.
680#[derive(Debug, Serialize, Deserialize)]
681pub struct FunctionType {
682 /// The type of this function, indexed into the module-wide type tables for
683 /// a module compilation.
684 pub signature: ModuleInternedTypeIndex,
685 /// The index into the funcref table, if present. Note that this is
686 /// `reserved_value()` if the function does not escape from a module.
687 pub func_ref: FuncRefIndex,
688}
689
690impl FunctionType {
691 /// Returns whether this function's type is one that "escapes" the current
692 /// module, meaning that the function is exported, used in `ref.func`, used
693 /// in a table, etc.
694 pub fn is_escaping(&self) -> bool {
695 !self.func_ref.is_reserved_value()
696 }
697}
698
699/// Index into the funcref table within a VMContext for a function.
700#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
701pub struct FuncRefIndex(u32);
702cranelift_entity::entity_impl!(FuncRefIndex);