wasmi/table/
mod.rs

1pub use self::{
2    element::{ElementSegment, ElementSegmentEntity, ElementSegmentIdx},
3    error::TableError,
4};
5use super::{AsContext, AsContextMut, Stored};
6use crate::{
7    collections::arena::ArenaIndex,
8    core::{TrapCode, UntypedVal, ValType},
9    error::EntityGrowError,
10    store::{Fuel, FuelError, ResourceLimiterRef},
11    value::WithType,
12    IndexType,
13    Val,
14};
15use alloc::vec::Vec;
16use core::{cmp::max, iter};
17
18mod element;
19mod error;
20
21#[cfg(test)]
22mod tests;
23
24/// A raw index to a table entity.
25#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
26pub struct TableIdx(u32);
27
28impl ArenaIndex for TableIdx {
29    fn into_usize(self) -> usize {
30        self.0 as usize
31    }
32
33    fn from_usize(value: usize) -> Self {
34        let value = value.try_into().unwrap_or_else(|error| {
35            panic!("index {value} is out of bounds as table index: {error}")
36        });
37        Self(value)
38    }
39}
40
41/// A descriptor for a [`Table`] instance.
42#[derive(Debug, Copy, Clone, PartialEq, Eq)]
43pub struct TableType {
44    /// The type of values stored in the [`Table`].
45    element: ValType,
46    /// The minimum number of elements the [`Table`] must have.
47    min: u64,
48    /// The optional maximum number of elements the [`Table`] can have.
49    ///
50    /// If this is `None` then the [`Table`] is not limited in size.
51    max: Option<u64>,
52    /// The index type used by the [`Table`].
53    index_ty: IndexType,
54}
55
56impl TableType {
57    /// Creates a new [`TableType`].
58    ///
59    /// # Panics
60    ///
61    /// If `min` is greater than `max`.
62    pub fn new(element: ValType, min: u32, max: Option<u32>) -> Self {
63        Self::new_impl(element, IndexType::I32, u64::from(min), max.map(u64::from))
64    }
65
66    /// Creates a new [`TableType`] with a 64-bit index type.
67    ///
68    /// # Note
69    ///
70    /// 64-bit tables are part of the [Wasm `memory64` proposal].
71    ///
72    /// [Wasm `memory64` proposal]: https://github.com/WebAssembly/memory64
73    ///
74    /// # Panics
75    ///
76    /// If `min` is greater than `max`.
77    pub fn new64(element: ValType, min: u64, max: Option<u64>) -> Self {
78        Self::new_impl(element, IndexType::I64, min, max)
79    }
80
81    /// Convenience constructor to create a new [`TableType`].
82    pub(crate) fn new_impl(
83        element: ValType,
84        index_ty: IndexType,
85        min: u64,
86        max: Option<u64>,
87    ) -> Self {
88        let absolute_max = index_ty.max_size();
89        assert!(u128::from(min) <= absolute_max);
90        max.inspect(|&max| {
91            assert!(min <= max && u128::from(max) <= absolute_max);
92        });
93        Self {
94            element,
95            min,
96            max,
97            index_ty,
98        }
99    }
100
101    /// Returns `true` if this is a 64-bit [`TableType`].
102    ///
103    /// 64-bit memories are part of the Wasm `memory64` proposal.
104    pub fn is_64(&self) -> bool {
105        self.index_ty.is_64()
106    }
107
108    /// Returns the [`IndexType`] used by the [`TableType`].
109    pub(crate) fn index_ty(&self) -> IndexType {
110        self.index_ty
111    }
112
113    /// Returns the [`ValType`] of elements stored in the [`Table`].
114    pub fn element(&self) -> ValType {
115        self.element
116    }
117
118    /// Returns minimum number of elements the [`Table`] must have.
119    pub fn minimum(&self) -> u64 {
120        self.min
121    }
122
123    /// The optional maximum number of elements the [`Table`] can have.
124    ///
125    /// If this returns `None` then the [`Table`] is not limited in size.
126    pub fn maximum(&self) -> Option<u64> {
127        self.max
128    }
129
130    /// Returns a [`TableError`] if `ty` does not match the [`Table`] element [`ValType`].
131    fn matches_element_type(&self, ty: ValType) -> Result<(), TableError> {
132        let expected = self.element();
133        let actual = ty;
134        if actual != expected {
135            return Err(TableError::ElementTypeMismatch { expected, actual });
136        }
137        Ok(())
138    }
139
140    /// Checks if `self` is a subtype of `other`.
141    ///
142    /// # Note
143    ///
144    /// This implements the [subtyping rules] according to the WebAssembly spec.
145    ///
146    /// [import subtyping]:
147    /// https://webassembly.github.io/spec/core/valid/types.html#import-subtyping
148    ///
149    /// # Errors
150    ///
151    /// - If the `element` type of `self` does not match the `element` type of `other`.
152    /// - If the `minimum` size of `self` is less than or equal to the `minimum` size of `other`.
153    /// - If the `maximum` size of `self` is greater than the `maximum` size of `other`.
154    pub(crate) fn is_subtype_or_err(&self, other: &TableType) -> Result<(), TableError> {
155        match self.is_subtype_of(other) {
156            true => Ok(()),
157            false => Err(TableError::InvalidSubtype {
158                ty: *self,
159                other: *other,
160            }),
161        }
162    }
163
164    /// Returns `true` if the [`TableType`] is a subtype of the `other` [`TableType`].
165    ///
166    /// # Note
167    ///
168    /// This implements the [subtyping rules] according to the WebAssembly spec.
169    ///
170    /// [import subtyping]:
171    /// https://webassembly.github.io/spec/core/valid/types.html#import-subtyping
172    pub(crate) fn is_subtype_of(&self, other: &Self) -> bool {
173        if self.is_64() != other.is_64() {
174            return false;
175        }
176        if self.matches_element_type(other.element()).is_err() {
177            return false;
178        }
179        if self.minimum() < other.minimum() {
180            return false;
181        }
182        match (self.maximum(), other.maximum()) {
183            (_, None) => true,
184            (Some(max), Some(other_max)) => max <= other_max,
185            _ => false,
186        }
187    }
188}
189
190/// A Wasm table entity.
191#[derive(Debug)]
192pub struct TableEntity {
193    ty: TableType,
194    elements: Vec<UntypedVal>,
195}
196
197impl TableEntity {
198    /// Creates a new table entity with the given resizable limits.
199    ///
200    /// # Errors
201    ///
202    /// If `init` does not match the [`TableType`] element type.
203    pub fn new(
204        ty: TableType,
205        init: Val,
206        limiter: &mut ResourceLimiterRef<'_>,
207    ) -> Result<Self, TableError> {
208        ty.matches_element_type(init.ty())?;
209        let Ok(min_size) = usize::try_from(ty.minimum()) else {
210            return Err(TableError::MinimumSizeOverflow);
211        };
212        let Ok(max_size) = ty.maximum().map(usize::try_from).transpose() else {
213            return Err(TableError::MaximumSizeOverflow);
214        };
215        if let Some(limiter) = limiter.as_resource_limiter() {
216            if !limiter.table_growing(0, min_size, max_size)? {
217                return Err(TableError::ResourceLimiterDeniedAllocation);
218            }
219        }
220        let mut elements = Vec::new();
221        if elements.try_reserve(min_size).is_err() {
222            let error = TableError::OutOfSystemMemory;
223            if let Some(limiter) = limiter.as_resource_limiter() {
224                limiter.table_grow_failed(&error)
225            }
226            return Err(error);
227        };
228        elements.extend(iter::repeat_n::<UntypedVal>(init.into(), min_size));
229        Ok(Self { ty, elements })
230    }
231
232    /// Returns the resizable limits of the table.
233    pub fn ty(&self) -> TableType {
234        self.ty
235    }
236
237    /// Returns the dynamic [`TableType`] of the [`TableEntity`].
238    ///
239    /// # Note
240    ///
241    /// This respects the current size of the [`TableEntity`]
242    /// as its minimum size and is useful for import subtyping checks.
243    pub fn dynamic_ty(&self) -> TableType {
244        TableType::new_impl(
245            self.ty().element(),
246            self.ty().index_ty,
247            self.size(),
248            self.ty().maximum(),
249        )
250    }
251
252    /// Returns the current size of the [`Table`].
253    pub fn size(&self) -> u64 {
254        let len = self.elements.len();
255        let Ok(len64) = u64::try_from(len) else {
256            panic!("table.size is out of system bounds: {len}");
257        };
258        len64
259    }
260
261    /// Grows the table by the given amount of elements.
262    ///
263    /// Returns the old size of the [`Table`] upon success.
264    ///
265    /// # Note
266    ///
267    /// The newly added elements are initialized to the `init` [`Val`].
268    ///
269    /// # Errors
270    ///
271    /// - If the table is grown beyond its maximum limits.
272    /// - If `value` does not match the [`Table`] element type.
273    pub fn grow(
274        &mut self,
275        delta: u64,
276        init: Val,
277        fuel: Option<&mut Fuel>,
278        limiter: &mut ResourceLimiterRef<'_>,
279    ) -> Result<u64, EntityGrowError> {
280        self.ty()
281            .matches_element_type(init.ty())
282            .map_err(|_| EntityGrowError::InvalidGrow)?;
283        self.grow_untyped(delta, init.into(), fuel, limiter)
284    }
285
286    /// Grows the table by the given amount of elements.
287    ///
288    /// Returns the old size of the [`Table`] upon success.
289    ///
290    /// # Note
291    ///
292    /// This is an internal API that exists for efficiency purposes.
293    ///
294    /// The newly added elements are initialized to the `init` [`Val`].
295    ///
296    /// # Errors
297    ///
298    /// If the table is grown beyond its maximum limits.
299    pub fn grow_untyped(
300        &mut self,
301        delta: u64,
302        init: UntypedVal,
303        fuel: Option<&mut Fuel>,
304        limiter: &mut ResourceLimiterRef<'_>,
305    ) -> Result<u64, EntityGrowError> {
306        let Ok(delta_size) = usize::try_from(delta) else {
307            return Err(EntityGrowError::InvalidGrow);
308        };
309        let Some(desired) = self.size().checked_add(delta) else {
310            return Err(EntityGrowError::InvalidGrow);
311        };
312        // We need to divide the `max_size` (in bytes) by 8 because each table element requires 8 bytes.
313        let max_size = self.ty.index_ty.max_size() / 8;
314        if u128::from(desired) > max_size {
315            return Err(EntityGrowError::InvalidGrow);
316        }
317        let current = self.elements.len();
318        let Ok(desired) = usize::try_from(desired) else {
319            return Err(EntityGrowError::InvalidGrow);
320        };
321        let Ok(maximum) = self.ty.maximum().map(usize::try_from).transpose() else {
322            return Err(EntityGrowError::InvalidGrow);
323        };
324
325        // ResourceLimiter gets first look at the request.
326        if let Some(limiter) = limiter.as_resource_limiter() {
327            match limiter.table_growing(current, desired, maximum) {
328                Ok(true) => (),
329                Ok(false) => return Err(EntityGrowError::InvalidGrow),
330                Err(_) => return Err(EntityGrowError::TrapCode(TrapCode::GrowthOperationLimited)),
331            }
332        }
333        let notify_limiter =
334            |limiter: &mut ResourceLimiterRef<'_>| -> Result<u64, EntityGrowError> {
335                if let Some(limiter) = limiter.as_resource_limiter() {
336                    limiter.table_grow_failed(&TableError::OutOfSystemMemory);
337                }
338                Err(EntityGrowError::InvalidGrow)
339            };
340        if let Some(maximum) = maximum {
341            if desired > maximum {
342                return notify_limiter(limiter);
343            }
344        }
345        if let Some(fuel) = fuel {
346            match fuel.consume_fuel(|costs| costs.fuel_for_copies(delta)) {
347                Ok(_) | Err(FuelError::FuelMeteringDisabled) => {}
348                Err(FuelError::OutOfFuel) => return notify_limiter(limiter),
349            }
350        }
351        if self.elements.try_reserve(delta_size).is_err() {
352            return notify_limiter(limiter);
353        }
354        let size_before = self.size();
355        self.elements.resize(desired, init);
356        Ok(size_before)
357    }
358
359    /// Converts the internal [`UntypedVal`] into a [`Val`] for this [`Table`] element type.
360    fn make_typed(&self, untyped: UntypedVal) -> Val {
361        untyped.with_type(self.ty().element())
362    }
363
364    /// Returns the [`Table`] element value at `index`.
365    ///
366    /// Returns `None` if `index` is out of bounds.
367    pub fn get(&self, index: u64) -> Option<Val> {
368        self.get_untyped(index)
369            .map(|untyped| self.make_typed(untyped))
370    }
371
372    /// Returns the untyped [`Table`] element value at `index`.
373    ///
374    /// Returns `None` if `index` is out of bounds.
375    ///
376    /// # Note
377    ///
378    /// This is a more efficient version of [`Table::get`] for
379    /// internal use only.
380    pub fn get_untyped(&self, index: u64) -> Option<UntypedVal> {
381        let index = usize::try_from(index).ok()?;
382        self.elements.get(index).copied()
383    }
384
385    /// Sets the [`Val`] of this [`Table`] at `index`.
386    ///
387    /// # Errors
388    ///
389    /// - If `index` is out of bounds.
390    /// - If `value` does not match the [`Table`] element type.
391    pub fn set(&mut self, index: u64, value: Val) -> Result<(), TableError> {
392        self.ty().matches_element_type(value.ty())?;
393        self.set_untyped(index, value.into())
394    }
395
396    /// Returns the [`UntypedVal`] of the [`Table`] at `index`.
397    ///
398    /// # Errors
399    ///
400    /// If `index` is out of bounds.
401    pub fn set_untyped(&mut self, index: u64, value: UntypedVal) -> Result<(), TableError> {
402        let current = self.size();
403        let untyped = self
404            .elements
405            .get_mut(index as usize)
406            .ok_or(TableError::AccessOutOfBounds { current, index })?;
407        *untyped = value;
408        Ok(())
409    }
410
411    /// Initialize `len` elements from `src_element[src_index..]` into `self[dst_index..]`.
412    ///
413    /// # Errors
414    ///
415    /// Returns an error if the range is out of bounds of either the source or destination tables.
416    ///
417    /// # Panics
418    ///
419    /// If the [`ElementSegmentEntity`] element type does not match the [`Table`] element type.
420    /// Note: This is a panic instead of an error since it is asserted at Wasm validation time.
421    pub fn init(
422        &mut self,
423        element: &ElementSegmentEntity,
424        dst_index: u64,
425        src_index: u32,
426        len: u32,
427        fuel: Option<&mut Fuel>,
428    ) -> Result<(), TrapCode> {
429        let table_type = self.ty();
430        assert!(
431            table_type.element().is_ref(),
432            "table.init currently only works on reftypes"
433        );
434        table_type
435            .matches_element_type(element.ty())
436            .map_err(|_| TrapCode::BadSignature)?;
437        // Convert parameters to indices.
438        let Ok(dst_index) = usize::try_from(dst_index) else {
439            return Err(TrapCode::TableOutOfBounds);
440        };
441        let Ok(src_index) = usize::try_from(src_index) else {
442            return Err(TrapCode::TableOutOfBounds);
443        };
444        let Ok(len_size) = usize::try_from(len) else {
445            return Err(TrapCode::TableOutOfBounds);
446        };
447        // Perform bounds check before anything else.
448        let dst_items = self
449            .elements
450            .get_mut(dst_index..)
451            .and_then(|items| items.get_mut(..len_size))
452            .ok_or(TrapCode::TableOutOfBounds)?;
453        let src_items = element
454            .items()
455            .get(src_index..)
456            .and_then(|items| items.get(..len_size))
457            .ok_or(TrapCode::TableOutOfBounds)?;
458        if len == 0 {
459            // Bail out early if nothing needs to be initialized.
460            // The Wasm spec demands to still perform the bounds check
461            // so we cannot bail out earlie64
462            return Ok(());
463        }
464        if let Some(fuel) = fuel {
465            fuel.consume_fuel_if(|costs| costs.fuel_for_copies(u64::from(len)))?;
466        }
467        // Perform the actual table initialization.
468        dst_items.copy_from_slice(src_items);
469        Ok(())
470    }
471
472    /// Copy `len` elements from `src_table[src_index..]` into
473    /// `dst_table[dst_index..]`.
474    ///
475    /// # Errors
476    ///
477    /// Returns an error if the range is out of bounds of either the source or
478    /// destination tables.
479    pub fn copy(
480        dst_table: &mut Self,
481        dst_index: u64,
482        src_table: &Self,
483        src_index: u64,
484        len: u64,
485        fuel: Option<&mut Fuel>,
486    ) -> Result<(), TrapCode> {
487        // Turn parameters into proper slice indices.
488        let Ok(src_index) = usize::try_from(src_index) else {
489            return Err(TrapCode::TableOutOfBounds);
490        };
491        let Ok(dst_index) = usize::try_from(dst_index) else {
492            return Err(TrapCode::TableOutOfBounds);
493        };
494        let Ok(len_size) = usize::try_from(len) else {
495            return Err(TrapCode::TableOutOfBounds);
496        };
497        // Perform bounds check before anything else.
498        let dst_items = dst_table
499            .elements
500            .get_mut(dst_index..)
501            .and_then(|items| items.get_mut(..len_size))
502            .ok_or(TrapCode::TableOutOfBounds)?;
503        let src_items = src_table
504            .elements
505            .get(src_index..)
506            .and_then(|items| items.get(..len_size))
507            .ok_or(TrapCode::TableOutOfBounds)?;
508        if let Some(fuel) = fuel {
509            fuel.consume_fuel_if(|costs| costs.fuel_for_copies(len))?;
510        }
511        // Finally, copy elements in-place for the table.
512        dst_items.copy_from_slice(src_items);
513        Ok(())
514    }
515
516    /// Copy `len` elements from `self[src_index..]` into `self[dst_index..]`.
517    ///
518    /// # Errors
519    ///
520    /// Returns an error if the range is out of bounds of the table.
521    pub fn copy_within(
522        &mut self,
523        dst_index: u64,
524        src_index: u64,
525        len: u64,
526        fuel: Option<&mut Fuel>,
527    ) -> Result<(), TrapCode> {
528        // These accesses just perform the bounds checks required by the Wasm spec.
529        let max_offset = max(dst_index, src_index);
530        max_offset
531            .checked_add(len)
532            .filter(|&offset| offset <= self.size())
533            .ok_or(TrapCode::TableOutOfBounds)?;
534        // Turn parameters into proper indices.
535        let Ok(src_index) = usize::try_from(src_index) else {
536            return Err(TrapCode::TableOutOfBounds);
537        };
538        let Ok(dst_index) = usize::try_from(dst_index) else {
539            return Err(TrapCode::TableOutOfBounds);
540        };
541        let Ok(len_size) = usize::try_from(len) else {
542            return Err(TrapCode::TableOutOfBounds);
543        };
544        if let Some(fuel) = fuel {
545            fuel.consume_fuel_if(|costs| costs.fuel_for_copies(len))?;
546        }
547        // Finally, copy elements in-place for the table.
548        self.elements
549            .copy_within(src_index..src_index.wrapping_add(len_size), dst_index);
550        Ok(())
551    }
552
553    /// Fill `table[dst..(dst + len)]` with the given value.
554    ///
555    /// # Errors
556    ///
557    /// - If `val` has a type mismatch with the element type of the [`Table`].
558    /// - If the region to be filled is out of bounds for the [`Table`].
559    /// - If `val` originates from a different [`Store`] than the [`Table`].
560    ///
561    /// # Panics
562    ///
563    /// If `ctx` does not own `dst_table` or `src_table`.
564    ///
565    /// [`Store`]: [`crate::Store`]
566    pub fn fill(
567        &mut self,
568        dst: u64,
569        val: Val,
570        len: u64,
571        fuel: Option<&mut Fuel>,
572    ) -> Result<(), TrapCode> {
573        self.ty()
574            .matches_element_type(val.ty())
575            .map_err(|_| TrapCode::BadSignature)?;
576        self.fill_untyped(dst, val.into(), len, fuel)
577    }
578
579    /// Fill `table[dst..(dst + len)]` with the given value.
580    ///
581    /// # Note
582    ///
583    /// This is an API for internal use only and exists for efficiency reasons.
584    ///
585    /// # Errors
586    ///
587    /// - If the region to be filled is out of bounds for the [`Table`].
588    ///
589    /// # Panics
590    ///
591    /// If `ctx` does not own `dst_table` or `src_table`.
592    ///
593    /// [`Store`]: [`crate::Store`]
594    pub fn fill_untyped(
595        &mut self,
596        dst: u64,
597        val: UntypedVal,
598        len: u64,
599        fuel: Option<&mut Fuel>,
600    ) -> Result<(), TrapCode> {
601        let Ok(dst_index) = usize::try_from(dst) else {
602            return Err(TrapCode::TableOutOfBounds);
603        };
604        let Ok(len_size) = usize::try_from(len) else {
605            return Err(TrapCode::TableOutOfBounds);
606        };
607        let dst = self
608            .elements
609            .get_mut(dst_index..)
610            .and_then(|elements| elements.get_mut(..len_size))
611            .ok_or(TrapCode::TableOutOfBounds)?;
612        if let Some(fuel) = fuel {
613            fuel.consume_fuel_if(|costs| costs.fuel_for_copies(len))?;
614        }
615        dst.fill(val);
616        Ok(())
617    }
618}
619
620/// A Wasm table reference.
621#[derive(Debug, Copy, Clone)]
622#[repr(transparent)]
623pub struct Table(Stored<TableIdx>);
624
625impl Table {
626    /// Creates a new table reference.
627    pub(super) fn from_inner(stored: Stored<TableIdx>) -> Self {
628        Self(stored)
629    }
630
631    /// Returns the underlying stored representation.
632    pub(super) fn as_inner(&self) -> &Stored<TableIdx> {
633        &self.0
634    }
635
636    /// Creates a new table to the store.
637    ///
638    /// # Errors
639    ///
640    /// If `init` does not match the [`TableType`] element type.
641    pub fn new(mut ctx: impl AsContextMut, ty: TableType, init: Val) -> Result<Self, TableError> {
642        let (inner, mut resource_limiter) = ctx
643            .as_context_mut()
644            .store
645            .store_inner_and_resource_limiter_ref();
646        let entity = TableEntity::new(ty, init, &mut resource_limiter)?;
647        let table = inner.alloc_table(entity);
648        Ok(table)
649    }
650
651    /// Returns the type and limits of the table.
652    ///
653    /// # Panics
654    ///
655    /// Panics if `ctx` does not own this [`Table`].
656    pub fn ty(&self, ctx: impl AsContext) -> TableType {
657        ctx.as_context().store.inner.resolve_table(self).ty()
658    }
659
660    /// Returns the dynamic [`TableType`] of the [`Table`].
661    ///
662    /// # Note
663    ///
664    /// This respects the current size of the [`Table`] as
665    /// its minimum size and is useful for import subtyping checks.
666    ///
667    /// # Panics
668    ///
669    /// Panics if `ctx` does not own this [`Table`].
670    pub(crate) fn dynamic_ty(&self, ctx: impl AsContext) -> TableType {
671        ctx.as_context()
672            .store
673            .inner
674            .resolve_table(self)
675            .dynamic_ty()
676    }
677
678    /// Returns the current size of the [`Table`].
679    ///
680    /// # Panics
681    ///
682    /// If `ctx` does not own this [`Table`].
683    pub fn size(&self, ctx: impl AsContext) -> u64 {
684        ctx.as_context().store.inner.resolve_table(self).size()
685    }
686
687    /// Grows the table by the given amount of elements.
688    ///
689    /// Returns the old size of the [`Table`] upon success.
690    ///
691    /// # Note
692    ///
693    /// The newly added elements are initialized to the `init` [`Val`].
694    ///
695    /// # Errors
696    ///
697    /// - If the table is grown beyond its maximum limits.
698    /// - If `value` does not match the [`Table`] element type.
699    ///
700    /// # Panics
701    ///
702    /// Panics if `ctx` does not own this [`Table`].
703    pub fn grow(
704        &self,
705        mut ctx: impl AsContextMut,
706        delta: u64,
707        init: Val,
708    ) -> Result<u64, TableError> {
709        let (inner, mut limiter) = ctx
710            .as_context_mut()
711            .store
712            .store_inner_and_resource_limiter_ref();
713        let table = inner.resolve_table_mut(self);
714        let current = table.size();
715        let maximum = table.ty().maximum().unwrap_or(u64::MAX);
716        table
717            .grow(delta, init, None, &mut limiter)
718            .map_err(|_| TableError::GrowOutOfBounds {
719                maximum,
720                current,
721                delta,
722            })
723    }
724
725    /// Returns the [`Table`] element value at `index`.
726    ///
727    /// Returns `None` if `index` is out of bounds.
728    ///
729    /// # Panics
730    ///
731    /// Panics if `ctx` does not own this [`Table`].
732    pub fn get(&self, ctx: impl AsContext, index: u64) -> Option<Val> {
733        ctx.as_context().store.inner.resolve_table(self).get(index)
734    }
735
736    /// Sets the [`Val`] of this [`Table`] at `index`.
737    ///
738    /// # Errors
739    ///
740    /// - If `index` is out of bounds.
741    /// - If `value` does not match the [`Table`] element type.
742    ///
743    /// # Panics
744    ///
745    /// Panics if `ctx` does not own this [`Table`].
746    pub fn set(
747        &self,
748        mut ctx: impl AsContextMut,
749        index: u64,
750        value: Val,
751    ) -> Result<(), TableError> {
752        ctx.as_context_mut()
753            .store
754            .inner
755            .resolve_table_mut(self)
756            .set(index, value)
757    }
758
759    /// Returns `true` if `lhs` and `rhs` [`Table`] refer to the same entity.
760    ///
761    /// # Note
762    ///
763    /// We do not implement `Eq` and `PartialEq` and
764    /// intentionally keep this API hidden from users.
765    #[inline]
766    pub(crate) fn eq(lhs: &Self, rhs: &Self) -> bool {
767        lhs.as_inner() == rhs.as_inner()
768    }
769
770    /// Copy `len` elements from `src_table[src_index..]` into
771    /// `dst_table[dst_index..]`.
772    ///
773    /// # Errors
774    ///
775    /// Returns an error if the range is out of bounds of either the source or
776    /// destination tables.
777    ///
778    /// # Panics
779    ///
780    /// Panics if `store` does not own either `dst_table` or `src_table`.
781    pub fn copy(
782        mut store: impl AsContextMut,
783        dst_table: &Table,
784        dst_index: u64,
785        src_table: &Table,
786        src_index: u64,
787        len: u64,
788    ) -> Result<(), TableError> {
789        if Self::eq(dst_table, src_table) {
790            // The `dst_table` and `src_table` are the same table
791            // therefore we have to copy within the same table.
792            let table = store
793                .as_context_mut()
794                .store
795                .inner
796                .resolve_table_mut(dst_table);
797            table
798                .copy_within(dst_index, src_index, len, None)
799                .map_err(|_| TableError::CopyOutOfBounds)
800        } else {
801            // The `dst_table` and `src_table` are different entities
802            // therefore we have to copy from one table to the other.
803            let dst_ty = dst_table.ty(&store);
804            let src_ty = src_table.ty(&store).element();
805            dst_ty.matches_element_type(src_ty)?;
806            let (dst_table, src_table, _fuel) = store
807                .as_context_mut()
808                .store
809                .inner
810                .resolve_table_pair_and_fuel(dst_table, src_table);
811            TableEntity::copy(dst_table, dst_index, src_table, src_index, len, None)
812                .map_err(|_| TableError::CopyOutOfBounds)
813        }
814    }
815
816    /// Fill `table[dst..(dst + len)]` with the given value.
817    ///
818    /// # Errors
819    ///
820    /// - If `val` has a type mismatch with the element type of the [`Table`].
821    /// - If the region to be filled is out of bounds for the [`Table`].
822    /// - If `val` originates from a different [`Store`] than the [`Table`].
823    ///
824    /// # Panics
825    ///
826    /// If `ctx` does not own `dst_table` or `src_table`.
827    ///
828    /// [`Store`]: [`crate::Store`]
829    pub fn fill(
830        &self,
831        mut ctx: impl AsContextMut,
832        dst: u64,
833        val: Val,
834        len: u64,
835    ) -> Result<(), TrapCode> {
836        ctx.as_context_mut()
837            .store
838            .inner
839            .resolve_table_mut(self)
840            .fill(dst, val, len, None)
841    }
842}