//! Helper functions to gather information for each of the non-function sections of a //! WebAssembly module. //! //! The code of theses helper function is straightforward since it is only about reading metadata //! about linear memories, tables, globals, etc. and storing them for later use. //! //! The special case of the initialize expressions for table elements offsets or global variables //! is handled, according to the semantics of WebAssembly, to only specific expressions that are //! interpreted on the fly. use translation_utils::{type_to_type, TableIndex, FunctionIndex, GlobalIndex, SignatureIndex, MemoryIndex, Global, GlobalInit, Table, TableElementType, Memory}; use cretonne::ir::{Signature, ArgumentType, CallConv}; use cretonne; use wasmparser::{Parser, ParserState, FuncType, ImportSectionEntryType, ExternalKind, WasmDecoder, MemoryType, Operator}; use wasmparser; use std::collections::HashMap; use std::str::from_utf8; use runtime::WasmRuntime; pub enum SectionParsingError { WrongSectionContent(String), } /// Reads the Type Section of the wasm module and returns the corresponding function signatures. pub fn parse_function_signatures( parser: &mut Parser, runtime: &mut WasmRuntime, ) -> Result<(), SectionParsingError> { loop { match *parser.read() { ParserState::EndSection => break, ParserState::TypeSectionEntry(FuncType { form: wasmparser::Type::Func, ref params, ref returns, }) => { let mut sig = Signature::new(CallConv::Native); sig.argument_types.extend(params.iter().map(|ty| { let cret_arg: cretonne::ir::Type = type_to_type(ty).expect( "only numeric types are supported in function signatures", ); ArgumentType::new(cret_arg) })); sig.return_types.extend(returns.iter().map(|ty| { let cret_arg: cretonne::ir::Type = type_to_type(ty).expect( "only numeric types are supported in function signatures", ); ArgumentType::new(cret_arg) })); runtime.declare_signature(&sig); } ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), } } Ok(()) } /// Retrieves the imports from the imports section of the binary. pub fn parse_import_section( parser: &mut Parser, runtime: &mut WasmRuntime, ) -> Result<(), SectionParsingError> { loop { match *parser.read() { ParserState::ImportSectionEntry { ty: ImportSectionEntryType::Function(sig), module, field, } => { runtime.declare_func_import(sig as SignatureIndex, module, field); } ParserState::ImportSectionEntry { ty: ImportSectionEntryType::Memory(MemoryType { limits: ref memlimits }), .. } => { runtime.declare_memory(Memory { pages_count: memlimits.initial as usize, maximum: memlimits.maximum.map(|x| x as usize), }); } ParserState::ImportSectionEntry { ty: ImportSectionEntryType::Global(ref ty), .. } => { runtime.declare_global(Global { ty: type_to_type(&ty.content_type).unwrap(), mutability: ty.mutable, initializer: GlobalInit::Import(), }); } ParserState::ImportSectionEntry { ty: ImportSectionEntryType::Table(ref tab), .. } => { runtime.declare_table(Table { ty: match type_to_type(&tab.element_type) { Ok(t) => TableElementType::Val(t), Err(()) => TableElementType::Func(), }, size: tab.limits.initial as usize, maximum: tab.limits.maximum.map(|x| x as usize), }); } ParserState::EndSection => break, ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), }; } Ok(()) } /// Retrieves the correspondances between functions and signatures from the function section pub fn parse_function_section( parser: &mut Parser, runtime: &mut WasmRuntime, ) -> Result<(), SectionParsingError> { loop { match *parser.read() { ParserState::FunctionSectionEntry(sigindex) => { runtime.declare_func_type(sigindex as SignatureIndex); } ParserState::EndSection => break, ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), }; } Ok(()) } /// Retrieves the names of the functions from the export section pub fn parse_export_section( parser: &mut Parser, ) -> Result, SectionParsingError> { let mut exports: HashMap = HashMap::new(); loop { match *parser.read() { ParserState::ExportSectionEntry { field, ref kind, index, } => { match *kind { ExternalKind::Function => { exports.insert( index as FunctionIndex, String::from(from_utf8(field).unwrap()), ); } _ => (),//TODO: deal with other kind of exports } } ParserState::EndSection => break, ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), }; } Ok(exports) } /// Retrieves the start function index from the start section pub fn parse_start_section( parser: &mut Parser, runtime: &mut WasmRuntime, ) -> Result<(), SectionParsingError> { loop { match *parser.read() { ParserState::StartSectionEntry(index) => { runtime.declare_start_func(index as FunctionIndex); } ParserState::EndSection => break, ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), }; } Ok(()) } /// Retrieves the size and maximum fields of memories from the memory section pub fn parse_memory_section( parser: &mut Parser, runtime: &mut WasmRuntime, ) -> Result<(), SectionParsingError> { loop { match *parser.read() { ParserState::MemorySectionEntry(ref ty) => { runtime.declare_memory(Memory { pages_count: ty.limits.initial as usize, maximum: ty.limits.maximum.map(|x| x as usize), }); } ParserState::EndSection => break, ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), }; } Ok(()) } /// Retrieves the size and maximum fields of memories from the memory section pub fn parse_global_section( parser: &mut Parser, runtime: &mut WasmRuntime, ) -> Result<(), SectionParsingError> { loop { let (content_type, mutability) = match *parser.read() { ParserState::BeginGlobalSectionEntry(ref ty) => (ty.content_type, ty.mutable), ParserState::EndSection => break, ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), }; match *parser.read() { ParserState::BeginInitExpressionBody => (), ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), } let initializer = match *parser.read() { ParserState::InitExpressionOperator(Operator::I32Const { value }) => { GlobalInit::I32Const(value) } ParserState::InitExpressionOperator(Operator::I64Const { value }) => { GlobalInit::I64Const(value) } ParserState::InitExpressionOperator(Operator::F32Const { value }) => { GlobalInit::F32Const(value.bits()) } ParserState::InitExpressionOperator(Operator::F64Const { value }) => { GlobalInit::F64Const(value.bits()) } ParserState::InitExpressionOperator(Operator::GetGlobal { global_index }) => { GlobalInit::GlobalRef(global_index as GlobalIndex) } ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), }; match *parser.read() { ParserState::EndInitExpressionBody => (), ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), } let global = Global { ty: type_to_type(&content_type).unwrap(), mutability: mutability, initializer: initializer, }; runtime.declare_global(global); match *parser.read() { ParserState::EndGlobalSectionEntry => (), ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), } } Ok(()) } pub fn parse_data_section( parser: &mut Parser, runtime: &mut WasmRuntime, ) -> Result<(), SectionParsingError> { loop { let memory_index = match *parser.read() { ParserState::BeginDataSectionEntry(memory_index) => memory_index, ParserState::EndSection => break, ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), }; match *parser.read() { ParserState::BeginInitExpressionBody => (), ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), }; let mut offset = match *parser.read() { ParserState::InitExpressionOperator(Operator::I32Const { value }) => { if value < 0 { return Err(SectionParsingError::WrongSectionContent(String::from( "negative \ offset value", ))); } else { value as usize } } ParserState::InitExpressionOperator(Operator::GetGlobal { global_index }) => { match runtime.get_global(global_index as GlobalIndex).initializer { GlobalInit::I32Const(value) => { if value < 0 { return Err(SectionParsingError::WrongSectionContent(String::from( "\ negative offset value", ))); } else { value as usize } } GlobalInit::Import() => { return Err(SectionParsingError::WrongSectionContent(String::from( "\ imported globals not supported", ))) } // TODO: add runtime support _ => panic!("should not happen"), } } ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), }; match *parser.read() { ParserState::EndInitExpressionBody => (), ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), }; match *parser.read() { ParserState::BeginDataSectionEntryBody(_) => (), ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), }; loop { let data = match *parser.read() { ParserState::DataSectionEntryBodyChunk(data) => data, ParserState::EndDataSectionEntryBody => break, ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), }; match runtime.declare_data_initialization(memory_index as MemoryIndex, offset, data) { Ok(()) => (), Err(s) => return Err(SectionParsingError::WrongSectionContent(s)), }; offset += data.len(); } match *parser.read() { ParserState::EndDataSectionEntry => (), ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), }; } Ok(()) } /// Retrieves the tables from the table section pub fn parse_table_section( parser: &mut Parser, runtime: &mut WasmRuntime, ) -> Result<(), SectionParsingError> { loop { match *parser.read() { ParserState::TableSectionEntry(ref table) => { runtime.declare_table(Table { ty: match type_to_type(&table.element_type) { Ok(t) => TableElementType::Val(t), Err(()) => TableElementType::Func(), }, size: table.limits.initial as usize, maximum: table.limits.maximum.map(|x| x as usize), }) } ParserState::EndSection => break, ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), }; } Ok(()) } /// Retrieves the tables from the table section pub fn parse_elements_section( parser: &mut Parser, runtime: &mut WasmRuntime, ) -> Result<(), SectionParsingError> { loop { let table_index = match *parser.read() { ParserState::BeginElementSectionEntry(ref table_index) => *table_index as TableIndex, ParserState::EndSection => break, ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), }; match *parser.read() { ParserState::BeginInitExpressionBody => (), ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), }; let offset = match *parser.read() { ParserState::InitExpressionOperator(Operator::I32Const { value }) => { if value < 0 { return Err(SectionParsingError::WrongSectionContent(String::from( "negative \ offset value", ))); } else { value as usize } } ParserState::InitExpressionOperator(Operator::GetGlobal { global_index }) => { match runtime.get_global(global_index as GlobalIndex).initializer { GlobalInit::I32Const(value) => { if value < 0 { return Err(SectionParsingError::WrongSectionContent(String::from( "\ negative offset value", ))); } else { value as usize } } GlobalInit::Import() => 0, // TODO: add runtime support _ => panic!("should not happen"), } } ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), }; match *parser.read() { ParserState::EndInitExpressionBody => (), ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), }; match *parser.read() { ParserState::ElementSectionEntryBody(ref elements) => { let elems: Vec = elements.iter().map(|&x| x as FunctionIndex).collect(); runtime.declare_table_elements(table_index, offset, &elems) } ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), }; match *parser.read() { ParserState::EndElementSectionEntry => (), ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), }; } Ok(()) }