use wasmer_runtime_core::{ backend::Backend, module::{ DataInitializer, ExportIndex, ImportName, ModuleInfo, StringTable, StringTableBuilder, TableInitializer, }, structures::{Map, TypedIndex}, types::{ ElementType, FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit, ImportedGlobalIndex, Initializer, MemoryDescriptor, MemoryIndex, SigIndex, TableDescriptor, TableIndex, Type, Value, }, units::Pages, }; use wasmparser::{ BinaryReaderError, CodeSectionReader, Data, DataKind, Element, ElementKind, Export, ExternalKind, FuncType, Import, ImportSectionEntryType, InitExpr, ModuleReader, Operator, SectionCode, Type as WpType, }; pub fn read_module(wasm: &[u8]) -> Result<(ModuleInfo, CodeSectionReader), BinaryReaderError> { let mut info = ModuleInfo { memories: Map::new(), globals: Map::new(), tables: Map::new(), imported_functions: Map::new(), imported_memories: Map::new(), imported_tables: Map::new(), imported_globals: Map::new(), exports: Default::default(), data_initializers: Vec::new(), elem_initializers: Vec::new(), start_func: None, func_assoc: Map::new(), signatures: Map::new(), backend: Backend::LLVM, namespace_table: StringTable::new(), name_table: StringTable::new(), }; let mut reader = ModuleReader::new(wasm)?; let mut code_reader = None; loop { if reader.eof() { return Ok((info, code_reader.unwrap())); } let section = reader.read()?; match section.code { SectionCode::Type => { let type_reader = section.get_type_section_reader()?; for ty in type_reader { let ty = ty?; info.signatures.push(func_type_to_func_sig(ty)?); } } SectionCode::Import => { let import_reader = section.get_import_section_reader()?; let mut namespace_builder = StringTableBuilder::new(); let mut name_builder = StringTableBuilder::new(); for import in import_reader { let Import { module, field, ty } = import?; let namespace_index = namespace_builder.register(module); let name_index = name_builder.register(field); let import_name = ImportName { namespace_index, name_index, }; match ty { ImportSectionEntryType::Function(sigindex) => { let sigindex = SigIndex::new(sigindex as usize); info.imported_functions.push(import_name); info.func_assoc.push(sigindex); } ImportSectionEntryType::Table(table_ty) => { assert_eq!(table_ty.element_type, WpType::AnyFunc); let table_desc = TableDescriptor { element: ElementType::Anyfunc, minimum: table_ty.limits.initial, maximum: table_ty.limits.maximum, }; info.imported_tables.push((import_name, table_desc)); } ImportSectionEntryType::Memory(memory_ty) => { let mem_desc = MemoryDescriptor { minimum: Pages(memory_ty.limits.initial), maximum: memory_ty.limits.maximum.map(|max| Pages(max)), shared: memory_ty.shared, }; info.imported_memories.push((import_name, mem_desc)); } ImportSectionEntryType::Global(global_ty) => { let global_desc = GlobalDescriptor { mutable: global_ty.mutable, ty: type_to_type(global_ty.content_type)?, }; info.imported_globals.push((import_name, global_desc)); } } } } SectionCode::Function => { let func_decl_reader = section.get_function_section_reader()?; for sigindex in func_decl_reader { let sigindex = sigindex?; let sigindex = SigIndex::new(sigindex as usize); info.func_assoc.push(sigindex); } } SectionCode::Table => { let table_decl_reader = section.get_table_section_reader()?; for table_ty in table_decl_reader { let table_ty = table_ty?; let table_desc = TableDescriptor { element: ElementType::Anyfunc, minimum: table_ty.limits.initial, maximum: table_ty.limits.maximum, }; info.tables.push(table_desc); } } SectionCode::Memory => { let mem_decl_reader = section.get_memory_section_reader()?; for memory_ty in mem_decl_reader { let memory_ty = memory_ty?; let mem_desc = MemoryDescriptor { minimum: Pages(memory_ty.limits.initial), maximum: memory_ty.limits.maximum.map(|max| Pages(max)), shared: memory_ty.shared, }; info.memories.push(mem_desc); } } SectionCode::Global => { let global_decl_reader = section.get_global_section_reader()?; for global in global_decl_reader { let global = global?; let desc = GlobalDescriptor { mutable: global.ty.mutable, ty: type_to_type(global.ty.content_type)?, }; let global_init = GlobalInit { desc, init: eval_init_expr(&global.init_expr)?, }; info.globals.push(global_init); } } SectionCode::Export => { let export_reader = section.get_export_section_reader()?; for export in export_reader { let Export { field, kind, index } = export?; let export_index = match kind { ExternalKind::Function => ExportIndex::Func(FuncIndex::new(index as usize)), ExternalKind::Table => ExportIndex::Table(TableIndex::new(index as usize)), ExternalKind::Memory => { ExportIndex::Memory(MemoryIndex::new(index as usize)) } ExternalKind::Global => { ExportIndex::Global(GlobalIndex::new(index as usize)) } }; info.exports.insert(field.to_string(), export_index); } } SectionCode::Start => { let start_index = section.get_start_section_content()?; info.start_func = Some(FuncIndex::new(start_index as usize)); } SectionCode::Element => { let element_reader = section.get_element_section_reader()?; for element in element_reader { let Element { kind, items } = element?; match kind { ElementKind::Active { table_index, init_expr, } => { let table_index = TableIndex::new(table_index as usize); let base = eval_init_expr(&init_expr)?; let items_reader = items.get_items_reader()?; let elements: Vec<_> = items_reader .into_iter() .map(|res| res.map(|index| FuncIndex::new(index as usize))) .collect::>()?; let table_init = TableInitializer { table_index, base, elements, }; info.elem_initializers.push(table_init); } ElementKind::Passive(_ty) => { return Err(BinaryReaderError { message: "passive tables are not yet supported", offset: -1isize as usize, }); } } } } SectionCode::Code => { code_reader = Some(section.get_code_section_reader()?); } SectionCode::Data => { let data_reader = section.get_data_section_reader()?; for data in data_reader { let Data { kind, data } = data?; match kind { DataKind::Active { memory_index, init_expr, } => { let memory_index = MemoryIndex::new(memory_index as usize); let base = eval_init_expr(&init_expr)?; let data_init = DataInitializer { memory_index, base, data: data.to_vec(), }; info.data_initializers.push(data_init); } DataKind::Passive => { return Err(BinaryReaderError { message: "passive memories are not yet supported", offset: -1isize as usize, }); } } } } SectionCode::DataCount => {} SectionCode::Custom { .. } => {} } } } pub fn type_to_type(ty: WpType) -> Result { Ok(match ty { WpType::I32 => Type::I32, WpType::I64 => Type::I64, WpType::F32 => Type::F32, WpType::F64 => Type::F64, WpType::V128 => { return Err(BinaryReaderError { message: "the wasmer llvm backend does not yet support the simd extension", offset: -1isize as usize, }); } _ => panic!("broken invariant, invalid type"), }) } fn func_type_to_func_sig(func_ty: FuncType) -> Result { assert_eq!(func_ty.form, WpType::Func); Ok(FuncSig::new( func_ty .params .iter() .cloned() .map(type_to_type) .collect::, _>>()?, func_ty .returns .iter() .cloned() .map(type_to_type) .collect::, _>>()?, )) } fn eval_init_expr(expr: &InitExpr) -> Result { let mut reader = expr.get_operators_reader(); let (op, offset) = reader.read_with_offset()?; Ok(match op { Operator::GetGlobal { global_index } => { Initializer::GetGlobal(ImportedGlobalIndex::new(global_index as usize)) } Operator::I32Const { value } => Initializer::Const(Value::I32(value)), Operator::I64Const { value } => Initializer::Const(Value::I64(value)), Operator::F32Const { value } => { Initializer::Const(Value::F32(f32::from_bits(value.bits()))) } Operator::F64Const { value } => { Initializer::Const(Value::F64(f64::from_bits(value.bits()))) } _ => { return Err(BinaryReaderError { message: "init expr evaluation failed: unsupported opcode", offset, }); } }) }