use core::convert::TryFrom;
use std::vec::Vec;
use wasmer_types::entity::EntityRef;
use wasmer_types::{
ExportIndex, FunctionIndex, FunctionType, GlobalIndex, GlobalType, ImportIndex, MemoryIndex,
MemoryType, ModuleInfo, Pages, SignatureIndex, TableIndex, TableType, Type,
};
use wasmparser::{
self, BinaryReaderError, Export, ExportSectionReader, ExternalKind, FunctionSectionReader,
GlobalSectionReader, GlobalType as WPGlobalType, ImportSectionReader, MemorySectionReader,
MemoryType as WPMemoryType, NameSectionReader, Parser, Payload, TableSectionReader, TypeRef,
TypeSectionReader,
};
pub type WasmResult<T> = Result<T, String>;
#[derive(Default)]
pub struct ModuleInfoPolyfill {
pub(crate) info: ModuleInfo,
}
impl ModuleInfoPolyfill {
pub(crate) fn declare_export(&mut self, export: ExportIndex, name: &str) -> WasmResult<()> {
self.info.exports.insert(String::from(name), export);
Ok(())
}
pub(crate) fn declare_import(
&mut self,
import: ImportIndex,
module: &str,
field: &str,
) -> WasmResult<()> {
self.info.imports.insert(
wasmer_types::ImportKey {
module: String::from(module),
field: String::from(field),
import_idx: self.info.imports.len() as u32,
},
import,
);
Ok(())
}
pub(crate) fn reserve_signatures(&mut self, num: u32) -> WasmResult<()> {
self.info
.signatures
.reserve_exact(usize::try_from(num).unwrap());
Ok(())
}
pub(crate) fn declare_signature(&mut self, sig: FunctionType) -> WasmResult<()> {
self.info.signatures.push(sig);
Ok(())
}
pub(crate) fn declare_func_import(
&mut self,
sig_index: SignatureIndex,
module: &str,
field: &str,
) -> WasmResult<()> {
debug_assert_eq!(
self.info.functions.len(),
self.info.num_imported_functions,
"Imported functions must be declared first"
);
self.declare_import(
ImportIndex::Function(FunctionIndex::from_u32(
self.info.num_imported_functions as _,
)),
module,
field,
)?;
self.info.functions.push(sig_index);
self.info.num_imported_functions += 1;
Ok(())
}
pub(crate) fn declare_table_import(
&mut self,
table: TableType,
module: &str,
field: &str,
) -> WasmResult<()> {
debug_assert_eq!(
self.info.tables.len(),
self.info.num_imported_tables,
"Imported tables must be declared first"
);
self.declare_import(
ImportIndex::Table(TableIndex::from_u32(self.info.num_imported_tables as _)),
module,
field,
)?;
self.info.tables.push(table);
self.info.num_imported_tables += 1;
Ok(())
}
pub(crate) fn declare_memory_import(
&mut self,
memory: MemoryType,
module: &str,
field: &str,
) -> WasmResult<()> {
debug_assert_eq!(
self.info.memories.len(),
self.info.num_imported_memories,
"Imported memories must be declared first"
);
self.declare_import(
ImportIndex::Memory(MemoryIndex::from_u32(self.info.num_imported_memories as _)),
module,
field,
)?;
self.info.memories.push(memory);
self.info.num_imported_memories += 1;
Ok(())
}
pub(crate) fn declare_global_import(
&mut self,
global: GlobalType,
module: &str,
field: &str,
) -> WasmResult<()> {
debug_assert_eq!(
self.info.globals.len(),
self.info.num_imported_globals,
"Imported globals must be declared first"
);
self.declare_import(
ImportIndex::Global(GlobalIndex::from_u32(self.info.num_imported_globals as _)),
module,
field,
)?;
self.info.globals.push(global);
self.info.num_imported_globals += 1;
Ok(())
}
pub(crate) fn reserve_func_types(&mut self, num: u32) -> WasmResult<()> {
self.info
.functions
.reserve_exact(usize::try_from(num).unwrap());
Ok(())
}
pub(crate) fn declare_func_type(&mut self, sig_index: SignatureIndex) -> WasmResult<()> {
self.info.functions.push(sig_index);
Ok(())
}
pub(crate) fn reserve_tables(&mut self, num: u32) -> WasmResult<()> {
self.info
.tables
.reserve_exact(usize::try_from(num).unwrap());
Ok(())
}
pub(crate) fn declare_table(&mut self, table: TableType) -> WasmResult<()> {
self.info.tables.push(table);
Ok(())
}
pub(crate) fn reserve_memories(&mut self, num: u32) -> WasmResult<()> {
self.info
.memories
.reserve_exact(usize::try_from(num).unwrap());
Ok(())
}
pub(crate) fn declare_memory(&mut self, memory: MemoryType) -> WasmResult<()> {
self.info.memories.push(memory);
Ok(())
}
pub(crate) fn reserve_globals(&mut self, num: u32) -> WasmResult<()> {
self.info
.globals
.reserve_exact(usize::try_from(num).unwrap());
Ok(())
}
pub(crate) fn declare_global(&mut self, global: GlobalType) -> WasmResult<()> {
self.info.globals.push(global);
Ok(())
}
pub(crate) fn reserve_exports(&mut self, num: u32) -> WasmResult<()> {
self.info.exports.reserve(usize::try_from(num).unwrap());
Ok(())
}
pub(crate) fn reserve_imports(&mut self, num: u32) -> WasmResult<()> {
self.info.imports.reserve(usize::try_from(num).unwrap());
Ok(())
}
pub(crate) fn declare_func_export(
&mut self,
func_index: FunctionIndex,
name: &str,
) -> WasmResult<()> {
self.declare_export(ExportIndex::Function(func_index), name)
}
pub(crate) fn declare_table_export(
&mut self,
table_index: TableIndex,
name: &str,
) -> WasmResult<()> {
self.declare_export(ExportIndex::Table(table_index), name)
}
pub(crate) fn declare_memory_export(
&mut self,
memory_index: MemoryIndex,
name: &str,
) -> WasmResult<()> {
self.declare_export(ExportIndex::Memory(memory_index), name)
}
pub(crate) fn declare_global_export(
&mut self,
global_index: GlobalIndex,
name: &str,
) -> WasmResult<()> {
self.declare_export(ExportIndex::Global(global_index), name)
}
pub(crate) fn declare_module_name(&mut self, name: &str) -> WasmResult<()> {
self.info.name = Some(name.to_string());
Ok(())
}
}
fn transform_err(err: BinaryReaderError) -> String {
err.message().into()
}
pub fn translate_module<'data>(data: &'data [u8]) -> WasmResult<ModuleInfoPolyfill> {
let mut module_info: ModuleInfoPolyfill = Default::default();
for payload in Parser::new(0).parse_all(data) {
match payload.map_err(transform_err)? {
Payload::TypeSection(types) => {
parse_type_section(types, &mut module_info)?;
}
Payload::ImportSection(imports) => {
parse_import_section(imports, &mut module_info)?;
}
Payload::FunctionSection(functions) => {
parse_function_section(functions, &mut module_info)?;
}
Payload::TableSection(tables) => {
parse_table_section(tables, &mut module_info)?;
}
Payload::MemorySection(memories) => {
parse_memory_section(memories, &mut module_info)?;
}
Payload::GlobalSection(globals) => {
parse_global_section(globals, &mut module_info)?;
}
Payload::ExportSection(exports) => {
parse_export_section(exports, &mut module_info)?;
}
Payload::CustomSection(sectionreader) => {
let name = sectionreader.name();
if name == "name" {
parse_name_section(
NameSectionReader::new(sectionreader.data(), sectionreader.data_offset()),
&mut module_info,
)?;
}
}
_ => {}
}
}
Ok(module_info)
}
pub fn wptype_to_type(ty: wasmparser::ValType) -> WasmResult<Type> {
match ty {
wasmparser::ValType::I32 => Ok(Type::I32),
wasmparser::ValType::I64 => Ok(Type::I64),
wasmparser::ValType::F32 => Ok(Type::F32),
wasmparser::ValType::F64 => Ok(Type::F64),
wasmparser::ValType::V128 => Ok(Type::V128),
wasmparser::ValType::Ref(ty) => wpreftype_to_type(ty),
}
}
pub fn wpreftype_to_type(ty: wasmparser::RefType) -> WasmResult<Type> {
if ty.is_extern_ref() {
Ok(Type::ExternRef)
} else if ty.is_func_ref() {
Ok(Type::FuncRef)
} else {
Err(format!("Unsupported ref type: {:?}", ty))
}
}
pub fn parse_type_section(
reader: TypeSectionReader,
module_info: &mut ModuleInfoPolyfill,
) -> WasmResult<()> {
module_info.reserve_signatures(reader.count())?;
for res in reader {
let group = res.map_err(transform_err)?;
for ty in group.into_types() {
match ty.composite_type {
wasmparser::CompositeType::Func(functype) => {
let params = functype.params();
let returns = functype.results();
let sig_params: Vec<Type> = params
.iter()
.map(|ty| {
wptype_to_type(*ty)
.expect("only numeric types are supported in function signatures")
})
.collect();
let sig_returns: Vec<Type> = returns
.iter()
.map(|ty| {
wptype_to_type(*ty)
.expect("only numeric types are supported in function signatures")
})
.collect();
let sig = FunctionType::new(sig_params, sig_returns);
module_info.declare_signature(sig)?;
}
_ => {
unimplemented!("GC is not implemented yet")
}
}
}
}
Ok(())
}
pub fn parse_import_section<'data>(
imports: ImportSectionReader<'data>,
module_info: &mut ModuleInfoPolyfill,
) -> WasmResult<()> {
module_info.reserve_imports(imports.count())?;
for entry in imports {
let import = entry.map_err(transform_err)?;
let module_name = import.module;
let field_name = import.name;
match import.ty {
TypeRef::Func(sig) => {
module_info.declare_func_import(
SignatureIndex::from_u32(sig),
module_name,
field_name,
)?;
}
TypeRef::Tag(_) => {
unimplemented!("exception handling not implemented yet")
}
TypeRef::Memory(WPMemoryType {
shared,
memory64,
initial,
maximum,
}) => {
if memory64 {
unimplemented!("64bit memory not implemented yet");
}
module_info.declare_memory_import(
MemoryType {
minimum: Pages(initial as u32),
maximum: maximum.map(|p| Pages(p as u32)),
shared,
},
module_name,
field_name,
)?;
}
TypeRef::Global(ref ty) => {
module_info.declare_global_import(
GlobalType {
ty: wptype_to_type(ty.content_type).unwrap(),
mutability: ty.mutable.into(),
},
module_name,
field_name,
)?;
}
TypeRef::Table(ref tab) => {
module_info.declare_table_import(
TableType {
ty: wpreftype_to_type(tab.element_type).unwrap(),
minimum: tab.initial,
maximum: tab.maximum,
},
module_name,
field_name,
)?;
}
}
}
Ok(())
}
pub fn parse_function_section(
functions: FunctionSectionReader,
module_info: &mut ModuleInfoPolyfill,
) -> WasmResult<()> {
let num_functions = functions.count();
module_info.reserve_func_types(num_functions)?;
for entry in functions {
let sigindex = entry.map_err(transform_err)?;
module_info.declare_func_type(SignatureIndex::from_u32(sigindex))?;
}
Ok(())
}
pub fn parse_table_section(
tables: TableSectionReader,
module_info: &mut ModuleInfoPolyfill,
) -> WasmResult<()> {
module_info.reserve_tables(tables.count())?;
for entry in tables {
let table = entry.map_err(transform_err)?;
module_info.declare_table(TableType {
ty: wpreftype_to_type(table.ty.element_type).unwrap(),
minimum: table.ty.initial,
maximum: table.ty.maximum,
})?;
}
Ok(())
}
pub fn parse_memory_section(
memories: MemorySectionReader,
module_info: &mut ModuleInfoPolyfill,
) -> WasmResult<()> {
module_info.reserve_memories(memories.count())?;
for entry in memories {
let WPMemoryType {
shared,
memory64,
initial,
maximum,
} = entry.map_err(transform_err)?;
if memory64 {
unimplemented!("64bit memory not implemented yet");
}
module_info.declare_memory(MemoryType {
minimum: Pages(initial as u32),
maximum: maximum.map(|p| Pages(p as u32)),
shared,
})?;
}
Ok(())
}
pub fn parse_global_section(
globals: GlobalSectionReader,
module_info: &mut ModuleInfoPolyfill,
) -> WasmResult<()> {
module_info.reserve_globals(globals.count())?;
for entry in globals {
let WPGlobalType {
content_type,
mutable,
} = entry.map_err(transform_err)?.ty;
let global = GlobalType {
ty: wptype_to_type(content_type).unwrap(),
mutability: mutable.into(),
};
module_info.declare_global(global)?;
}
Ok(())
}
pub fn parse_export_section<'data>(
exports: ExportSectionReader<'data>,
module_info: &mut ModuleInfoPolyfill,
) -> WasmResult<()> {
module_info.reserve_exports(exports.count())?;
for entry in exports {
let Export {
name,
ref kind,
index,
} = entry.map_err(transform_err)?;
let index = index as usize;
match *kind {
ExternalKind::Func => {
module_info.declare_func_export(FunctionIndex::new(index), name)?
}
ExternalKind::Table => {
module_info.declare_table_export(TableIndex::new(index), name)?
}
ExternalKind::Memory => {
module_info.declare_memory_export(MemoryIndex::new(index), name)?
}
ExternalKind::Global => {
module_info.declare_global_export(GlobalIndex::new(index), name)?
}
ExternalKind::Tag => {
unimplemented!("exception handling not implemented yet")
}
}
}
Ok(())
}
pub fn parse_name_section<'data>(
mut names: NameSectionReader<'data>,
module_info: &mut ModuleInfoPolyfill,
) -> WasmResult<()> {
while let Some(Ok(subsection)) = names.next() {
match subsection {
wasmparser::Name::Function(_function_subsection) => {
}
wasmparser::Name::Module {
name,
name_range: _,
} => {
module_info.declare_module_name(name)?;
}
wasmparser::Name::Local(_) => {}
wasmparser::Name::Label(_)
| wasmparser::Name::Type(_)
| wasmparser::Name::Table(_)
| wasmparser::Name::Memory(_)
| wasmparser::Name::Global(_)
| wasmparser::Name::Element(_)
| wasmparser::Name::Data(_)
| wasmparser::Name::Tag(_)
| wasmparser::Name::Unknown { .. } => {}
};
}
Ok(())
}