1use crate::Trap;
9use crate::VMExternRef;
10use crate::VMFuncRef;
11use crate::store::MaybeInstanceOwned;
12use crate::vmcontext::VMTableDefinition;
13use bytesize::ByteSize;
14use std::cell::UnsafeCell;
15use std::convert::TryFrom;
16use std::fmt;
17use std::ptr::NonNull;
18use wasmer_types::TableStyle;
19use wasmer_types::{TableType, TrapCode, Type as ValType};
20
21#[derive(Debug, Clone)]
23pub enum TableElement {
24 ExternRef(Option<VMExternRef>),
26 FuncRef(Option<VMFuncRef>),
28}
29
30impl From<TableElement> for RawTableElement {
31 fn from(other: TableElement) -> Self {
32 match other {
33 TableElement::ExternRef(extern_ref) => Self { extern_ref },
34 TableElement::FuncRef(func_ref) => Self { func_ref },
35 }
36 }
37}
38
39#[repr(C)]
40#[derive(Clone, Copy)]
41pub union RawTableElement {
42 pub(crate) extern_ref: Option<VMExternRef>,
43 pub(crate) func_ref: Option<VMFuncRef>,
44}
45
46#[cfg(test)]
47#[test]
48fn table_element_size_test() {
49 use std::mem::size_of;
50 assert_eq!(size_of::<RawTableElement>(), size_of::<VMExternRef>());
51 assert_eq!(size_of::<RawTableElement>(), size_of::<VMFuncRef>());
52}
53
54impl fmt::Debug for RawTableElement {
55 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
56 f.debug_struct("RawTableElement").finish()
57 }
58}
59
60impl Default for RawTableElement {
61 fn default() -> Self {
62 Self { func_ref: None }
63 }
64}
65
66impl Default for TableElement {
67 fn default() -> Self {
68 Self::FuncRef(None)
69 }
70}
71
72const TABLE_MAX_SIZE: usize = ByteSize::mib(128).as_u64() as usize;
73
74#[derive(Debug)]
76pub struct VMTable {
77 vec: Vec<RawTableElement>,
78 maximum: Option<u32>,
79 table: TableType,
81 style: TableStyle,
83 vm_table_definition: MaybeInstanceOwned<VMTableDefinition>,
84}
85
86impl VMTable {
87 pub fn new(table: &TableType, style: &TableStyle) -> Result<Self, String> {
92 unsafe { Self::new_inner(table, style, None) }
93 }
94
95 pub fn get_runtime_size(&self) -> u32 {
97 self.vec.len() as u32
98 }
99
100 pub unsafe fn from_definition(
108 table: &TableType,
109 style: &TableStyle,
110 vm_table_location: NonNull<VMTableDefinition>,
111 ) -> Result<Self, String> {
112 unsafe { Self::new_inner(table, style, Some(vm_table_location)) }
113 }
114
115 unsafe fn new_inner(
117 table: &TableType,
118 style: &TableStyle,
119 vm_table_location: Option<NonNull<VMTableDefinition>>,
120 ) -> Result<Self, String> {
121 unsafe {
122 match table.ty {
123 ValType::FuncRef | ValType::ExternRef => (),
124 ty => {
125 return Err(format!(
126 "tables of types other than funcref or externref ({ty})",
127 ));
128 }
129 };
130 if let Some(max) = table.maximum
131 && max < table.minimum
132 {
133 return Err(format!(
134 "Table minimum ({}) is larger than maximum ({})!",
135 table.minimum, max
136 ));
137 }
138 if table.minimum as usize > TABLE_MAX_SIZE {
139 return Err(format!(
140 "Table minimum ({}) is larger than maximum allowed size ({TABLE_MAX_SIZE})!",
141 table.minimum
142 ));
143 }
144 if let Some(max) = table.maximum
145 && max as usize > TABLE_MAX_SIZE
146 {
147 return Err(format!(
148 "Table maximum ({max}) is larger than maximum allowed size ({TABLE_MAX_SIZE})!",
149 ));
150 }
151 let table_minimum = usize::try_from(table.minimum)
152 .map_err(|_| "Table minimum is bigger than usize".to_string())?;
153 let mut vec = vec![RawTableElement::default(); table_minimum];
154 let base = vec.as_mut_ptr();
155 match style {
156 TableStyle::CallerChecksSignature => Ok(Self {
157 vec,
158 maximum: table.maximum,
159 table: *table,
160 style: style.clone(),
161 vm_table_definition: if let Some(table_loc) = vm_table_location {
162 {
163 let mut ptr = table_loc;
164 let td = ptr.as_mut();
165 td.base = base as _;
166 td.current_elements = table_minimum as _;
167 }
168 MaybeInstanceOwned::Instance(table_loc)
169 } else {
170 MaybeInstanceOwned::Host(Box::new(UnsafeCell::new(VMTableDefinition {
171 base: base as _,
172 current_elements: table_minimum as _,
173 })))
174 },
175 }),
176 }
177 }
178 }
179
180 fn get_vm_table_definition(&self) -> NonNull<VMTableDefinition> {
182 self.vm_table_definition.as_ptr()
183 }
184
185 pub fn ty(&self) -> &TableType {
187 &self.table
188 }
189
190 pub fn style(&self) -> &TableStyle {
192 &self.style
193 }
194
195 pub fn size(&self) -> u32 {
197 unsafe {
199 let td_ptr = self.get_vm_table_definition();
200 let td = td_ptr.as_ref();
201 td.current_elements
202 }
203 }
204
205 pub fn grow(&mut self, delta: u32, init_value: TableElement) -> Option<u32> {
210 if self.table.readonly {
211 return None;
213 }
214 let size = self.size();
215 let new_len = size.checked_add(delta)?;
216 if self.maximum.is_some_and(|max| new_len > max) {
217 return None;
218 }
219 if new_len == size {
220 debug_assert_eq!(delta, 0);
221 return Some(size);
222 }
223
224 self.vec
225 .resize(usize::try_from(new_len).unwrap(), init_value.into());
226
227 unsafe {
229 let mut td_ptr = self.get_vm_table_definition();
230 let td = td_ptr.as_mut();
231 td.current_elements = new_len;
232 td.base = self.vec.as_mut_ptr() as _;
233 }
234 Some(size)
235 }
236
237 pub fn get(&self, index: u32) -> Option<TableElement> {
241 let raw_data = self.vec.get(index as usize).cloned()?;
242 Some(match self.table.ty {
243 ValType::ExternRef => TableElement::ExternRef(unsafe { raw_data.extern_ref }),
244 ValType::FuncRef => TableElement::FuncRef(unsafe { raw_data.func_ref }),
245 _ => todo!("getting invalid type from table, handle this error"),
246 })
247 }
248
249 pub fn set(&mut self, index: u32, reference: TableElement) -> Result<(), Trap> {
255 self.set_with_construction(index, reference, false)
256 }
257
258 pub(crate) fn set_with_construction(
259 &mut self,
260 index: u32,
261 reference: TableElement,
262 in_construction: bool,
263 ) -> Result<(), Trap> {
264 if !in_construction && self.table.readonly {
265 return Err(Trap::lib(TrapCode::ReadonlyTableModified));
266 }
267 match self.vec.get_mut(index as usize) {
268 Some(slot) => {
269 match (self.table.ty, reference) {
270 (ValType::ExternRef, r @ TableElement::ExternRef(_)) => {
271 *slot = r.into();
272 }
273 (ValType::FuncRef, r @ TableElement::FuncRef(_)) => {
274 *slot = r.into();
275 }
276 (ty, v) => {
279 panic!("Attempted to set a table of type {ty} with the value {v:?}")
280 }
281 };
282
283 Ok(())
284 }
285 None => Err(Trap::lib(TrapCode::TableAccessOutOfBounds)),
286 }
287 }
288
289 pub fn vmtable(&self) -> NonNull<VMTableDefinition> {
291 self.get_vm_table_definition()
292 }
293
294 pub fn copy(
301 &mut self,
302 src_table: &Self,
303 dst_index: u32,
304 src_index: u32,
305 len: u32,
306 ) -> Result<(), Trap> {
307 if self.table.readonly {
308 return Err(Trap::lib(TrapCode::ReadonlyTableModified));
309 }
310
311 if src_index
314 .checked_add(len)
315 .is_none_or(|n| n > src_table.size())
316 {
317 return Err(Trap::lib(TrapCode::TableAccessOutOfBounds));
318 }
319
320 if dst_index.checked_add(len).is_none_or(|m| m > self.size()) {
321 return Err(Trap::lib(TrapCode::TableAccessOutOfBounds));
322 }
323
324 let srcs = src_index..src_index + len;
325 let dsts = dst_index..dst_index + len;
326
327 if dst_index <= src_index {
332 for (s, d) in (srcs).zip(dsts) {
333 self.set(d, src_table.get(s).unwrap())?;
334 }
335 } else {
336 for (s, d) in srcs.rev().zip(dsts.rev()) {
337 self.set(d, src_table.get(s).unwrap())?;
338 }
339 }
340
341 Ok(())
342 }
343
344 pub fn copy_on_write(&self) -> Result<Self, String> {
346 let mut ret = Self::new(&self.table, &self.style)?;
347 ret.copy(self, 0, 0, self.size())
348 .map_err(|trap| format!("failed to copy the table - {trap:?}"))?;
349 Ok(ret)
350 }
351
352 pub fn copy_within(&mut self, dst_index: u32, src_index: u32, len: u32) -> Result<(), Trap> {
359 if self.table.readonly {
360 return Err(Trap::lib(TrapCode::ReadonlyTableModified));
361 }
362
363 if src_index.checked_add(len).is_none_or(|n| n > self.size()) {
366 return Err(Trap::lib(TrapCode::TableAccessOutOfBounds));
367 }
368
369 if dst_index.checked_add(len).is_none_or(|m| m > self.size()) {
370 return Err(Trap::lib(TrapCode::TableAccessOutOfBounds));
371 }
372
373 let srcs = src_index..src_index + len;
374 let dsts = dst_index..dst_index + len;
375
376 if dst_index <= src_index {
381 for (s, d) in (srcs).zip(dsts) {
382 self.set(d, self.get(s).unwrap())?;
383 }
384 } else {
385 for (s, d) in srcs.rev().zip(dsts.rev()) {
386 self.set(d, self.get(s).unwrap())?;
387 }
388 }
389
390 Ok(())
391 }
392}
393
394#[cfg(test)]
395mod tests {
396 use super::{TableElement, VMTable};
397 use wasmer_types::{TableStyle, TableType, Type};
398
399 #[test]
400 fn readonly_table_rejects_grow() {
401 let mut ty = TableType::new(Type::FuncRef, 0, Some(0));
402 ty.readonly = true;
403 let mut table = VMTable::new(&ty, &TableStyle::CallerChecksSignature).unwrap();
404 assert_eq!(table.grow(0, TableElement::FuncRef(None)), None);
405 }
406}