1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
use crate::{types::Val, TableType, WasmEdgeResult};
use wasmedge_sys as sys;

/// Defines a table storing the references to host functions or external objects.
#[derive(Debug, Clone)]
pub struct Table {
    pub(crate) inner: sys::Table,
    pub(crate) name: Option<String>,
    pub(crate) mod_name: Option<String>,
    pub(crate) ty: TableType,
}
impl Table {
    /// Creates a new wasm table instance with the given type.
    ///
    /// # Argument
    ///
    /// * `ty` - The type of the table instance to be created.
    ///
    /// # Error
    ///
    /// * If fail to create the table instance, then WasmEdgeError::Table(TableError::Create)(crate::error::TableError) is returned.
    pub fn new(ty: TableType) -> WasmEdgeResult<Self> {
        let inner = sys::Table::create(&ty.clone().into())?;
        Ok(Self {
            inner,
            name: None,
            mod_name: None,
            ty,
        })
    }

    /// Returns the exported name of this table instance.
    ///
    /// Notice that this field is meaningful only if this table is used as an exported instance.
    pub fn name(&self) -> Option<&str> {
        match &self.name {
            Some(name) => Some(name.as_ref()),
            None => None,
        }
    }

    /// Returns the name of the [module instance](crate::Instance) from which this table instance exports.
    ///
    /// Notice that this field is meaningful only if this table is used as an exported instance.
    pub fn mod_name(&self) -> Option<&str> {
        match &self.mod_name {
            Some(mod_name) => Some(mod_name.as_ref()),
            None => None,
        }
    }

    /// Returns a reference to the type of this table.
    pub fn ty(&self) -> &TableType {
        &self.ty
    }

    /// Returns the size of this [Table].
    pub fn size(&self) -> u32 {
        self.inner.capacity() as u32
    }

    /// Grows the size of this table by `delta`, initializing the elements with the provided init value if `init` is given. Returns the previous size of the table.
    ///
    /// # Arguments
    ///
    /// * `delta` - The number of elements to grow the table instance by.
    ///
    /// * `init` - The value to initialize the new table slots with.
    ///
    /// # Error
    ///
    /// If fail to grow the table, then an error is returned.
    pub fn grow(&mut self, delta: u32, init: Option<Val>) -> WasmEdgeResult<u32> {
        // get the current size
        let original_size = self.size();
        // grow the table by delta
        self.inner.grow(delta)?;
        // initialize the new elements
        if let Some(init) = init {
            for idx in original_size..original_size + delta {
                self.inner.set_data(init.clone().into(), idx)?;
            }
        }
        Ok(original_size)
    }

    /// Returns the table element at the `index`.
    ///
    /// # Argument
    ///
    /// * `index` - the index of the table element to get.
    ///
    /// # Error
    ///
    /// If fail to get the table element, then an error is returned.
    pub fn get(&self, index: u32) -> WasmEdgeResult<Val> {
        let value = self.inner.get_data(index)?;
        Ok(value.into())
    }

