1use crate::Trap;
9use crate::VMExternRef;
10use crate::VMFuncRef;
11use crate::store::MaybeInstanceOwned;
12use crate::vmcontext::VMTableDefinition;
13use std::cell::UnsafeCell;
14use std::convert::TryFrom;
15use std::fmt;
16use std::ptr::NonNull;
17use wasmer_types::{TableStyle, TableType, TrapCode, Type as ValType};
18
19#[derive(Debug, Clone)]
21pub enum TableElement {
22 ExternRef(Option<VMExternRef>),
24 FuncRef(Option<VMFuncRef>),
26}
27
28impl From<TableElement> for RawTableElement {
29 fn from(other: TableElement) -> Self {
30 match other {
31 TableElement::ExternRef(extern_ref) => Self { extern_ref },
32 TableElement::FuncRef(func_ref) => Self { func_ref },
33 }
34 }
35}
36
37#[repr(C)]
38#[derive(Clone, Copy)]
39pub union RawTableElement {
40 pub(crate) extern_ref: Option<VMExternRef>,
41 pub(crate) func_ref: Option<VMFuncRef>,
42}
43
44#[cfg(test)]
45#[test]
46fn table_element_size_test() {
47 use std::mem::size_of;
48 assert_eq!(size_of::<RawTableElement>(), size_of::<VMExternRef>());
49 assert_eq!(size_of::<RawTableElement>(), size_of::<VMFuncRef>());
50}
51
52impl fmt::Debug for RawTableElement {
53 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
54 f.debug_struct("RawTableElement").finish()
55 }
56}
57
58impl Default for RawTableElement {
59 fn default() -> Self {
60 Self { func_ref: None }
61 }
62}
63
64impl Default for TableElement {
65 fn default() -> Self {
66 Self::FuncRef(None)
67 }
68}
69
70#[derive(Debug)]
72pub struct VMTable {
73 vec: Vec<RawTableElement>,
74 maximum: Option<u32>,
75 table: TableType,
77 style: TableStyle,
79 vm_table_definition: MaybeInstanceOwned<VMTableDefinition>,
80}
81
82impl VMTable {
83 pub fn new(table: &TableType, style: &TableStyle) -> Result<Self, String> {
88 unsafe { Self::new_inner(table, style, None) }
89 }
90
91 pub fn get_runtime_size(&self) -> u32 {
93 self.vec.len() as u32
94 }
95
96 pub unsafe fn from_definition(
104 table: &TableType,
105 style: &TableStyle,
106 vm_table_location: NonNull<VMTableDefinition>,
107 ) -> Result<Self, String> {
108 unsafe { Self::new_inner(table, style, Some(vm_table_location)) }
109 }
110
111 unsafe fn new_inner(
113 table: &TableType,
114 style: &TableStyle,
115 vm_table_location: Option<NonNull<VMTableDefinition>>,
116 ) -> Result<Self, String> {
117 unsafe {
118 match table.ty {
119 ValType::FuncRef | ValType::ExternRef => (),
120 ty => {
121 return Err(format!(
122 "tables of types other than funcref or externref ({ty})",
123 ));
124 }
125 };
126 if let Some(max) = table.maximum
127 && max < table.minimum
128 {
129 return Err(format!(
130 "Table minimum ({}) is larger than maximum ({})!",
131 table.minimum, max
132 ));
133 }
134 let table_minimum = usize::try_from(table.minimum)
135 .map_err(|_| "Table minimum is bigger than usize".to_string())?;
136 let mut vec = vec![RawTableElement::default(); table_minimum];
137 let base = vec.as_mut_ptr();
138 match style {
139 TableStyle::CallerChecksSignature => Ok(Self {
140 vec,
141 maximum: table.maximum,
142 table: *table,
143 style: style.clone(),
144 vm_table_definition: if let Some(table_loc) = vm_table_location {
145 {
146 let mut ptr = table_loc;
147 let td = ptr.as_mut();
148 td.base = base as _;
149 td.current_elements = table_minimum as _;
150 }
151 MaybeInstanceOwned::Instance(table_loc)
152 } else {
153 MaybeInstanceOwned::Host(Box::new(UnsafeCell::new(VMTableDefinition {
154 base: base as _,
155 current_elements: table_minimum as _,
156 })))
157 },
158 }),
159 }
160 }
161 }
162
163 fn get_vm_table_definition(&self) -> NonNull<VMTableDefinition> {
165 self.vm_table_definition.as_ptr()
166 }
167
168 pub fn ty(&self) -> &TableType {
170 &self.table
171 }
172
173 pub fn style(&self) -> &TableStyle {
175 &self.style
176 }
177
178 pub fn size(&self) -> u32 {
180 unsafe {
182 let td_ptr = self.get_vm_table_definition();
183 let td = td_ptr.as_ref();
184 td.current_elements
185 }
186 }
187
188 pub fn grow(&mut self, delta: u32, init_value: TableElement) -> Option<u32> {
193 if self.table.readonly {
194 return None;
196 }
197 let size = self.size();
198 let new_len = size.checked_add(delta)?;
199 if self.maximum.is_some_and(|max| new_len > max) {
200 return None;
201 }
202 if new_len == size {
203 debug_assert_eq!(delta, 0);
204 return Some(size);
205 }
206
207 self.vec
208 .resize(usize::try_from(new_len).unwrap(), init_value.into());
209
210 unsafe {
212 let mut td_ptr = self.get_vm_table_definition();
213 let td = td_ptr.as_mut();
214 td.current_elements = new_len;
215 td.base = self.vec.as_mut_ptr() as _;
216 }
217 Some(size)
218 }
219
220 pub fn get(&self, index: u32) -> Option<TableElement> {
224 let raw_data = self.vec.get(index as usize).cloned()?;
225 Some(match self.table.ty {
226 ValType::ExternRef => TableElement::ExternRef(unsafe { raw_data.extern_ref }),
227 ValType::FuncRef => TableElement::FuncRef(unsafe { raw_data.func_ref }),
228 _ => todo!("getting invalid type from table, handle this error"),
229 })
230 }
231
232 pub fn set(&mut self, index: u32, reference: TableElement) -> Result<(), Trap> {
238 self.set_with_construction(index, reference, false)
239 }
240
241 pub(crate) fn set_with_construction(
242 &mut self,
243 index: u32,
244 reference: TableElement,
245 in_construction: bool,
246 ) -> Result<(), Trap> {
247 if !in_construction && self.table.readonly {
248 return Err(Trap::lib(TrapCode::ReadonlyTableModified));
249 }
250 match self.vec.get_mut(index as usize) {
251 Some(slot) => {
252 match (self.table.ty, reference) {
253 (ValType::ExternRef, r @ TableElement::ExternRef(_)) => {
254 *slot = r.into();
255 }
256 (ValType::FuncRef, r @ TableElement::FuncRef(_)) => {
257 *slot = r.into();
258 }
259 (ty, v) => {
262 panic!("Attempted to set a table of type {ty} with the value {v:?}")
263 }
264 };
265
266 Ok(())
267 }
268 None => Err(Trap::lib(TrapCode::TableAccessOutOfBounds)),
269 }
270 }
271
272 pub fn vmtable(&self) -> NonNull<VMTableDefinition> {
274 self.get_vm_table_definition()
275 }
276
277 pub fn copy(
284 &mut self,
285 src_table: &Self,
286 dst_index: u32,
287 src_index: u32,
288 len: u32,
289 ) -> Result<(), Trap> {
290 if self.table.readonly {
291 return Err(Trap::lib(TrapCode::ReadonlyTableModified));
292 }
293
294 if src_index
297 .checked_add(len)
298 .is_none_or(|n| n > src_table.size())
299 {
300 return Err(Trap::lib(TrapCode::TableAccessOutOfBounds));
301 }
302
303 if dst_index.checked_add(len).is_none_or(|m| m > self.size()) {
304 return Err(Trap::lib(TrapCode::TableAccessOutOfBounds));
305 }
306
307 let srcs = src_index..src_index + len;
308 let dsts = dst_index..dst_index + len;
309
310 if dst_index <= src_index {
315 for (s, d) in (srcs).zip(dsts) {
316 self.set(d, src_table.get(s).unwrap())?;
317 }
318 } else {
319 for (s, d) in srcs.rev().zip(dsts.rev()) {
320 self.set(d, src_table.get(s).unwrap())?;
321 }
322 }
323
324 Ok(())
325 }
326
327 pub fn copy_on_write(&self) -> Result<Self, String> {
329 let mut ret = Self::new(&self.table, &self.style)?;
330 ret.copy(self, 0, 0, self.size())
331 .map_err(|trap| format!("failed to copy the table - {trap:?}"))?;
332 Ok(ret)
333 }
334
335 pub fn copy_within(&mut self, dst_index: u32, src_index: u32, len: u32) -> Result<(), Trap> {
342 if self.table.readonly {
343 return Err(Trap::lib(TrapCode::ReadonlyTableModified));
344 }
345
346 if src_index.checked_add(len).is_none_or(|n| n > self.size()) {
349 return Err(Trap::lib(TrapCode::TableAccessOutOfBounds));
350 }
351
352 if dst_index.checked_add(len).is_none_or(|m| m > self.size()) {
353 return Err(Trap::lib(TrapCode::TableAccessOutOfBounds));
354 }
355
356 let srcs = src_index..src_index + len;
357 let dsts = dst_index..dst_index + len;
358
359 if dst_index <= src_index {
364 for (s, d) in (srcs).zip(dsts) {
365 self.set(d, self.get(s).unwrap())?;
366 }
367 } else {
368 for (s, d) in srcs.rev().zip(dsts.rev()) {
369 self.set(d, self.get(s).unwrap())?;
370 }
371 }
372
373 Ok(())
374 }
375}
376
377#[cfg(test)]
378mod tests {
379 use super::{TableElement, VMTable};
380 use wasmer_types::{TableStyle, TableType, Type};
381
382 #[test]
383 fn readonly_table_rejects_grow() {
384 let mut ty = TableType::new(Type::FuncRef, 0, Some(0));
385 ty.readonly = true;
386 let mut table = VMTable::new(&ty, &TableStyle::CallerChecksSignature).unwrap();
387 assert_eq!(table.grow(0, TableElement::FuncRef(None)), None);
388 }
389}