use crate::canon::{
self, AggKind, ArrayLayout, CanonicalTypeId, GroupId, IrGlobalType, IrHeap, IrTableType,
Layout, ModuleType, Slot, StructLayout,
};
use crate::engine::Engine;
use crate::module::op::CompiledFunc;
use crate::value::{ExternType, FuncType, GlobalType, MemoryType, TableType, TagType};
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
pub(crate) struct ModuleInner {
pub types: Vec<ModuleType>,
pub func_types: Vec<u32>,
pub num_imported_funcs: u32,
pub functions: Vec<CompiledFunc>,
pub code: crate::module::code::CodeArenas,
pub imports: Vec<Import>,
pub exports: Vec<Export>,
pub memories: Vec<MemoryType>,
pub tables: Vec<TableDef>,
pub globals: Vec<GlobalDef>,
pub tags: Vec<TagDef>,
pub num_imported_tags: u32,
pub datas: Vec<DataSegment>,
pub elems: Vec<ElemSegment>,
pub start: Option<u32>,
#[serde(skip)]
pub type_ids: Vec<CanonicalTypeId>,
#[serde(skip)]
pub group_handles: Vec<GroupId>,
#[serde(skip)]
pub layouts: Vec<Option<Layout>>,
#[serde(skip)]
pub engine: Option<Engine>,
pub debug: crate::module::debug::DebugSections,
}
impl ModuleInner {
pub(crate) fn intern(&mut self, engine: &Engine) {
let (type_ids, group_handles) = engine.intern_types(&self.types);
self.type_ids = type_ids;
self.group_handles = group_handles;
self.layouts = self
.types
.iter()
.map(|t| Layout::from_body(&t.body))
.collect();
self.engine = Some(engine.clone());
}
pub(crate) fn canonical_type_id(&self, type_index: u32) -> CanonicalTypeId {
self.type_ids[type_index as usize]
}
pub(crate) fn struct_layout(&self, type_index: u32) -> crate::Result<&StructLayout> {
self.layout(type_index)
.and_then(Layout::as_struct)
.ok_or_else(|| crate::Error::msg(format!("type {type_index} is not a struct type")))
}
pub(crate) fn struct_field(&self, type_index: u32, field: u32) -> crate::Result<Slot> {
self.struct_layout(type_index)?
.field(field as usize)
.ok_or_else(|| {
crate::Error::msg(format!("type {type_index} has no struct field {field}"))
})
}
pub(crate) fn array_layout(&self, type_index: u32) -> crate::Result<ArrayLayout> {
self.layout(type_index)
.and_then(Layout::as_array)
.ok_or_else(|| crate::Error::msg(format!("type {type_index} is not an array type")))
}
fn layout(&self, type_index: u32) -> Option<&Layout> {
self.layouts.get(type_index as usize)?.as_ref()
}
pub(crate) fn engine(&self) -> &Engine {
self.engine.as_ref().expect("module not interned")
}
pub(crate) fn type_kinds(&self) -> Vec<AggKind> {
self.types.iter().map(ModuleType::kind).collect()
}
}
impl Drop for ModuleInner {
fn drop(&mut self) {
if let Some(engine) = &self.engine {
engine.release_types(&self.group_handles);
}
}
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub(crate) struct Import {
pub module: String,
pub name: String,
pub kind: ImportKind,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub(crate) enum ImportKind {
Func(u32),
Table(IrTableType),
Memory(MemoryType),
Global(IrGlobalType),
Tag(u32),
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub(crate) struct Export {
pub name: String,
pub kind: ExportKind,
}
#[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub(crate) enum ExportKind {
Func(u32),
Table(u32),
Memory(u32),
Global(u32),
Tag(u32),
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub(crate) struct TableDef {
pub ty: IrTableType,
pub init: Option<ConstExpr>,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub(crate) struct GlobalDef {
pub ty: IrGlobalType,
pub init: ConstExpr,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub(crate) struct TagDef {
pub type_index: u32,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub(crate) struct DataSegment {
pub mode: DataMode,
pub bytes: Box<[u8]>,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub(crate) enum DataMode {
Passive,
Active { memory: u32, offset: ConstExpr },
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub(crate) struct ElemSegment {
pub mode: ElemMode,
pub items: ElemItems,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub(crate) enum ElemMode {
Passive,
Declared,
Active { table: u32, offset: ConstExpr },
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub(crate) enum ElemItems {
Funcs(Box<[u32]>),
Exprs(Box<[ConstExpr]>),
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub(crate) struct ConstExpr(pub Box<[ConstOp]>);
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub(crate) enum ConstOp {
I32(i32),
I64(i64),
F32(u32),
F64(u64),
#[cfg(feature = "simd")]
V128(u128),
RefNull(IrHeap),
RefFunc(u32),
GlobalGet(u32),
I32Add,
I32Sub,
I32Mul,
I64Add,
I64Sub,
I64Mul,
RefI31,
StructNew(u32),
StructNewDefault(u32),
ArrayNew(u32),
ArrayNewDefault(u32),
ArrayNewFixed {
ty: u32,
n: u32,
},
ArrayNewData {
ty: u32,
data: u32,
},
ArrayNewElem {
ty: u32,
elem: u32,
},
AnyConvertExtern,
ExternConvertAny,
}
impl ModuleInner {
pub(crate) fn func_type(&self, module_func_idx: u32) -> FuncType {
canon::func_handle(
self.engine(),
self.canonical_type_id(self.func_types[module_func_idx as usize]),
)
}
pub(crate) fn import_extern_type(&self, kind: &ImportKind) -> ExternType {
let (engine, ids) = (self.engine(), &self.type_ids);
match kind {
ImportKind::Func(t) => {
ExternType::Func(canon::func_handle(engine, self.canonical_type_id(*t)))
}
ImportKind::Table(tt) => ExternType::Table(canon::materialize_table(engine, ids, tt)),
ImportKind::Memory(mt) => ExternType::Memory(mt.clone()),
ImportKind::Global(gt) => {
ExternType::Global(canon::materialize_global(engine, ids, gt))
}
ImportKind::Tag(t) => ExternType::Tag(TagType::new(canon::func_handle(
engine,
self.canonical_type_id(*t),
))),
}
}
pub(crate) fn export_extern_type(&self, kind: ExportKind) -> ExternType {
match kind {
ExportKind::Func(i) => ExternType::Func(self.func_type(i)),
ExportKind::Table(i) => ExternType::Table(self.nth_table(i)),
ExportKind::Memory(i) => ExternType::Memory(self.nth_memory(i)),
ExportKind::Global(i) => ExternType::Global(self.nth_global(i)),
ExportKind::Tag(i) => ExternType::Tag(self.nth_tag(i)),
}
}
fn nth_memory(&self, idx: u32) -> MemoryType {
let mut n = 0;
for imp in &self.imports {
if let ImportKind::Memory(mt) = &imp.kind {
if n == idx {
return mt.clone();
}
n += 1;
}
}
self.memories[(idx - n) as usize].clone()
}
fn nth_table(&self, idx: u32) -> TableType {
let (engine, ids) = (self.engine(), &self.type_ids);
let mut n = 0;
for imp in &self.imports {
if let ImportKind::Table(tt) = &imp.kind {
if n == idx {
return canon::materialize_table(engine, ids, tt);
}
n += 1;
}
}
canon::materialize_table(engine, ids, &self.tables[(idx - n) as usize].ty)
}
fn nth_global(&self, idx: u32) -> GlobalType {
let (engine, ids) = (self.engine(), &self.type_ids);
let mut n = 0;
for imp in &self.imports {
if let ImportKind::Global(gt) = &imp.kind {
if n == idx {
return canon::materialize_global(engine, ids, gt);
}
n += 1;
}
}
canon::materialize_global(engine, ids, &self.globals[(idx - n) as usize].ty)
}
fn nth_tag(&self, idx: u32) -> TagType {
let mut n = 0;
for imp in &self.imports {
if let ImportKind::Tag(t) = &imp.kind {
if n == idx {
return self.tag_type(*t);
}
n += 1;
}
}
self.tag_type(self.tags[(idx - n) as usize].type_index)
}
pub(crate) fn tag_type(&self, type_index: u32) -> TagType {
TagType::new(canon::func_handle(
self.engine(),
self.canonical_type_id(type_index),
))
}
}