use std::sync::atomic::{AtomicBool, Ordering};
use crate::{FunctionBinaryReader, MiddlewareBinaryReader};
use super::environ::ModuleEnvironment;
use super::error::from_binaryreadererror_wasmerror;
use super::sections::{
parse_data_section, parse_element_section, parse_export_section, parse_function_section,
parse_global_section, parse_import_section, parse_memory_section, parse_name_section,
parse_start_section, parse_table_section, parse_tag_section, parse_type_section,
};
use super::state::ModuleTranslationState;
use itertools::Itertools;
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use wasmer_types::entity::PrimaryMap;
use wasmer_types::{
InitExpr, InitExprOp, LocalFunctionIndex, ModuleInfo, TableIndex, WasmError, WasmResult,
};
use wasmparser::{BinaryReader, NameSectionReader, Parser, Payload};
fn analyze_function_readonly_table(
function_body: &super::environ::FunctionBodyData<'_>,
table_index: TableIndex,
) -> WasmResult<bool> {
let mut reader =
MiddlewareBinaryReader::new_with_offset(function_body.data, function_body.module_offset);
let local_count = reader.read_local_count()?;
for _ in 0..local_count {
reader.read_local_decl()?;
}
while !reader.eof() {
match reader.read_operator()? {
wasmparser::Operator::TableSet { table }
| wasmparser::Operator::TableFill { table }
| wasmparser::Operator::TableGrow { table } => {
if TableIndex::from_u32(table) == table_index {
return Ok(false);
}
}
wasmparser::Operator::TableCopy { dst_table, .. } => {
if TableIndex::from_u32(dst_table) == table_index {
return Ok(false);
}
}
wasmparser::Operator::TableInit { table, .. } => {
if TableIndex::from_u32(table) == table_index {
return Ok(false);
}
}
wasmparser::Operator::ElemDrop { .. } => return Ok(false),
_ => {}
}
}
Ok(true)
}
pub(crate) fn analyze_readonly_funcref_table(
module: &ModuleInfo,
function_body_inputs: &PrimaryMap<LocalFunctionIndex, super::environ::FunctionBodyData<'_>>,
) -> WasmResult<Option<TableIndex>> {
let Ok(table_index) = module
.tables
.iter()
.filter_map(|(index, table)| {
if module.local_table_index(index).is_some() && table.is_fixed_funcref_table() {
Some(index)
} else {
None
}
})
.exactly_one()
else {
return Ok(None);
};
let table = module.tables[table_index];
let Ok(table_initializer) = module
.table_initializers
.iter()
.filter(|initializer| initializer.table_index == table_index)
.exactly_one()
else {
return Ok(None);
};
if table_initializer.offset_expr != InitExpr::new([InitExprOp::I32Const(1)])
|| table_initializer.elements.len() as u32 != table.minimum.saturating_sub(1)
{
return Ok(None);
}
if table_initializer
.elements
.iter()
.any(|func_index| module.is_imported_function(*func_index))
{
return Ok(None);
}
let readonly = AtomicBool::new(true);
function_body_inputs
.iter()
.collect_vec()
.par_iter()
.map(|(_local_func_index, function_body)| {
if !readonly.load(Ordering::Relaxed) {
return Ok(());
}
if !analyze_function_readonly_table(function_body, table_index)? {
readonly.store(false, Ordering::Relaxed);
}
Ok(())
})
.collect::<WasmResult<Vec<_>>>()?;
if !readonly.load(Ordering::Relaxed) {
return Ok(None);
}
Ok(Some(table_index))
}
pub fn translate_module<'data>(
data: &'data [u8],
environ: &mut ModuleEnvironment<'data>,
) -> WasmResult<ModuleTranslationState> {
let mut module_translation_state = ModuleTranslationState::new();
for payload in Parser::new(0).parse_all(data) {
match payload.map_err(from_binaryreadererror_wasmerror)? {
Payload::Version { .. } | Payload::End { .. } => {}
Payload::TypeSection(types) => {
parse_type_section(types, &mut module_translation_state, environ)?;
}
Payload::ImportSection(imports) => {
parse_import_section(imports, environ)?;
}
Payload::FunctionSection(functions) => {
parse_function_section(functions, environ)?;
}
Payload::TableSection(tables) => {
parse_table_section(tables, environ)?;
}
Payload::MemorySection(memories) => {
parse_memory_section(memories, environ)?;
}
Payload::GlobalSection(globals) => {
parse_global_section(globals, environ)?;
}
Payload::ExportSection(exports) => {
parse_export_section(exports, environ)?;
}
Payload::StartSection { func, .. } => {
parse_start_section(func, environ)?;
}
Payload::ElementSection(elements) => {
parse_element_section(elements, environ)?;
}
Payload::CodeSectionStart { .. } => {}
Payload::CodeSectionEntry(code) => {
let mut code = code.get_binary_reader();
let size = code.bytes_remaining();
let offset = code.original_position();
environ.define_function_body(
&module_translation_state,
code.read_bytes(size)
.map_err(from_binaryreadererror_wasmerror)?,
offset,
)?;
}
Payload::DataSection(data) => {
parse_data_section(data, environ)?;
}
Payload::DataCountSection { count, .. } => {
environ.reserve_passive_data(count)?;
}
Payload::TagSection(t) => parse_tag_section(t, environ)?,
Payload::CustomSection(sectionreader) => {
let name = sectionreader.name();
environ.custom_section(name, sectionreader.data())?;
if name == "name" {
parse_name_section(
NameSectionReader::new(BinaryReader::new(
sectionreader.data(),
sectionreader.data_offset(),
)),
environ,
)?;
}
}
Payload::UnknownSection { .. } => unreachable!(),
k => {
return Err(WasmError::Unsupported(format!(
"Unsupported paylod kind: {k:?}"
)));
}
}
}
Ok(module_translation_state)
}