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