use alloc::{string::String, vec::Vec};
use crate::io;
use super::{Deserialize, Error, Module, Serialize, VarUint32, VarUint7, Type};
use super::index_map::IndexMap;
const NAME_TYPE_MODULE: u8 = 0;
const NAME_TYPE_FUNCTION: u8 = 1;
const NAME_TYPE_LOCAL: u8 = 2;
#[derive(Clone, Debug, PartialEq)]
pub struct NameSection {
module: Option<ModuleNameSubsection>,
functions: Option<FunctionNameSubsection>,
locals: Option<LocalNameSubsection>,
}
impl NameSection {
pub fn new(module: Option<ModuleNameSubsection>,
functions: Option<FunctionNameSubsection>,
locals: Option<LocalNameSubsection>) -> Self {
Self {
module,
functions,
locals,
}
}
pub fn module(&self) -> Option<&ModuleNameSubsection> {
self.module.as_ref()
}
pub fn module_mut(&mut self) -> &mut Option<ModuleNameSubsection> {
&mut self.module
}
pub fn functions(&self) -> Option<&FunctionNameSubsection> {
self.functions.as_ref()
}
pub fn functions_mut(&mut self) -> &mut Option<FunctionNameSubsection> {
&mut self.functions
}
pub fn locals(&self) -> Option<&LocalNameSubsection> {
self.locals.as_ref()
}
pub fn locals_mut(&mut self) -> &mut Option<LocalNameSubsection> {
&mut self.locals
}
}
impl NameSection {
pub fn deserialize<R: io::Read>(
module: &Module,
rdr: &mut R,
) -> Result<Self, Error> {
let mut module_name: Option<ModuleNameSubsection> = None;
let mut function_names: Option<FunctionNameSubsection> = None;
let mut local_names: Option<LocalNameSubsection> = None;
loop {
let subsection_type: u8 = match VarUint7::deserialize(rdr) {
Ok(raw_subsection_type) => raw_subsection_type.into(),
Err(_) => { break; },
};
VarUint32::deserialize(rdr)?;
match subsection_type {
NAME_TYPE_MODULE => {
if let Some(_) = module_name {
return Err(Error::DuplicatedNameSubsections(NAME_TYPE_FUNCTION));
}
module_name = Some(ModuleNameSubsection::deserialize(rdr)?);
},
NAME_TYPE_FUNCTION => {
if let Some(_) = function_names {
return Err(Error::DuplicatedNameSubsections(NAME_TYPE_FUNCTION));
}
function_names = Some(FunctionNameSubsection::deserialize(module, rdr)?);
},
NAME_TYPE_LOCAL => {
if let Some(_) = local_names {
return Err(Error::DuplicatedNameSubsections(NAME_TYPE_LOCAL));
}
local_names = Some(LocalNameSubsection::deserialize(module, rdr)?);
},
_ => return Err(Error::UnknownNameSubsectionType(subsection_type))
};
}
Ok(Self {
module: module_name,
functions: function_names,
locals: local_names,
})
}
}
impl Serialize for NameSection {
type Error = Error;
fn serialize<W: io::Write>(self, wtr: &mut W) -> Result<(), Error> {
fn serialize_subsection<W: io::Write>(wtr: &mut W, name_type: u8, name_payload: &Vec<u8>) -> Result<(), Error> {
VarUint7::from(name_type).serialize(wtr)?;
VarUint32::from(name_payload.len()).serialize(wtr)?;
wtr.write(name_payload).map_err(Into::into)
}
if let Some(module_name_subsection) = self.module {
let mut buffer = vec![];
module_name_subsection.serialize(&mut buffer)?;
serialize_subsection(wtr, NAME_TYPE_MODULE, &buffer)?;
}
if let Some(function_name_subsection) = self.functions {
let mut buffer = vec![];
function_name_subsection.serialize(&mut buffer)?;
serialize_subsection(wtr, NAME_TYPE_FUNCTION, &buffer)?;
}
if let Some(local_name_subsection) = self.locals {
let mut buffer = vec![];
local_name_subsection.serialize(&mut buffer)?;
serialize_subsection(wtr, NAME_TYPE_LOCAL, &buffer)?;
}
Ok(())
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct ModuleNameSubsection {
name: String,
}
impl ModuleNameSubsection {
pub fn new<S: Into<String>>(name: S) -> ModuleNameSubsection {
ModuleNameSubsection { name: name.into() }
}
pub fn name(&self) -> &str {
&self.name
}
pub fn name_mut(&mut self) -> &mut String {
&mut self.name
}
}
impl Serialize for ModuleNameSubsection {
type Error = Error;
fn serialize<W: io::Write>(self, wtr: &mut W) -> Result<(), Error> {
self.name.serialize(wtr)
}
}
impl Deserialize for ModuleNameSubsection {
type Error = Error;
fn deserialize<R: io::Read>(rdr: &mut R) -> Result<ModuleNameSubsection, Error> {
let name = String::deserialize(rdr)?;
Ok(ModuleNameSubsection { name })
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct FunctionNameSubsection {
names: NameMap,
}
impl FunctionNameSubsection {
pub fn names(&self) -> &NameMap {
&self.names
}
pub fn names_mut(&mut self) -> &mut NameMap {
&mut self.names
}
pub fn deserialize<R: io::Read>(
module: &Module,
rdr: &mut R,
) -> Result<FunctionNameSubsection, Error> {
let names = IndexMap::deserialize(module.functions_space(), rdr)?;
Ok(FunctionNameSubsection { names })
}
}
impl Serialize for FunctionNameSubsection {
type Error = Error;
fn serialize<W: io::Write>(self, wtr: &mut W) -> Result<(), Error> {
self.names.serialize(wtr)
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct LocalNameSubsection {
local_names: IndexMap<NameMap>,
}
impl LocalNameSubsection {
pub fn local_names(&self) -> &IndexMap<NameMap> {
&self.local_names
}
pub fn local_names_mut(&mut self) -> &mut IndexMap<NameMap> {
&mut self.local_names
}
pub fn deserialize<R: io::Read>(
module: &Module,
rdr: &mut R,
) -> Result<LocalNameSubsection, Error> {
let max_entry_space = module.functions_space();
let max_signature_args = module
.type_section()
.map(|ts|
ts.types()
.iter()
.map(|x| { let Type::Function(ref func) = *x; func.params().len() })
.max()
.unwrap_or(0))
.unwrap_or(0);
let max_locals = module
.code_section()
.map(|cs| cs.bodies().iter().map(|f| f.locals().len()).max().unwrap_or(0))
.unwrap_or(0);
let max_space = max_signature_args + max_locals;
let deserialize_locals = |_: u32, rdr: &mut R| IndexMap::deserialize(max_space, rdr);
let local_names = IndexMap::deserialize_with(
max_entry_space,
&deserialize_locals,
rdr,
)?;
Ok(LocalNameSubsection { local_names })
}}
impl Serialize for LocalNameSubsection {
type Error = Error;
fn serialize<W: io::Write>(self, wtr: &mut W) -> Result<(), Error> {
self.local_names.serialize(wtr)
}
}
pub type NameMap = IndexMap<String>;
#[cfg(test)]
mod tests {
use super::*;
fn serialize_test(original: NameSection) -> Vec<u8> {
let mut buffer = vec![];
original
.serialize(&mut buffer)
.expect("serialize error");
buffer
}
#[test]
fn serialize_module_name() {
let module_name_subsection = ModuleNameSubsection::new("my_mod");
let original = NameSection::new(Some(module_name_subsection), None, None);
serialize_test(original.clone());
}
#[test]
fn serialize_function_names() {
let mut function_name_subsection = FunctionNameSubsection::default();
function_name_subsection.names_mut().insert(0, "hello_world".to_string());
let name_section = NameSection::new(None, Some(function_name_subsection), None);
serialize_test(name_section);
}
#[test]
fn serialize_local_names() {
let mut local_name_subsection = LocalNameSubsection::default();
let mut locals = NameMap::default();
locals.insert(0, "msg".to_string());
local_name_subsection.local_names_mut().insert(0, locals);
let name_section = NameSection::new(None, None, Some(local_name_subsection));
serialize_test(name_section);
}
#[test]
fn serialize_all_subsections() {
let module_name_subsection = ModuleNameSubsection::new("ModuleNameSubsection");
let mut function_name_subsection = FunctionNameSubsection::default();
function_name_subsection.names_mut().insert(0, "foo".to_string());
function_name_subsection.names_mut().insert(1, "bar".to_string());
let mut local_name_subsection = LocalNameSubsection::default();
let mut locals = NameMap::default();
locals.insert(0, "msg1".to_string());
locals.insert(1, "msg2".to_string());
local_name_subsection.local_names_mut().insert(0, locals);
let name_section = NameSection::new(Some(module_name_subsection), Some(function_name_subsection), Some(local_name_subsection));
serialize_test(name_section);
}
#[test]
fn deserialize_local_names() {
let module = super::super::deserialize_file("./res/cases/v1/names_with_imports.wasm")
.expect("Should be deserialized")
.parse_names()
.expect("Names to be parsed");
let name_section = module.names_section().expect("name_section should be present");
let local_names = name_section.locals().expect("local_name_section should be present");
let locals = local_names.local_names().get(0).expect("entry #0 should be present");
assert_eq!(
locals.get(0).expect("entry #0 should be present"),
"abc"
);
let locals = local_names.local_names().get(1).expect("entry #1 should be present");
assert_eq!(
locals.get(0).expect("entry #0 should be present"),
"def"
);
}
}