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 let size = self.size();
211 let new_len = size.checked_add(delta)?;
212 if self.maximum.is_some_and(|max| new_len > max) {
213 return None;
214 }
215 if new_len == size {
216 debug_assert_eq!(delta, 0);
217 return Some(size);
218 }
219
220 self.vec
221 .resize(usize::try_from(new_len).unwrap(), init_value.into());
222
223 unsafe {
225 let mut td_ptr = self.get_vm_table_definition();
226 let td = td_ptr.as_mut();
227 td.current_elements = new_len;
228 td.base = self.vec.as_mut_ptr() as _;
229 }
230 Some(size)
231 }
232
233 pub fn get(&self, index: u32) -> Option<TableElement> {
237 let raw_data = self.vec.get(index as usize).cloned()?;
238 Some(match self.table.ty {
239 ValType::ExternRef => TableElement::ExternRef(unsafe { raw_data.extern_ref }),
240 ValType::FuncRef => TableElement::FuncRef(unsafe { raw_data.func_ref }),
241 _ => todo!("getting invalid type from table, handle this error"),
242 })
243 }
244
245 pub fn set(&mut self, index: u32, reference: TableElement) -> Result<(), Trap> {
251 match self.vec.get_mut(index as usize) {
252 Some(slot) => {
253 match (self.table.ty, reference) {
254 (ValType::ExternRef, r @ TableElement::ExternRef(_)) => {
255 *slot = r.into();
256 }
257 (ValType::FuncRef, r @ TableElement::FuncRef(_)) => {
258 *slot = r.into();
259 }
260 (ty, v) => {
263 panic!("Attempted to set a table of type {ty} with the value {v:?}")
264 }
265 };
266
267 Ok(())
268 }
269 None => Err(Trap::lib(TrapCode::TableAccessOutOfBounds)),
270 }
271 }
272
273 pub fn vmtable(&self) -> NonNull<VMTableDefinition> {
275 self.get_vm_table_definition()
276 }
277
278 pub fn copy(
285 &mut self,
286 src_table: &Self,
287 dst_index: u32,
288 src_index: u32,
289 len: u32,
290 ) -> Result<(), Trap> {
291 if src_index
294 .checked_add(len)
295 .is_none_or(|n| n > src_table.size())
296 {
297 return Err(Trap::lib(TrapCode::TableAccessOutOfBounds));
298 }
299
300 if dst_index.checked_add(len).is_none_or(|m| m > self.size()) {
301 return Err(Trap::lib(TrapCode::TableAccessOutOfBounds));
302 }
303
304 let srcs = src_index..src_index + len;
305 let dsts = dst_index..dst_index + len;
306
307 if dst_index <= src_index {
312 for (s, d) in (srcs).zip(dsts) {
313 self.set(d, src_table.get(s).unwrap())?;
314 }
315 } else {
316 for (s, d) in srcs.rev().zip(dsts.rev()) {
317 self.set(d, src_table.get(s).unwrap())?;
318 }
319 }
320
321 Ok(())
322 }
323
324 pub fn copy_on_write(&self) -> Result<Self, String> {
326 let mut ret = Self::new(&self.table, &self.style)?;
327 ret.copy(self, 0, 0, self.size())
328 .map_err(|trap| format!("failed to copy the table - {trap:?}"))?;
329 Ok(ret)
330 }
331
332 pub fn copy_within(&mut self, dst_index: u32, src_index: u32, len: u32) -> Result<(), Trap> {
339 if src_index.checked_add(len).is_none_or(|n| n > self.size()) {
342 return Err(Trap::lib(TrapCode::TableAccessOutOfBounds));
343 }
344
345 if dst_index.checked_add(len).is_none_or(|m| m > self.size()) {
346 return Err(Trap::lib(TrapCode::TableAccessOutOfBounds));
347 }
348
349 let srcs = src_index..src_index + len;
350 let dsts = dst_index..dst_index + len;
351
352 if dst_index <= src_index {
357 for (s, d) in (srcs).zip(dsts) {
358 self.set(d, self.get(s).unwrap())?;
359 }
360 } else {
361 for (s, d) in srcs.rev().zip(dsts.rev()) {
362 self.set(d, self.get(s).unwrap())?;
363 }
364 }
365
366 Ok(())
367 }
368}