    /// Stores the `data` at the `index` of this table.
    ///
    /// # Arguments
    ///
    /// * `index` - the index of the table element to store.
    ///
    /// * `data` - the data to store at the `index` of this table.
    ///
    ///
    /// # Error
    ///
    /// If fail to store the data, then an error is returned.
    pub fn set(&mut self, index: u32, data: Val) -> WasmEdgeResult<()> {
        self.inner.set_data(data.into(), index)?;
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{
        config::{CommonConfigOptions, ConfigBuilder},
        error::HostFuncError,
        types::Val,
        CallingFrame, Executor, ImportObjectBuilder, NeverType, RefType, Statistics, Store,
        ValType, WasmValue,
    };

    #[test]
    #[allow(clippy::assertions_on_result_states)]
    fn test_table_type() {
        // create a TableType instance
        let ty = TableType::new(RefType::FuncRef, 10, Some(20));

        // check element type
        assert_eq!(ty.elem_ty(), RefType::FuncRef);
        // check minimum
        assert_eq!(ty.minimum(), 10);
        // check maximum
        assert_eq!(ty.maximum(), Some(20));
    }

    #[test]
    #[allow(clippy::assertions_on_result_states)]
    fn test_table_basic() {
        // create a table instance
        let result = Table::new(TableType::new(RefType::FuncRef, 10, Some(20)));
        assert!(result.is_ok());
        let table = result.unwrap();

        // create an import object
        let result = ImportObjectBuilder::new()
            .with_func::<(i32, i32), i32, NeverType>("add", real_add, None)
            .expect("failed to add host func")
            .with_table("table", table)
            .build::<NeverType>("extern", None);
        assert!(result.is_ok());
        let import = result.unwrap();

        // create an executor
        let result = ConfigBuilder::new(CommonConfigOptions::default()).build();
        assert!(result.is_ok());
        let config = result.unwrap();

        let result = Statistics::new();
        assert!(result.is_ok());
        let mut stat = result.unwrap();

        let result = Executor::new(Some(&config), Some(&mut stat));
        assert!(result.is_ok());
        let mut executor = result.unwrap();

        // create a store
        let result = Store::new();
        assert!(result.is_ok());
        let mut store = result.unwrap();

        // register the import module
        let result = store.register_import_module(&mut executor, &import);
        assert!(result.is_ok());

        // get the module instance by name
        let result = store.named_instance("extern");
        assert!(result.is_ok());
        let instance = result.unwrap();

        // get the exported host function
        let result = instance.func("add");
        assert!(result.is_ok());
        let host_func = result.unwrap();

        // get the exported table by name
        let result = instance.table("table");
        assert!(result.is_ok());
        let mut table = result.unwrap();

        // check table
        assert!(table.name().is_some());
        assert_eq!(table.name().unwrap(), "table");
        assert!(table.mod_name().is_some());
        assert_eq!(table.mod_name().unwrap(), "extern");
        assert_eq!(table.size(), 10);
        let ty = table.ty();
        assert_eq!(ty.elem_ty(), RefType::FuncRef);
        assert_eq!(ty.minimum(), 10);
        assert_eq!(ty.maximum(), Some(20));

        // get value from table[0]
        let result = table.get(0);
        assert!(result.is_ok());
        if let Val::FuncRef(func_ref) = result.unwrap() {
            assert!(func_ref.is_none());
        }

        // set value to table[0]
        let func_ref = host_func.as_ref();
        let result = table.set(0, Val::FuncRef(Some(func_ref)));
        assert!(result.is_ok());
        // get the value in table[0]
        let result = table.get(0);
        assert!(result.is_ok());
        if let Val::FuncRef(func_ref) = result.unwrap() {
            assert!(func_ref.is_some());
            let func_ref = func_ref.unwrap();
            // check the signature of the host function
            let func_ty = func_ref.ty();
            assert!(func_ty.args().is_some());
            assert_eq!(func_ty.args().unwrap(), [ValType::I32; 2]);
            assert!(func_ty.returns().is_some());
            assert_eq!(func_ty.returns().unwrap(), [ValType::I32]);
        }

        let result = store.named_instance("extern");
        assert!(result.is_ok());
        let instance = result.unwrap();

        let result = instance.table("table");
        assert!(result.is_ok());
        let table = result.unwrap();

        // get the value in table[0]
        let result = table.get(0);
        assert!(result.is_ok());
        if let Val::FuncRef(func_ref) = result.unwrap() {
            assert!(func_ref.is_some());
            let func_ref = func_ref.unwrap();
            let func_ty = func_ref.ty();
            assert!(func_ty.args().is_some());
            assert_eq!(func_ty.args().unwrap(), [ValType::I32; 2]);
            assert!(func_ty.returns().is_some());
            assert_eq!(func_ty.returns().unwrap(), [ValType::I32]);
        }
    }

    fn real_add(
        _frame: CallingFrame,
        inputs: Vec<WasmValue>,
        _data: *mut std::os::raw::c_void,
    ) -> std::result::Result<Vec<WasmValue>, HostFuncError> {
        if inputs.len() != 2 {
            return Err(HostFuncError::User(1));
        }

        let a = if inputs[0].ty() == ValType::I32 {
            inputs[0].to_i32()
        } else {
            return Err(HostFuncError::User(2));
        };

        let b = if inputs[1].ty() == ValType::I32 {
            inputs[1].to_i32()
        } else {
            return Err(HostFuncError::User(3));
        };

        let c = a + b;

        Ok(vec![WasmValue::from_i32(c)])
    }
}