Files
wasmtime/lib/wasm/src/sections_translator.rs
Dan Gohman 8cd1b87917 Rename parse_elements_section.
In WebAssembly documentation, it's the "element" section.
2018-10-19 10:59:32 -07:00

446 lines
18 KiB
Rust

//! 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 cranelift_codegen::ir::{self, AbiParam, Signature};
use cranelift_entity::EntityRef;
use environ::{ModuleEnvironment, WasmError, WasmResult};
use std::str::from_utf8;
use std::vec::Vec;
use translation_utils::{
type_to_type, FuncIndex, Global, GlobalIndex, GlobalInit, Memory, MemoryIndex, SignatureIndex,
Table, TableElementType, TableIndex,
};
use wasmparser;
use wasmparser::{
ExternalKind, FuncType, ImportSectionEntryType, MemoryType, Operator, Parser, ParserState,
WasmDecoder,
};
/// Reads the Type Section of the wasm module and returns the corresponding function signatures.
pub fn parse_function_signatures(
parser: &mut Parser,
environ: &mut ModuleEnvironment,
) -> WasmResult<()> {
loop {
match *parser.read() {
ParserState::EndSection => break,
ParserState::TypeSectionEntry(FuncType {
form: wasmparser::Type::Func,
ref params,
ref returns,
}) => {
let mut sig = Signature::new(environ.flags().call_conv());
sig.params.extend(params.iter().map(|ty| {
let cret_arg: ir::Type = type_to_type(*ty)
.expect("only numeric types are supported in function signatures");
AbiParam::new(cret_arg)
}));
sig.returns.extend(returns.iter().map(|ty| {
let cret_arg: ir::Type = type_to_type(*ty)
.expect("only numeric types are supported in function signatures");
AbiParam::new(cret_arg)
}));
environ.declare_signature(&sig);
}
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
}
}
Ok(())
}
/// Retrieves the imports from the imports section of the binary.
pub fn parse_import_section<'data>(
parser: &mut Parser<'data>,
environ: &mut ModuleEnvironment<'data>,
) -> WasmResult<()> {
loop {
match *parser.read() {
ParserState::ImportSectionEntry {
ty: ImportSectionEntryType::Function(sig),
module,
field,
} => {
// The input has already been validated, so we should be able to
// assume valid UTF-8 and use `from_utf8_unchecked` if performance
// becomes a concern here.
let module_name = from_utf8(module).unwrap();
let field_name = from_utf8(field).unwrap();
environ.declare_func_import(
SignatureIndex::new(sig as usize),
module_name,
field_name,
);
}
ParserState::ImportSectionEntry {
ty:
ImportSectionEntryType::Memory(MemoryType {
limits: ref memlimits,
shared,
}),
..
} => {
environ.declare_memory(Memory {
pages_count: memlimits.initial as usize,
maximum: memlimits.maximum.map(|x| x as usize),
shared,
});
}
ParserState::ImportSectionEntry {
ty: ImportSectionEntryType::Global(ref ty),
..
} => {
environ.declare_global(Global {
ty: type_to_type(ty.content_type).unwrap(),
mutability: ty.mutable,
initializer: GlobalInit::Import(),
});
}
ParserState::ImportSectionEntry {
ty: ImportSectionEntryType::Table(ref tab),
..
} => environ.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,
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
};
}
Ok(())
}
/// Retrieves the correspondences between functions and signatures from the function section
pub fn parse_function_section(
parser: &mut Parser,
environ: &mut ModuleEnvironment,
) -> WasmResult<()> {
loop {
match *parser.read() {
ParserState::FunctionSectionEntry(sigindex) => {
environ.declare_func_type(SignatureIndex::new(sigindex as usize));
}
ParserState::EndSection => break,
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
};
}
Ok(())
}
/// Retrieves the names of the functions from the export section
pub fn parse_export_section<'data>(
parser: &mut Parser<'data>,
environ: &mut ModuleEnvironment<'data>,
) -> WasmResult<()> {
loop {
match *parser.read() {
ParserState::ExportSectionEntry {
field,
ref kind,
index,
} => {
// The input has already been validated, so we should be able to
// assume valid UTF-8 and use `from_utf8_unchecked` if performance
// becomes a concern here.
let name = from_utf8(field).unwrap();
let index = index as usize;
match *kind {
ExternalKind::Function => {
environ.declare_func_export(FuncIndex::new(index), name)
}
ExternalKind::Table => {
environ.declare_table_export(TableIndex::new(index), name)
}
ExternalKind::Memory => {
environ.declare_memory_export(MemoryIndex::new(index), name)
}
ExternalKind::Global => {
environ.declare_global_export(GlobalIndex::new(index), name)
}
}
}
ParserState::EndSection => break,
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
};
}
Ok(())
}
/// Retrieves the start function index from the start section
pub fn parse_start_section(parser: &mut Parser, environ: &mut ModuleEnvironment) -> WasmResult<()> {
loop {
match *parser.read() {
ParserState::StartSectionEntry(index) => {
environ.declare_start_func(FuncIndex::new(index as usize));
}
ParserState::EndSection => break,
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
};
}
Ok(())
}
/// Retrieves the size and maximum fields of memories from the memory section
pub fn parse_memory_section(
parser: &mut Parser,
environ: &mut ModuleEnvironment,
) -> WasmResult<()> {
loop {
match *parser.read() {
ParserState::MemorySectionEntry(ref ty) => {
environ.declare_memory(Memory {
pages_count: ty.limits.initial as usize,
maximum: ty.limits.maximum.map(|x| x as usize),
shared: ty.shared,
});
}
ParserState::EndSection => break,
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
};
}
Ok(())
}
/// Retrieves the size and maximum fields of memories from the memory section
pub fn parse_global_section(
parser: &mut Parser,
environ: &mut ModuleEnvironment,
) -> WasmResult<()> {
loop {
let (content_type, mutability) = match *parser.read() {
ParserState::BeginGlobalSectionEntry(ref ty) => (ty.content_type, ty.mutable),
ParserState::EndSection => break,
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
};
match *parser.read() {
ParserState::BeginInitExpressionBody => (),
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", 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(GlobalIndex::new(global_index as usize))
}
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
};
match *parser.read() {
ParserState::EndInitExpressionBody => (),
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
}
let global = Global {
ty: type_to_type(content_type).unwrap(),
mutability,
initializer,
};
environ.declare_global(global);
match *parser.read() {
ParserState::EndGlobalSectionEntry => (),
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
}
}
Ok(())
}
pub fn parse_data_section<'data>(
parser: &mut Parser<'data>,
environ: &mut ModuleEnvironment<'data>,
) -> WasmResult<()> {
loop {
let memory_index = match *parser.read() {
ParserState::BeginDataSectionEntry(memory_index) => memory_index,
ParserState::EndSection => break,
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
};
match *parser.read() {
ParserState::BeginInitExpressionBody => (),
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
};
let (base, offset) = match *parser.read() {
ParserState::InitExpressionOperator(Operator::I32Const { value }) => {
(None, value as u32 as usize)
}
ParserState::InitExpressionOperator(Operator::GetGlobal { global_index }) => {
match environ
.get_global(GlobalIndex::new(global_index as usize))
.initializer
{
GlobalInit::I32Const(value) => (None, value as u32 as usize),
GlobalInit::Import() => (Some(GlobalIndex::new(global_index as usize)), 0),
_ => panic!("should not happen"),
}
}
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
};
match *parser.read() {
ParserState::EndInitExpressionBody => (),
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
};
match *parser.read() {
ParserState::BeginDataSectionEntryBody(_) => (),
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
};
let mut running_offset = offset;
loop {
let data = match *parser.read() {
ParserState::DataSectionEntryBodyChunk(data) => data,
ParserState::EndDataSectionEntryBody => break,
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
};
environ.declare_data_initialization(
MemoryIndex::new(memory_index as usize),
base,
running_offset,
data,
);
running_offset += data.len();
}
match *parser.read() {
ParserState::EndDataSectionEntry => (),
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
};
}
Ok(())
}
/// Retrieves the tables from the table section
pub fn parse_table_section(parser: &mut Parser, environ: &mut ModuleEnvironment) -> WasmResult<()> {
loop {
match *parser.read() {
ParserState::TableSectionEntry(ref table) => environ.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,
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
};
}
Ok(())
}
/// Retrieves the elements from the element section
pub fn parse_element_section(
parser: &mut Parser,
environ: &mut ModuleEnvironment,
) -> WasmResult<()> {
loop {
let table_index = match *parser.read() {
ParserState::BeginElementSectionEntry(table_index) => {
TableIndex::new(table_index as usize)
}
ParserState::EndSection => break,
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
};
match *parser.read() {
ParserState::BeginInitExpressionBody => (),
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
};
let (base, offset) = match *parser.read() {
ParserState::InitExpressionOperator(Operator::I32Const { value }) => {
(None, value as u32 as usize)
}
ParserState::InitExpressionOperator(Operator::GetGlobal { global_index }) => {
match environ
.get_global(GlobalIndex::new(global_index as usize))
.initializer
{
GlobalInit::I32Const(value) => (None, value as u32 as usize),
GlobalInit::Import() => (Some(GlobalIndex::new(global_index as usize)), 0),
_ => panic!("should not happen"),
}
}
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
};
match *parser.read() {
ParserState::EndInitExpressionBody => (),
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
};
match *parser.read() {
ParserState::ElementSectionEntryBody(ref elements) => {
let elems: Vec<FuncIndex> = elements
.iter()
.map(|&x| FuncIndex::new(x as usize))
.collect();
environ.declare_table_elements(table_index, base, offset, elems)
}
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
};
match *parser.read() {
ParserState::EndElementSectionEntry => (),
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("unexpected section content: {:?}", s),
};
}
Ok(())
}
/// Parses every function body in the code section and defines the corresponding function.
pub fn parse_code_section<'data>(
parser: &mut Parser<'data>,
environ: &mut ModuleEnvironment<'data>,
) -> WasmResult<()> {
loop {
match *parser.read() {
ParserState::BeginFunctionBody { .. } => {}
ParserState::EndSection => break,
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
ref s => panic!("wrong content in code section: {:?}", s),
}
let mut reader = parser.create_binary_reader();
let size = reader.bytes_remaining();
environ.define_function_body(
reader
.read_bytes(size)
.map_err(WasmError::from_binary_reader_error)?,
)?;
}
Ok(())
}