use anyhow::{anyhow, bail, ensure, Result};
use crate::binary_grammar::{
DataSegment, ElementSegment, ExportDescription, Function, FunctionType, Global,
ImportDeclaration, ImportDescription, MemoryType, Module, TableType, Tag,
};
use crate::execution_grammar::{
DataInstance, ElementInstance, ExportInstance, ExternalImport, ExternalValue, FunctionInstance,
GlobalInstance, ImportValue, MemoryInstance, ModuleInstance, Ref, TableInstance, TagInstance,
Value,
};
#[derive(Debug, Default)]
pub struct Store<'a> {
pub functions: Vec<FunctionInstance<'a>>,
pub tables: Vec<TableInstance>,
pub memories: Vec<MemoryInstance>,
pub globals: Vec<GlobalInstance>,
pub tags: Vec<TagInstance>,
pub element_segments: Vec<ElementInstance>,
pub data_segments: Vec<DataInstance<'a>>,
}
impl<'a> Store<'a> {
pub const fn new() -> Self {
Self {
functions: vec![],
tables: vec![],
memories: vec![],
globals: vec![],
tags: vec![],
element_segments: vec![],
data_segments: vec![],
}
}
fn allocate_function(
&mut self,
f: Function,
module_instance: &ModuleInstance<'a>,
) -> Result<usize> {
let f_address = self.functions.len();
let function_type = module_instance
.types
.get(f.type_index as usize)
.ok_or_else(|| {
anyhow!(
"Function type index {} too large to index into module instance types. Len: {}",
f.type_index,
module_instance.types.len()
)
})?;
self.functions.push(FunctionInstance::Local {
function_type: function_type.clone(),
module: module_instance.clone(),
code: f,
});
Ok(f_address)
}
fn allocate_host_function(
&mut self,
h_f: Box<dyn Fn()>,
f_idx: u32,
module_instance: &ModuleInstance<'a>,
) -> Result<usize> {
let func_address = self.functions.len();
let function_type = module_instance
.types
.get(f_idx as usize)
.ok_or_else(|| anyhow!("oob: {}", f_idx))?;
self.functions.push(FunctionInstance::Host {
function_type: function_type.clone(),
code: h_f,
});
Ok(func_address)
}
fn allocate_table(&mut self, table_type: TableType, initial_ref: Ref) -> usize {
let n = table_type.limit.min;
let table_address = self.tables.len();
self.tables.push(TableInstance {
table_type,
elem: vec![initial_ref; n as usize],
});
table_address
}
fn allocate_memory(&mut self, memory_type: MemoryType) -> usize {
let memory_address = self.memories.len();
let n = memory_type.0.min;
self.memories.push(MemoryInstance {
memory_type,
data: vec![0u8; n as usize],
});
memory_address
}
fn allocate_global(&mut self, global: Global, initializer_value: Value) -> usize {
let global_address = self.globals.len();
self.globals.push(GlobalInstance {
global_type: global.global_type,
value: initializer_value,
});
global_address
}
fn allocate_element_segment(
&mut self,
element_segment: ElementSegment,
element_segment_ref: Vec<Ref>,
) -> usize {
let element_segment_address = self.element_segments.len();
self.element_segments.push(ElementInstance {
ref_type: element_segment.ref_type,
elem: element_segment_ref,
});
element_segment_address
}
fn allocate_data_instance(&mut self, data_segment: DataSegment<'a>) -> usize {
let data_address = self.data_segments.len();
self.data_segments.push(DataInstance {
data: data_segment.bytes,
});
data_address
}
fn allocate_tag(&mut self, tag_type: FunctionType) -> usize {
let addr = self.tags.len();
self.tags.push(TagInstance { tag_type });
addr
}
pub fn allocate_module(
&mut self,
module: Module<'a>,
mut extern_addrs: Vec<ExternalValue>,
initial_global_values: Vec<Value>,
initial_table_refs: Vec<Ref>,
element_segment_refs: Vec<Vec<Ref>>,
) -> Result<ModuleInstance<'a>> {
let mut module_instance = ModuleInstance::new(module.types);
for addr in extern_addrs {
match addr {
ExternalValue::Function { addr } => module_instance.function_addrs.push(addr),
ExternalValue::Table { addr } => module_instance.table_addrs.push(addr),
ExternalValue::Memory { addr } => module_instance.mem_addrs.push(addr),
ExternalValue::Global { addr } => module_instance.global_addrs.push(addr),
ExternalValue::Tag { addr } => module_instance.tag_addrs.push(addr),
}
}
let function_addresses = (0..module.functions.len()).map(|i| self.functions.len() + i);
let function_types = &module_instance.types;
for tag in &module.tags {
let tag_type = function_types
.get(tag.type_index as usize)
.ok_or_else(|| anyhow!("oob"))?;
let addr = self.allocate_tag(tag_type.clone());
module_instance.tag_addrs.push(addr);
}
module_instance.global_addrs.extend(
module
.globals
.into_iter()
.zip(initial_global_values)
.map(|(global, init_val)| self.allocate_global(global, init_val)),
);
module_instance
.mem_addrs
.extend(module.mems.into_iter().map(|m| self.allocate_memory(m)));
module_instance.table_addrs.extend(
module
.tables
.into_iter()
.zip(initial_table_refs)
.map(|(tt, ref_t)| self.allocate_table(tt, ref_t)),
);
module_instance.data_addrs.extend(
module
.data_segments
.into_iter()
.map(|ds| self.allocate_data_instance(ds)),
);
for (elem, refs) in module
.element_segments
.into_iter()
.zip(element_segment_refs)
{
let addr = self.allocate_element_segment(elem, refs);
module_instance.elem_addrs.push(addr);
}
for func in module.functions {
let addr = self.allocate_function(func, &module_instance)?;
module_instance.function_addrs.push(addr);
}
for export in module.exports {
let extern_value = match export.description {
ExportDescription::Func(x) => ExternalValue::Function {
addr: *module_instance
.function_addrs
.get(x as usize)
.ok_or_else(|| anyhow!("oob"))?,
},
ExportDescription::Table(x) => ExternalValue::Table {
addr: *module_instance
.table_addrs
.get(x as usize)
.ok_or_else(|| anyhow!("oob"))?,
},
ExportDescription::Mem(x) => ExternalValue::Memory {
addr: *module_instance
.mem_addrs
.get(x as usize)
.ok_or_else(|| anyhow!("oob"))?,
},
ExportDescription::Global(x) => ExternalValue::Global {
addr: *module_instance
.global_addrs
.get(x as usize)
.ok_or_else(|| anyhow!("oob"))?,
},
};
module_instance.exports.push(ExportInstance {
name: export.name,
value: extern_value,
});
}
Ok(module_instance)
}
}