Skip to main content

substrate_wasmtime_runtime/
table.rs

1//! Memory management for tables.
2//!
3//! `Table` is to WebAssembly tables what `LinearMemory` is to WebAssembly linear memories.
4
5use crate::vmcontext::{VMCallerCheckedAnyfunc, VMTableDefinition};
6use crate::Trap;
7use std::cell::RefCell;
8use std::convert::{TryFrom, TryInto};
9use wasmtime_environ::wasm::TableElementType;
10use wasmtime_environ::{ir, TablePlan, TableStyle};
11
12/// A table instance.
13#[derive(Debug)]
14pub struct Table {
15    vec: RefCell<Vec<VMCallerCheckedAnyfunc>>,
16    maximum: Option<u32>,
17}
18
19impl Table {
20    /// Create a new table instance with specified minimum and maximum number of elements.
21    pub fn new(plan: &TablePlan) -> Self {
22        match plan.table.ty {
23            TableElementType::Func => (),
24            TableElementType::Val(ty) => {
25                unimplemented!("tables of types other than anyfunc ({})", ty)
26            }
27        };
28        match plan.style {
29            TableStyle::CallerChecksSignature => Self {
30                vec: RefCell::new(vec![
31                    VMCallerCheckedAnyfunc::default();
32                    usize::try_from(plan.table.minimum).unwrap()
33                ]),
34                maximum: plan.table.maximum,
35            },
36        }
37    }
38
39    /// Returns the number of allocated elements.
40    pub fn size(&self) -> u32 {
41        self.vec.borrow().len().try_into().unwrap()
42    }
43
44    /// Grow table by the specified amount of elements.
45    ///
46    /// Returns `None` if table can't be grown by the specified amount
47    /// of elements. Returns the previous size of the table if growth is
48    /// successful.
49    pub fn grow(&self, delta: u32) -> Option<u32> {
50        let size = self.size();
51        let new_len = match size.checked_add(delta) {
52            Some(len) => {
53                if let Some(max) = self.maximum {
54                    if len > max {
55                        return None;
56                    }
57                }
58                len
59            }
60            None => {
61                return None;
62            }
63        };
64        self.vec.borrow_mut().resize(
65            usize::try_from(new_len).unwrap(),
66            VMCallerCheckedAnyfunc::default(),
67        );
68        Some(size)
69    }
70
71    /// Get reference to the specified element.
72    ///
73    /// Returns `None` if the index is out of bounds.
74    pub fn get(&self, index: u32) -> Option<VMCallerCheckedAnyfunc> {
75        self.vec.borrow().get(index as usize).cloned()
76    }
77
78    /// Set reference to the specified element.
79    ///
80    /// # Panics
81    ///
82    /// Panics if `index` is out of bounds.
83    pub fn set(&self, index: u32, func: VMCallerCheckedAnyfunc) -> Result<(), ()> {
84        match self.vec.borrow_mut().get_mut(index as usize) {
85            Some(slot) => {
86                *slot = func;
87                Ok(())
88            }
89            None => Err(()),
90        }
91    }
92
93    /// Copy `len` elements from `src_table[src_index..]` into `dst_table[dst_index..]`.
94    ///
95    /// # Errors
96    ///
97    /// Returns an error if the range is out of bounds of either the source or
98    /// destination tables.
99    pub fn copy(
100        dst_table: &Self,
101        src_table: &Self,
102        dst_index: u32,
103        src_index: u32,
104        len: u32,
105    ) -> Result<(), Trap> {
106        // https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-copy
107
108        if src_index
109            .checked_add(len)
110            .map_or(true, |n| n > src_table.size())
111            || dst_index
112                .checked_add(len)
113                .map_or(true, |m| m > dst_table.size())
114        {
115            return Err(Trap::wasm(ir::TrapCode::TableOutOfBounds));
116        }
117
118        let srcs = src_index..src_index + len;
119        let dsts = dst_index..dst_index + len;
120
121        // Note on the unwraps: the bounds check above means that these will
122        // never panic.
123        //
124        // TODO(#983): investigate replacing this get/set loop with a `memcpy`.
125        if dst_index <= src_index {
126            for (s, d) in (srcs).zip(dsts) {
127                dst_table.set(d, src_table.get(s).unwrap()).unwrap();
128            }
129        } else {
130            for (s, d) in srcs.rev().zip(dsts.rev()) {
131                dst_table.set(d, src_table.get(s).unwrap()).unwrap();
132            }
133        }
134
135        Ok(())
136    }
137
138    /// Return a `VMTableDefinition` for exposing the table to compiled wasm code.
139    pub fn vmtable(&self) -> VMTableDefinition {
140        let mut vec = self.vec.borrow_mut();
141        VMTableDefinition {
142            base: vec.as_mut_ptr() as *mut u8,
143            current_elements: vec.len().try_into().unwrap(),
144        }
145    }
146}