use crate::Result; use crate::{module::Code, visit::process_operators}; use alloc::{boxed::Box, format, string::ToString, vec::Vec}; use tinywasm_types::*; use wasmparser::{FuncValidator, OperatorsReader, ValidatorResources}; pub(crate) fn convert_module_elements<'a, T: IntoIterator>>>( elements: T, ) -> Result> { let elements = elements.into_iter().map(|element| convert_module_element(element?)).collect::>>()?; Ok(elements) } pub(crate) fn convert_module_element(element: wasmparser::Element<'_>) -> Result { let kind = match element.kind { wasmparser::ElementKind::Active { table_index, offset_expr } => tinywasm_types::ElementKind::Active { table: table_index.unwrap_or(0), offset: process_const_operators(offset_expr.get_operators_reader())?, }, wasmparser::ElementKind::Passive => tinywasm_types::ElementKind::Passive, wasmparser::ElementKind::Declared => tinywasm_types::ElementKind::Declared, }; match element.items { wasmparser::ElementItems::Functions(funcs) => { let items = funcs .into_iter() .map(|func| Ok(ElementItem::Func(func?))) .collect::>>()? .into_boxed_slice(); Ok(tinywasm_types::Element { kind, items, ty: ValType::RefFunc, range: element.range }) } wasmparser::ElementItems::Expressions(ty, exprs) => { let items = exprs .into_iter() .map(|expr| Ok(ElementItem::Expr(process_const_operators(expr?.get_operators_reader())?))) .collect::>>()? .into_boxed_slice(); Ok(tinywasm_types::Element { kind, items, ty: convert_reftype(&ty), range: element.range }) } } } pub(crate) fn convert_module_data_sections<'a, T: IntoIterator>>>( data_sections: T, ) -> Result> { let data_sections = data_sections.into_iter().map(|data| convert_module_data(data?)).collect::>>()?; Ok(data_sections) } pub(crate) fn convert_module_data(data: wasmparser::Data<'_>) -> Result { Ok(tinywasm_types::Data { data: data.data.to_vec().into_boxed_slice(), range: data.range, kind: match data.kind { wasmparser::DataKind::Active { memory_index, offset_expr } => { let offset = process_const_operators(offset_expr.get_operators_reader())?; tinywasm_types::DataKind::Active { mem: memory_index, offset } } wasmparser::DataKind::Passive => tinywasm_types::DataKind::Passive, }, }) } pub(crate) fn convert_module_imports<'a, T: IntoIterator>>>( imports: T, ) -> Result> { let imports = imports.into_iter().map(|import| convert_module_import(import?)).collect::>>()?; Ok(imports) } pub(crate) fn convert_module_import(import: wasmparser::Import<'_>) -> Result { Ok(Import { module: import.module.to_string().into_boxed_str(), name: import.name.to_string().into_boxed_str(), kind: match import.ty { wasmparser::TypeRef::Func(ty) => ImportKind::Function(ty), wasmparser::TypeRef::Table(ty) => ImportKind::Table(TableType { element_type: convert_reftype(&ty.element_type), size_initial: ty.initial.try_into().map_err(|_| { crate::ParseError::UnsupportedOperator(format!("Table size initial is too large: {}", ty.initial)) })?, size_max: match ty.maximum { Some(max) => Some(max.try_into().map_err(|_| { crate::ParseError::UnsupportedOperator(format!("Table size max is too large: {}", max)) })?), None => None, }, }), wasmparser::TypeRef::Memory(ty) => ImportKind::Memory(convert_module_memory(ty)?), wasmparser::TypeRef::Global(ty) => { ImportKind::Global(GlobalType { mutable: ty.mutable, ty: convert_valtype(&ty.content_type) }) } wasmparser::TypeRef::Tag(ty) => { return Err(crate::ParseError::UnsupportedOperator(format!("Unsupported import kind: {:?}", ty))) } }, }) } pub(crate) fn convert_module_memories>>( memory_types: T, ) -> Result> { memory_types.into_iter().map(|memory| convert_module_memory(memory?)).collect::>>() } pub(crate) fn convert_module_memory(memory: wasmparser::MemoryType) -> Result { Ok(MemoryType { arch: match memory.memory64 { true => MemoryArch::I64, false => MemoryArch::I32, }, page_count_initial: memory.initial, page_count_max: memory.maximum, }) } pub(crate) fn convert_module_tables<'a, T: IntoIterator>>>( table_types: T, ) -> Result> { table_types.into_iter().map(|table| convert_module_table(table?)).collect::>>() } pub(crate) fn convert_module_table(table: wasmparser::Table<'_>) -> Result { let size_initial = table.ty.initial.try_into().map_err(|_| { crate::ParseError::UnsupportedOperator(format!("Table size initial is too large: {}", table.ty.initial)) })?; let size_max = match table.ty.maximum { Some(max) => Some( max.try_into() .map_err(|_| crate::ParseError::UnsupportedOperator(format!("Table size max is too large: {}", max)))?, ), None => None, }; Ok(TableType { element_type: convert_reftype(&table.ty.element_type), size_initial, size_max }) } pub(crate) fn convert_module_globals<'a, T: IntoIterator>>>( globals: T, ) -> Result> { let globals = globals .into_iter() .map(|global| { let global = global?; let ty = convert_valtype(&global.ty.content_type); let ops = global.init_expr.get_operators_reader(); Ok(Global { init: process_const_operators(ops)?, ty: GlobalType { mutable: global.ty.mutable, ty } }) }) .collect::>>()?; Ok(globals) } pub(crate) fn convert_module_export(export: wasmparser::Export<'_>) -> Result { let kind = match export.kind { wasmparser::ExternalKind::Func => ExternalKind::Func, wasmparser::ExternalKind::Table => ExternalKind::Table, wasmparser::ExternalKind::Memory => ExternalKind::Memory, wasmparser::ExternalKind::Global => ExternalKind::Global, wasmparser::ExternalKind::Tag => { return Err(crate::ParseError::UnsupportedOperator(format!("Unsupported export kind: {:?}", export.kind))) } }; Ok(Export { index: export.index, name: Box::from(export.name), kind }) } pub(crate) fn convert_module_code( func: wasmparser::FunctionBody<'_>, mut validator: FuncValidator, ) -> Result { let locals_reader = func.get_locals_reader()?; let count = locals_reader.get_count(); let pos = locals_reader.original_position(); let mut locals = Vec::with_capacity(count as usize); for (i, local) in locals_reader.into_iter().enumerate() { let local = local?; validator.define_locals(pos + i, local.0, local.1)?; for _ in 0..local.0 { locals.push(convert_valtype(&local.1)); } } let body = process_operators(Some(&mut validator), &func)?; let locals = locals.into_boxed_slice(); Ok((body, locals)) } pub(crate) fn convert_module_type(ty: wasmparser::RecGroup) -> Result { let mut types = ty.types(); if types.len() != 1 { return Err(crate::ParseError::UnsupportedOperator( "Expected exactly one type in the type section".to_string(), )); } let ty = types.next().unwrap().unwrap_func(); let params = ty.params().iter().map(convert_valtype).collect::>().into_boxed_slice(); let results = ty.results().iter().map(convert_valtype).collect::>().into_boxed_slice(); Ok(FuncType { params, results }) } pub(crate) fn convert_blocktype(blocktype: wasmparser::BlockType) -> BlockArgs { match blocktype { wasmparser::BlockType::Empty => BlockArgs::Empty, wasmparser::BlockType::Type(ty) => BlockArgs::Type(convert_valtype(&ty)), wasmparser::BlockType::FuncType(ty) => BlockArgs::FuncType(ty), } } pub(crate) fn convert_reftype(reftype: &wasmparser::RefType) -> ValType { match reftype { _ if reftype.is_func_ref() => ValType::RefFunc, _ if reftype.is_extern_ref() => ValType::RefExtern, _ => unimplemented!("Unsupported reference type: {:?}", reftype), } } pub(crate) fn convert_valtype(valtype: &wasmparser::ValType) -> ValType { match valtype { wasmparser::ValType::I32 => ValType::I32, wasmparser::ValType::I64 => ValType::I64, wasmparser::ValType::F32 => ValType::F32, wasmparser::ValType::F64 => ValType::F64, wasmparser::ValType::Ref(r) => convert_reftype(r), wasmparser::ValType::V128 => unimplemented!("128-bit values are not supported yet"), } } pub(crate) fn convert_memarg(memarg: wasmparser::MemArg) -> MemoryArg { MemoryArg { offset: memarg.offset, mem_addr: memarg.memory } } pub(crate) fn process_const_operators(ops: OperatorsReader<'_>) -> Result { let ops = ops.into_iter().collect::>>()?; // In practice, the len can never be something other than 2, // but we'll keep this here since it's part of the spec // Invalid modules will be rejected by the validator anyway (there are also tests for this in the testsuite) assert!(ops.len() >= 2); assert!(matches!(ops[ops.len() - 1], wasmparser::Operator::End)); process_const_operator(ops[ops.len() - 2].clone()) } pub(crate) fn process_const_operator(op: wasmparser::Operator<'_>) -> Result { match op { wasmparser::Operator::RefNull { hty } => Ok(ConstInstruction::RefNull(convert_heaptype(hty))), wasmparser::Operator::RefFunc { function_index } => Ok(ConstInstruction::RefFunc(function_index)), wasmparser::Operator::I32Const { value } => Ok(ConstInstruction::I32Const(value)), wasmparser::Operator::I64Const { value } => Ok(ConstInstruction::I64Const(value)), wasmparser::Operator::F32Const { value } => Ok(ConstInstruction::F32Const(f32::from_bits(value.bits()))), wasmparser::Operator::F64Const { value } => Ok(ConstInstruction::F64Const(f64::from_bits(value.bits()))), wasmparser::Operator::GlobalGet { global_index } => Ok(ConstInstruction::GlobalGet(global_index)), op => Err(crate::ParseError::UnsupportedOperator(format!("Unsupported const instruction: {:?}", op))), } } pub(crate) fn convert_heaptype(heap: wasmparser::HeapType) -> ValType { match heap { wasmparser::HeapType::Func => ValType::RefFunc, wasmparser::HeapType::Extern => ValType::RefExtern, _ => unimplemented!("Unsupported heap type: {:?}", heap), } }