Add elements to the store, returning their addresses in the store Should be called after the tables have been added
(
&mut self,
table_addrs: &[TableAddr],
func_addrs: &[FuncAddr],
global_addrs: &[Addr],
elements: &[Element],
)
| 413 | /// Add elements to the store, returning their addresses in the store |
| 414 | /// Should be called after the tables have been added |
| 415 | pub(crate) fn init_elements( |
| 416 | &mut self, |
| 417 | table_addrs: &[TableAddr], |
| 418 | func_addrs: &[FuncAddr], |
| 419 | global_addrs: &[Addr], |
| 420 | elements: &[Element], |
| 421 | ) -> Result<(Box<[Addr]>, Option<Trap>)> { |
| 422 | let elem_count = self.state.elements.len(); |
| 423 | let mut elem_addrs = Vec::with_capacity(elem_count); |
| 424 | for (i, element) in elements.iter().enumerate() { |
| 425 | let init = element |
| 426 | .items |
| 427 | .iter() |
| 428 | .map(|item| Ok(TableElement::from(self.elem_addr(item, global_addrs, func_addrs)?))) |
| 429 | .collect::<Result<Vec<_>>>()?; |
| 430 | |
| 431 | let items = match &element.kind { |
| 432 | // doesn't need to be initialized, can be initialized lazily using the `table.init` instruction |
| 433 | ElementKind::Passive => Some(init), |
| 434 | |
| 435 | // this one is not available to the runtime but needs to be initialized to declare references |
| 436 | ElementKind::Declared => None, // a. Execute the instruction elm.drop i |
| 437 | |
| 438 | // this one is active, so we need to initialize it (essentially a `table.init` instruction) |
| 439 | ElementKind::Active { offset, table } => { |
| 440 | let offset = self.eval_size_const(offset, global_addrs, func_addrs)?; |
| 441 | let table_addr = table_addrs |
| 442 | .get(*table as usize) |
| 443 | .copied() |
| 444 | .ok_or_else(|| Error::Other(format!("table {table} not found for element {i}")))?; |
| 445 | |
| 446 | let Some(table) = self.state.tables.get_mut(table_addr as usize) else { |
| 447 | return Err(Error::Other(format!("table {table} not found for element {i}"))); |
| 448 | }; |
| 449 | |
| 450 | // In wasm 2.0, it's possible to call a function that hasn't been instantiated yet, |
| 451 | // when using a partially initialized active element segments. |
| 452 | // This isn't mentioned in the spec, but the "unofficial" testsuite has a test for it: |
| 453 | // https://github.com/WebAssembly/testsuite/blob/5a1a590603d81f40ef471abba70a90a9ae5f4627/linking.wast#L264-L276 |
| 454 | // I have NO IDEA why this is allowed, but it is. |
| 455 | if let Err(trap) = table.init(offset, &init) { |
| 456 | return Ok((elem_addrs.into_boxed_slice(), Some(trap))); |
| 457 | } |
| 458 | |
| 459 | // f. Execute the instruction elm.drop i |
| 460 | None |
| 461 | } |
| 462 | }; |
| 463 | |
| 464 | self.state.elements.push(ElementInstance::new(element.kind.clone(), items)); |
| 465 | elem_addrs.push((i + elem_count) as Addr); |
| 466 | } |
| 467 | |
| 468 | // this should be optimized out by the compiler |
| 469 | Ok((elem_addrs.into_boxed_slice(), None)) |
| 470 | } |
| 471 | |
| 472 | /// Add data to the store, returning their addresses in the store |
no test coverage detected