Skip to main content

wasmedge_sys/instance/
table.rs

1//! Defines WasmEdge Table and TableType structs.
2//!
3//! A WasmEdge `Table` defines a WebAssembly table instance described by its `TableType`.
4//! `TableType` specifies the limits on the size of a table. The start of
5//! the limit range specifies the lower bound (inclusive) of the size, while
6//! the end resticts the upper bound (inclusive).
7
8use crate::{
9    ffi::{self},
10    types::{WasmEdgeLimit, WasmValue},
11    utils::check,
12    WasmEdgeResult,
13};
14
15use wasmedge_types::{
16    error::{TableError, WasmEdgeError},
17    RefType, ValType,
18};
19
20/// A WasmEdge [Table] defines a WebAssembly table instance described by its [type](crate::TableType). A table is an array-like structure and stores function references.
21///
22/// This [example](https://github.com/WasmEdge/WasmEdge/tree/master/bindings/rust/wasmedge-sys/examples/table_and_funcref.rs) shows how to use [Table] to store and retrieve function references.
23#[derive(Debug)]
24pub struct Table {
25    pub(crate) inner: InnerTable,
26}
27impl Table {
28    /// Creates a new [Table] to be associated with the given element type and the size.
29    ///
30    /// # Arguments
31    ///
32    /// - `ty` specifies the type of the new [Table].
33    ///
34    /// # Error
35    ///
36    /// * If fail to create the table instance, then WasmEdgeError::Table(TableError::Create)(crate::error::TableError) is returned.
37    pub fn create(ty: wasmedge_types::TableType) -> WasmEdgeResult<Self> {
38        let ty: TableType = ty.into();
39        let ctx = unsafe { ffi::WasmEdge_TableInstanceCreate(ty.inner.0) };
40
41        if ctx.is_null() {
42            Err(Box::new(WasmEdgeError::Table(TableError::Create)))
43        } else {
44            Ok(Table {
45                inner: InnerTable(ctx),
46            })
47        }
48    }
49
50    /// Returns the [TableType] of the [Table].
51    ///
52    /// # Error
53    ///
54    /// If fail to get type, then an error is returned.
55    pub fn ty(&self) -> WasmEdgeResult<wasmedge_types::TableType> {
56        let ty_ctx = unsafe { ffi::WasmEdge_TableInstanceGetTableType(self.inner.0) };
57        if ty_ctx.is_null() {
58            Err(Box::new(WasmEdgeError::Table(TableError::Type)))
59        } else {
60            let ty = std::mem::ManuallyDrop::new(TableType {
61                inner: InnerTableType(ty_ctx as *mut _),
62            });
63            Ok((&*ty).into())
64        }
65    }
66
67    /// Returns the element value at a specific position in the [Table].
68    ///
69    /// # Arguments
70    ///
71    /// - `idx` specifies the position in the [Table], at which the [WasmValue](crate::WasmValue) is returned.
72    ///
73    /// # Error
74    ///
75    /// If fail to get the data, then an error is returned.
76    pub fn get_data(&self, idx: u32) -> WasmEdgeResult<WasmValue> {
77        let raw_val = unsafe {
78            let mut data = ffi::WasmEdge_ValueGenI32(0);
79            check(ffi::WasmEdge_TableInstanceGetData(
80                self.inner.0,
81                &mut data as *mut _,
82                idx.into(),
83            ))?;
84            data
85        };
86        Ok(raw_val.into())
87    }
88
89    /// Sets a new element value at a specific position in the [Table].
90    ///
91    /// # Arguments
92    ///
93    /// - `data` specifies the new data.
94    ///
95    /// - `idx` specifies the position of the new data to be stored in the [Table].
96    ///
97    /// # Error
98    ///
99    /// If fail to set data, then an error is returned.
100    pub fn set_data(&mut self, data: WasmValue, idx: u32) -> WasmEdgeResult<()> {
101        unsafe {
102            check(ffi::WasmEdge_TableInstanceSetData(
103                self.inner.0,
104                data.as_raw(),
105                idx.into(),
106            ))
107        }
108    }
109
110    /// Returns the capacity of the [Table].
111    ///
112    pub fn capacity(&self) -> usize {
113        unsafe { ffi::WasmEdge_TableInstanceGetSize(self.inner.0) as usize }
114    }
115
116    /// Increases the capacity of the [Table].
117    ///
118    /// After growing, the new capacity must be in the range defined by `limit` when the table is created.
119    ///
120    /// # Argument
121    ///
122    /// - `size` specifies the size to be added to the [Table].
123    ///
124    /// # Error
125    ///
126    /// If fail to increase the size of the [Table], then an error is returned.
127    pub fn grow(&mut self, size: u32) -> WasmEdgeResult<()> {
128        unsafe { check(ffi::WasmEdge_TableInstanceGrow(self.inner.0, size.into())) }
129    }
130
131    /// # Safety
132    ///
133    /// The lifetime of the returned pointer must not exceed that of the object itself.
134    pub unsafe fn as_ptr(&self) -> *const ffi::WasmEdge_TableInstanceContext {
135        self.inner.0 as *const _
136    }
137
138    /// # Safety
139    ///
140    /// This function will take over the lifetime management of `ptr`, so do not call `ffi::WasmEdge_TableInstanceDelete` on `ptr` after this.
141    pub unsafe fn from_raw(ptr: *mut ffi::WasmEdge_TableInstanceContext) -> Self {
142        Self {
143            inner: InnerTable(ptr),
144        }
145    }
146}
147impl Drop for Table {
148    fn drop(&mut self) {
149        unsafe { ffi::WasmEdge_TableInstanceDelete(self.inner.0) };
150    }
151}
152
153#[derive(Debug)]
154pub(crate) struct InnerTable(pub(crate) *mut ffi::WasmEdge_TableInstanceContext);
155unsafe impl Send for InnerTable {}
156unsafe impl Sync for InnerTable {}
157
158/// A WasmEdge [TableType] classifies a [Table] instance over elements of element types within a size range.
159#[derive(Debug)]
160pub(crate) struct TableType {
161    pub(crate) inner: InnerTableType,
162}
163impl Drop for TableType {
164    fn drop(&mut self) {
165        unsafe {
166            ffi::WasmEdge_TableTypeDelete(self.inner.0);
167        }
168    }
169}
170impl TableType {
171    /// Creates a new [TableType] to be associated with the given limit range of the size and the reference type.
172    ///
173    /// # Arguments
174    ///
175    /// * `elem_type` - The element type.
176    ///
177    /// * `min` - The initial size of the table to be created.
178    ///
179    /// * `max` - The maximum size of the table to be created.
180    ///
181    /// # Error
182    ///
183    /// If fail to create a [TableType], then an error is returned.
184    ///
185    /// # Example
186    ///
187    /// ```ignore
188    /// let ty = TableType::create(WasmRefType::FuncRef, 10, Some(20)).expect("fail to create a TableType");
189    /// ```
190    ///
191    pub(crate) fn create(elem_ty: RefType, min: u32, max: Option<u32>) -> WasmEdgeResult<Self> {
192        let ty: ValType = elem_ty.into();
193        // WasmEdge_TableTypeCreate borrows the limit (declared `const` in
194        // the 0.17.0 C API), so the caller owns and must delete the context.
195        let limit_ctx = WasmEdgeLimit::new(min, max, false).to_context();
196        let ctx = unsafe { ffi::WasmEdge_TableTypeCreate(ty.into(), limit_ctx) };
197        unsafe { ffi::WasmEdge_LimitDelete(limit_ctx) };
198        if ctx.is_null() {
199            Err(Box::new(WasmEdgeError::TableTypeCreate))
200        } else {
201            Ok(Self {
202                inner: InnerTableType(ctx),
203            })
204        }
205    }
206
207    /// Returns the element type.
208    pub(crate) fn elem_ty(&self) -> RefType {
209        let ty = unsafe { ffi::WasmEdge_TableTypeGetRefType(self.inner.0) };
210        let ty: ValType = ty.into();
211        ty.into()
212    }
213
214    /// Returns the initial size of the [Table].
215    pub(crate) fn min(&self) -> u32 {
216        let limit = unsafe { ffi::WasmEdge_TableTypeGetLimit(self.inner.0) };
217        let limit = WasmEdgeLimit::from_context(limit);
218        limit.min()
219    }
220
221    /// Returns the maximum size of the [Table].
222    pub(crate) fn max(&self) -> Option<u32> {
223        let limit = unsafe { ffi::WasmEdge_TableTypeGetLimit(self.inner.0) };
224        let limit = WasmEdgeLimit::from_context(limit);
225        limit.max()
226    }
227}
228impl From<wasmedge_types::TableType> for TableType {
229    fn from(ty: wasmedge_types::TableType) -> Self {
230        TableType::create(ty.elem_ty(), ty.minimum(), ty.maximum()).expect(
231            "[wasmedge] Failed to convert wasmedge_types::TableType into wasmedge_sys::TableType.",
232        )
233    }
234}
235impl From<&TableType> for wasmedge_types::TableType {
236    fn from(ty: &TableType) -> Self {
237        wasmedge_types::TableType::new(ty.elem_ty(), ty.min(), ty.max())
238    }
239}
240
241#[derive(Debug)]
242pub(crate) struct InnerTableType(pub(crate) *mut ffi::WasmEdge_TableTypeContext);
243unsafe impl Send for InnerTableType {}
244unsafe impl Sync for InnerTableType {}
245
246#[cfg(test)]
247mod tests {
248    use super::*;
249    use crate::{instance::function::AsFunc, CallingFrame, Function, Instance};
250    use std::thread;
251    use wasmedge_types::{
252        error::{CoreError, CoreExecutionError},
253        RefType, ValType,
254    };
255
256    #[test]
257    #[allow(clippy::assertions_on_result_states)]
258    fn test_table_type() {
259        // create a TableType instance
260        let result = TableType::create(RefType::FuncRef, 10, Some(20));
261        assert!(result.is_ok());
262        let ty = result.unwrap();
263        assert!(!ty.inner.0.is_null());
264
265        // check element type
266        assert_eq!(ty.elem_ty(), RefType::FuncRef);
267        // check limit
268        assert_eq!(ty.min(), 10);
269        assert_eq!(ty.max(), Some(20));
270    }
271
272    #[test]
273    #[allow(clippy::assertions_on_result_states)]
274    fn test_table() {
275        // create a TableType instance
276        let ty = wasmedge_types::TableType::new(RefType::FuncRef, 10, Some(20));
277
278        // create a Table instance
279        let result = Table::create(ty);
280        assert!(result.is_ok());
281        let mut table = result.unwrap();
282
283        // check capacity
284        assert_eq!(table.capacity(), 10);
285
286        // get type
287        let result = table.ty();
288        assert!(result.is_ok());
289        let ty = result.unwrap();
290
291        // check limit and element type
292        assert_eq!(ty.minimum(), 10);
293        assert_eq!(ty.maximum(), Some(20));
294        assert_eq!(ty.elem_ty(), RefType::FuncRef);
295
296        // grow the capacity of table
297        let result = table.grow(5);
298        assert!(result.is_ok());
299        // check capacity
300        assert_eq!(table.capacity(), 15);
301    }
302
303    #[test]
304    #[allow(clippy::assertions_on_result_states)]
305    fn test_table_data() {
306        // create a FuncType
307        let func_ty = wasmedge_types::FuncType::new(vec![ValType::I32; 2], vec![ValType::I32]);
308        // create a host function
309        let mut host_data = ();
310        let result =
311            unsafe { Function::create_sync_func::<()>(&func_ty, real_add, &mut host_data, 0) };
312        assert!(result.is_ok());
313        let host_func = result.unwrap();
314
315        // create a TableType instance
316        let ty = wasmedge_types::TableType::new(RefType::FuncRef, 10, Some(20));
317
318        // create a Table instance
319        let result = Table::create(ty);
320        assert!(result.is_ok());
321        let mut table = result.unwrap();
322
323        // check capacity
324        assert_eq!(table.capacity(), 10);
325
326        // get data in the scope of the capacity
327        let result = table.get_data(9);
328        assert!(result.is_ok());
329        let value = result.unwrap();
330        assert!(value.is_null_ref());
331        assert_eq!(value.ty(), ValType::FuncRef);
332
333        // call set_data to store a function reference at the given index of the table instance
334        let result = table.set_data(WasmValue::from_func_ref((&host_func).into()), 3);
335        assert!(result.is_ok());
336        // call get_data to recover the function reference from the value at the given index of the table instance
337        let result = table.get_data(3);
338        assert!(result.is_ok());
339        let value = result.unwrap();
340        let result = value.func_ref();
341        assert!(result.is_some());
342        let func_ref = result.unwrap();
343
344        // get the function type by func_ref
345        let result = func_ref.ty();
346        assert!(result.is_some());
347        let func_ty = result.unwrap();
348        assert_eq!(func_ty.args_len(), 2);
349        assert_eq!(func_ty.args(), &[ValType::I32, ValType::I32]);
350        assert_eq!(func_ty.returns_len(), 1);
351        assert_eq!(func_ty.returns(), &[ValType::I32]);
352    }
353
354    #[test]
355    fn test_table_send() {
356        // create a TableType instance
357        let ty = wasmedge_types::TableType::new(RefType::FuncRef, 10, Some(20));
358
359        // create a Table instance
360        let result = Table::create(ty);
361        assert!(result.is_ok());
362        let table = result.unwrap();
363
364        let handle = thread::spawn(move || {
365            assert!(!table.inner.0.is_null());
366
367            // check capacity
368            assert_eq!(table.capacity(), 10);
369
370            // get type
371            let result = table.ty();
372            assert!(result.is_ok());
373            let ty = result.unwrap();
374
375            // check limit and element type
376            assert_eq!(ty.minimum(), 10);
377            assert_eq!(ty.maximum(), Some(20));
378            assert_eq!(ty.elem_ty(), RefType::FuncRef);
379        });
380
381        handle.join().unwrap();
382    }
383
384    #[test]
385    fn test_table_sync() {
386        // create a TableType instance
387        let ty = wasmedge_types::TableType::new(RefType::FuncRef, 10, Some(20));
388
389        // create a Table instance
390        let result = Table::create(ty);
391
392        assert!(result.is_ok());
393        let table = result.unwrap();
394
395        let table = &table;
396
397        std::thread::scope(move |s| {
398            let _ = s
399                .spawn(|| {
400                    let result = table.ty();
401                    assert!(result.is_ok());
402                    let ty = result.unwrap();
403                    // check limit and element type
404                    assert_eq!(ty.minimum(), 10);
405                    assert_eq!(ty.maximum(), Some(20));
406                    assert_eq!(ty.elem_ty(), RefType::FuncRef);
407                })
408                .join();
409        });
410    }
411
412    fn real_add(
413        _data: &mut (),
414        _inst: &mut Instance,
415        _frame: &mut CallingFrame,
416        input: Vec<WasmValue>,
417    ) -> Result<Vec<WasmValue>, CoreError> {
418        println!("Rust: Entering Rust function real_add");
419
420        if input.len() != 2 {
421            return Err(CoreError::Execution(CoreExecutionError::FuncSigMismatch));
422        }
423
424        let a = if input[0].ty() == ValType::I32 {
425            input[0].to_i32()
426        } else {
427            return Err(CoreError::Execution(CoreExecutionError::FuncSigMismatch));
428        };
429
430        let b = if input[1].ty() == ValType::I32 {
431            input[0].to_i32()
432        } else {
433            return Err(CoreError::Execution(CoreExecutionError::FuncSigMismatch));
434        };
435
436        let c = a + b;
437        println!("Rust: calcuating in real_add c: {c:?}");
438
439        println!("Rust: Leaving Rust function real_add");
440        Ok(vec![WasmValue::from_i32(c)])
441    }
442}