* dev-test: update dependencies * update wasmparser: 0.99.0 -> 0.101.1 Remove `i64` generation fix for elements as they only contain `reftype` and not `i64` * update wasmparser: 0.101.1 -> 0.103.0 See <https://github.com/bytecodealliance/wasm-tools/pull/957> for more information * update wasmparser: 0.103.0 -> 0.106.0 * update wasmparser: 0.106.0 -> 0.107.0
283 lines
6.6 KiB
Rust
283 lines
6.6 KiB
Rust
use std::collections::HashMap;
|
|
|
|
use wasmparser::{
|
|
BlockType, Data, Element, Export, ExternalKind, FunctionBody, Global, Import, LocalsReader,
|
|
MemoryType, Name, NameSectionReader, Parser, Payload, Result, Table, Type, TypeRef, ValType,
|
|
};
|
|
|
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
|
pub enum External {
|
|
Func,
|
|
Table,
|
|
Memory,
|
|
Global,
|
|
Tag,
|
|
}
|
|
|
|
impl From<TypeRef> for External {
|
|
fn from(value: TypeRef) -> Self {
|
|
match value {
|
|
TypeRef::Func(_) => Self::Func,
|
|
TypeRef::Table(_) => Self::Table,
|
|
TypeRef::Memory(_) => Self::Memory,
|
|
TypeRef::Global(_) => Self::Global,
|
|
TypeRef::Tag(_) => Self::Tag,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<ExternalKind> for External {
|
|
fn from(value: ExternalKind) -> Self {
|
|
match value {
|
|
ExternalKind::Func => Self::Func,
|
|
ExternalKind::Table => Self::Table,
|
|
ExternalKind::Memory => Self::Memory,
|
|
ExternalKind::Global => Self::Global,
|
|
ExternalKind::Tag => Self::Tag,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn read_checked<T, I>(reader: I) -> Result<Vec<T>>
|
|
where
|
|
I: IntoIterator<Item = Result<T>>,
|
|
{
|
|
reader.into_iter().collect()
|
|
}
|
|
|
|
pub(crate) fn read_checked_locals(reader: LocalsReader) -> Result<Vec<ValType>> {
|
|
read_checked(reader).map(|locals| {
|
|
let convert = |(a, b)| std::iter::repeat(b).take(usize::try_from(a).unwrap());
|
|
|
|
locals.into_iter().flat_map(convert).collect()
|
|
})
|
|
}
|
|
|
|
pub struct Module<'a> {
|
|
type_section: Vec<Type>,
|
|
import_section: Vec<Import<'a>>,
|
|
func_section: Vec<u32>,
|
|
table_section: Vec<Table<'a>>,
|
|
memory_section: Vec<MemoryType>,
|
|
global_section: Vec<Global<'a>>,
|
|
export_section: Vec<Export<'a>>,
|
|
element_section: Vec<Element<'a>>,
|
|
data_section: Vec<Data<'a>>,
|
|
code_section: Vec<FunctionBody<'a>>,
|
|
|
|
name_section: HashMap<u32, &'a str>,
|
|
|
|
start_section: Option<u32>,
|
|
}
|
|
|
|
impl<'a> Module<'a> {
|
|
/// # Errors
|
|
///
|
|
/// Returns a `BinaryReaderError` if any module section is malformed.
|
|
pub fn try_from_data(data: &'a [u8]) -> Result<Self> {
|
|
let mut temp = Module {
|
|
type_section: Vec::new(),
|
|
import_section: Vec::new(),
|
|
func_section: Vec::new(),
|
|
table_section: Vec::new(),
|
|
memory_section: Vec::new(),
|
|
global_section: Vec::new(),
|
|
export_section: Vec::new(),
|
|
element_section: Vec::new(),
|
|
data_section: Vec::new(),
|
|
code_section: Vec::new(),
|
|
name_section: HashMap::new(),
|
|
start_section: None,
|
|
};
|
|
|
|
temp.load_data(data)?;
|
|
Ok(temp)
|
|
}
|
|
|
|
fn load_data(&mut self, data: &'a [u8]) -> Result<()> {
|
|
for payload in Parser::new(0).parse_all(data) {
|
|
match payload? {
|
|
Payload::TypeSection(v) => self.type_section = read_checked(v)?,
|
|
Payload::ImportSection(v) => self.import_section = read_checked(v)?,
|
|
Payload::FunctionSection(v) => self.func_section = read_checked(v)?,
|
|
Payload::TableSection(v) => self.table_section = read_checked(v)?,
|
|
Payload::MemorySection(v) => self.memory_section = read_checked(v)?,
|
|
Payload::GlobalSection(v) => self.global_section = read_checked(v)?,
|
|
Payload::ExportSection(v) => self.export_section = read_checked(v)?,
|
|
Payload::ElementSection(v) => self.element_section = read_checked(v)?,
|
|
Payload::DataSection(v) => self.data_section = read_checked(v)?,
|
|
Payload::CodeSectionEntry(v) => {
|
|
self.code_section.push(v);
|
|
}
|
|
Payload::StartSection { func, .. } => {
|
|
self.start_section = Some(func);
|
|
}
|
|
Payload::CustomSection(v) if v.name() == "name" => {
|
|
for name in NameSectionReader::new(v.data(), v.data_offset()) {
|
|
if let Name::Function(map) = name? {
|
|
let mut iter = map.into_iter();
|
|
while let Some(Ok(elem)) = iter.next() {
|
|
self.name_section.insert(elem.index, elem.name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn import_count(&self, ext: External) -> usize {
|
|
let predicate = |v: &&Import| External::from(v.ty) == ext;
|
|
|
|
self.import_section.iter().filter(predicate).count()
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn function_space(&self) -> usize {
|
|
self.import_count(External::Func) + self.func_section.len()
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn table_space(&self) -> usize {
|
|
self.import_count(External::Table) + self.table_section.len()
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn memory_space(&self) -> usize {
|
|
self.import_count(External::Memory) + self.memory_section.len()
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn global_space(&self) -> usize {
|
|
self.import_count(External::Global) + self.global_section.len()
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn type_section(&self) -> &[Type] {
|
|
&self.type_section
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn import_section(&self) -> &[Import] {
|
|
&self.import_section
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn func_section(&self) -> &[u32] {
|
|
&self.func_section
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn table_section(&self) -> &[Table] {
|
|
&self.table_section
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn memory_section(&self) -> &[MemoryType] {
|
|
&self.memory_section
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn global_section(&self) -> &[Global] {
|
|
&self.global_section
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn export_section(&self) -> &[Export] {
|
|
&self.export_section
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn element_section(&self) -> &[Element] {
|
|
&self.element_section
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn data_section(&self) -> &[Data] {
|
|
&self.data_section
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn code_section(&self) -> &[FunctionBody] {
|
|
&self.code_section
|
|
}
|
|
|
|
#[must_use]
|
|
pub const fn name_section(&self) -> &HashMap<u32, &'a str> {
|
|
&self.name_section
|
|
}
|
|
|
|
#[must_use]
|
|
pub const fn start_section(&self) -> Option<u32> {
|
|
self.start_section
|
|
}
|
|
}
|
|
|
|
pub struct TypeInfo<'a> {
|
|
type_list: &'a [Type],
|
|
func_list: Vec<usize>,
|
|
}
|
|
|
|
impl<'a> TypeInfo<'a> {
|
|
#[must_use]
|
|
pub fn from_module(wasm: &'a Module) -> Self {
|
|
let mut temp = Self {
|
|
type_list: &wasm.type_section,
|
|
func_list: Vec::new(),
|
|
};
|
|
|
|
temp.load_import_list(&wasm.import_section);
|
|
temp.load_func_list(&wasm.func_section);
|
|
temp
|
|
}
|
|
|
|
fn load_import_list(&mut self, list: &[Import]) {
|
|
let iter = list
|
|
.iter()
|
|
.copied()
|
|
.filter_map(|v| match v.ty {
|
|
TypeRef::Func(v) => Some(v),
|
|
_ => None,
|
|
})
|
|
.map(|v| usize::try_from(v).unwrap());
|
|
|
|
self.func_list.extend(iter);
|
|
}
|
|
|
|
fn load_func_list(&mut self, list: &[u32]) {
|
|
let iter = list.iter().copied().map(|v| usize::try_from(v).unwrap());
|
|
|
|
self.func_list.extend(iter);
|
|
}
|
|
|
|
pub(crate) fn by_type_index(&self, index: usize) -> (usize, usize) {
|
|
let Type::Func(ty) = &self.type_list[index] else {
|
|
unreachable!("type at func index must be a func type");
|
|
};
|
|
|
|
(ty.params().len(), ty.results().len())
|
|
}
|
|
|
|
pub(crate) fn by_func_index(&self, index: usize) -> (usize, usize) {
|
|
let adjusted = self.func_list[index];
|
|
|
|
self.by_type_index(adjusted)
|
|
}
|
|
|
|
pub(crate) fn by_block_type(&self, ty: BlockType) -> (usize, usize) {
|
|
match ty {
|
|
BlockType::Empty => (0, 0),
|
|
BlockType::Type(_) => (0, 1),
|
|
BlockType::FuncType(i) => {
|
|
let id = i.try_into().unwrap();
|
|
|
|
self.by_type_index(id)
|
|
}
|
|
}
|
|
}
|
|
}
|