use crate::emit::{Emit, EmitContext, Section};
use crate::parse::IndicesToIds;
use crate::tombstone_arena::{Id, Tombstone, TombstoneArena};
use crate::{FunctionId, FunctionTable, GlobalId, MemoryId, Result, TableId};
use crate::{Module, TableKind, TypeId, ValType};
pub type ImportId = Id<Import>;
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct Import {
id: ImportId,
pub module: String,
pub name: String,
pub kind: ImportKind,
}
impl Tombstone for Import {
fn on_delete(&mut self) {
self.module = String::new();
self.name = String::new();
}
}
impl Import {
pub fn id(&self) -> ImportId {
self.id
}
}
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub enum ImportKind {
Function(FunctionId),
Table(TableId),
Memory(MemoryId),
Global(GlobalId),
}
#[derive(Debug, Default)]
pub struct ModuleImports {
arena: TombstoneArena<Import>,
}
impl ModuleImports {
pub fn get(&self, id: ImportId) -> &Import {
&self.arena[id]
}
pub fn get_mut(&mut self, id: ImportId) -> &mut Import {
&mut self.arena[id]
}
pub fn delete(&mut self, id: ImportId) {
self.arena.delete(id);
}
pub fn iter(&self) -> impl Iterator<Item = &Import> {
self.arena.iter().map(|(_, f)| f)
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Import> {
self.arena.iter_mut().map(|(_, f)| f)
}
pub fn add(&mut self, module: &str, name: &str, kind: impl Into<ImportKind>) -> ImportId {
self.arena.alloc_with_id(|id| Import {
id,
module: module.to_string(),
name: name.to_string(),
kind: kind.into(),
})
}
}
impl Module {
pub(crate) fn parse_imports(
&mut self,
section: wasmparser::ImportSectionReader,
ids: &mut IndicesToIds,
) -> Result<()> {
log::debug!("parse import section");
for entry in section {
let entry = entry?;
match entry.ty {
wasmparser::ImportSectionEntryType::Function(idx) => {
let ty = ids.get_type(idx)?;
let id = self.add_import_func(entry.module, entry.field, ty);
ids.push_func(id);
}
wasmparser::ImportSectionEntryType::Table(t) => {
let kind = match t.element_type {
wasmparser::Type::AnyFunc => TableKind::Function(FunctionTable::default()),
_ => failure::bail!("invalid table type"),
};
let id = self.add_import_table(
entry.module,
entry.field,
t.limits.initial,
t.limits.maximum,
kind,
);
ids.push_table(id);
}
wasmparser::ImportSectionEntryType::Memory(m) => {
let id = self.add_import_memory(
entry.module,
entry.field,
m.shared,
m.limits.initial,
m.limits.maximum,
);
ids.push_memory(id);
}
wasmparser::ImportSectionEntryType::Global(g) => {
let id = self.add_import_global(
entry.module,
entry.field,
ValType::parse(&g.content_type)?,
g.mutable,
);
ids.push_global(id);
}
}
}
Ok(())
}
pub fn add_import_func(&mut self, module: &str, name: &str, ty: TypeId) -> FunctionId {
let import = self.imports.arena.next_id();
let func = self.funcs.add_import(ty, import);
self.imports.add(module, name, func);
func
}
pub fn add_import_memory(
&mut self,
module: &str,
name: &str,
shared: bool,
initial: u32,
maximum: Option<u32>,
) -> MemoryId {
let import = self.imports.arena.next_id();
let mem = self.memories.add_import(shared, initial, maximum, import);
self.imports.add(module, name, mem);
mem
}
pub fn add_import_table(
&mut self,
module: &str,
name: &str,
initial: u32,
max: Option<u32>,
kind: TableKind,
) -> TableId {
let import = self.imports.arena.next_id();
let table = self.tables.add_import(initial, max, kind, import);
self.imports.add(module, name, table);
table
}
pub fn add_import_global(
&mut self,
module: &str,
name: &str,
ty: ValType,
mutable: bool,
) -> GlobalId {
let import = self.imports.arena.next_id();
let global = self.globals.add_import(ty, mutable, import);
self.imports.add(module, name, global);
global
}
}
impl Emit for ModuleImports {
fn emit(&self, cx: &mut EmitContext) {
log::debug!("emit import section");
let mut imports = Vec::new();
for (_id, import) in self.arena.iter() {
let used = match import.kind {
ImportKind::Function(id) => cx.used.funcs.contains(&id),
ImportKind::Global(id) => cx.used.globals.contains(&id),
ImportKind::Memory(id) => cx.used.memories.contains(&id),
ImportKind::Table(id) => cx.used.tables.contains(&id),
};
if !used {
continue;
}
imports.push(import);
}
if imports.len() == 0 {
return;
}
let mut cx = cx.start_section(Section::Import);
cx.encoder.usize(imports.len());
for import in imports {
cx.encoder.str(&import.module);
cx.encoder.str(&import.name);
match import.kind {
ImportKind::Function(id) => {
cx.encoder.byte(0x00);
cx.indices.push_func(id);
let ty = cx.module.funcs.get(id).ty();
let idx = cx.indices.get_type_index(ty);
cx.encoder.u32(idx);
}
ImportKind::Table(id) => {
cx.encoder.byte(0x01);
cx.indices.push_table(id);
cx.module.tables.get(id).emit(&mut cx);
}
ImportKind::Memory(id) => {
cx.encoder.byte(0x02);
cx.indices.push_memory(id);
cx.module.memories.get(id).emit(&mut cx);
}
ImportKind::Global(id) => {
cx.encoder.byte(0x03);
cx.indices.push_global(id);
cx.module.globals.get(id).emit(&mut cx);
}
}
}
}
}
impl From<MemoryId> for ImportKind {
fn from(id: MemoryId) -> ImportKind {
ImportKind::Memory(id)
}
}
impl From<FunctionId> for ImportKind {
fn from(id: FunctionId) -> ImportKind {
ImportKind::Function(id)
}
}
impl From<GlobalId> for ImportKind {
fn from(id: GlobalId) -> ImportKind {
ImportKind::Global(id)
}
}
impl From<TableId> for ImportKind {
fn from(id: TableId) -> ImportKind {
ImportKind::Table(id)
}
}