use super::InstanceEntity;
use crate::{
ElementSegment,
Error,
Extern,
ExternType,
Func,
Global,
Memory,
Module,
Table,
collections::Map,
instance::{AnyHandleAndEntity, InstanceLayout, handle::AnyHandle},
memory::DataSegment,
module::FuncIdx,
};
use alloc::{boxed::Box, vec::Vec};
use core::iter::FusedIterator;
#[derive(Debug)]
pub struct InstanceEntityBuilder {
tables: Vec<Table>,
funcs: Vec<Func>,
memories: Vec<Memory>,
globals: Vec<Global>,
start_fn: Option<FuncIdx>,
exports: Map<Box<str>, Extern>,
data_segments: Vec<DataSegment>,
elem_segments: Vec<ElementSegment>,
layout: InstanceLayout,
}
impl InstanceEntityBuilder {
pub fn new(module: &Module) -> Self {
fn vec_with_capacity_exact<T>(capacity: usize) -> Vec<T> {
let mut v = Vec::new();
v.reserve_exact(capacity);
v
}
let mut len_funcs = module.len_funcs();
let mut len_globals = module.len_globals();
let mut len_tables = module.len_tables();
let mut len_memories = module.len_memories();
for import in module.imports() {
match import.ty() {
ExternType::Func(_) => {
len_funcs += 1;
}
ExternType::Table(_) => {
len_tables += 1;
}
ExternType::Memory(_) => {
len_memories += 1;
}
ExternType::Global(_) => {
len_globals += 1;
}
}
}
let layout = *module.instance_layout();
Self {
tables: vec_with_capacity_exact(len_tables),
funcs: vec_with_capacity_exact(len_funcs),
memories: vec_with_capacity_exact(len_memories),
globals: vec_with_capacity_exact(len_globals),
start_fn: None,
exports: Map::default(),
data_segments: Vec::new(),
elem_segments: Vec::new(),
layout,
}
}
pub fn set_start(&mut self, start_fn: FuncIdx) {
match &mut self.start_fn {
Some(index) => panic!("already set start function {index:?}"),
None => {
self.start_fn = Some(start_fn);
}
}
}
pub fn get_start(&self) -> Option<FuncIdx> {
self.start_fn
}
pub fn get_memory(&self, index: u32) -> Memory {
self.memories
.get(index as usize)
.copied()
.unwrap_or_else(|| panic!("missing `Memory` at index: {index}"))
}
pub fn get_table(&self, index: u32) -> Table {
self.tables
.get(index as usize)
.copied()
.unwrap_or_else(|| panic!("missing `Table` at index: {index}"))
}
pub fn get_global(&self, index: u32) -> Global {
self.globals
.get(index as usize)
.copied()
.unwrap_or_else(|| panic!("missing `Global` at index: {index}"))
}
pub fn get_func(&self, index: u32) -> Func {
self.funcs
.get(index as usize)
.copied()
.unwrap_or_else(|| panic!("missing `Func` at index: {index}"))
}
pub fn push_memory(&mut self, memory: Memory) {
self.memories.push(memory);
}
pub fn push_table(&mut self, table: Table) {
self.tables.push(table);
}
pub fn push_global(&mut self, global: Global) {
self.globals.push(global);
}
pub fn push_func(&mut self, func: Func) {
self.funcs.push(func);
}
pub fn push_export(&mut self, name: &str, new_value: Extern) {
if let Some(old_value) = self.exports.get(name) {
panic!(
"tried to register {new_value:?} for name {name} \
but name is already used by {old_value:?}",
)
}
self.exports.insert(name.into(), new_value);
}
pub fn push_data_segment(&mut self, segment: DataSegment) {
self.data_segments.push(segment);
}
pub fn push_element_segment(&mut self, segment: ElementSegment) {
self.elem_segments.push(segment);
}
pub fn finish(self) -> Result<Box<InstanceEntity>, Error> {
let handles = self.finish_handles();
Ok(InstanceEntity::new_init(self.exports, self.layout, handles))
}
fn finish_handles(&self) -> Vec<AnyHandleAndEntity> {
fn handle_and_cache_iter<T>(
slice: &[T],
) -> impl ExactSizeIterator<Item = AnyHandleAndEntity> + FusedIterator
where
T: Clone,
AnyHandle: From<T>,
{
slice
.iter()
.cloned()
.map(AnyHandle::from)
.map(AnyHandleAndEntity::new)
}
let len_handles = self.memories.len()
+ self.globals.len()
+ self.tables.len()
+ self.funcs.len()
+ self.elem_segments.len()
+ self.data_segments.len();
let mut handles = Vec::with_capacity(len_handles);
handles.extend(handle_and_cache_iter(&self.memories));
handles.extend(handle_and_cache_iter(&self.globals));
handles.extend(handle_and_cache_iter(&self.tables));
handles.extend(handle_and_cache_iter(&self.funcs));
handles.extend(handle_and_cache_iter(&self.elem_segments));
handles.extend(handle_and_cache_iter(&self.data_segments));
handles
}
}