///| The standard `name` custom section is optional metadata. This decoder
///| handles the module-name and function-name subsections and deliberately
///|
/// preserves unknown subsections so new producers do not break inspection.
pub struct FunctionName {
index : Int
name : String
}
///|
pub struct NameSection {
module_name : String?
function_names : Array[FunctionName]
unknown_subsection_ids : Array[Int]
}
///|
pub fn NameSection::function_name(self : NameSection, index : Int) -> String? {
for item in self.function_names {
if item.index == index {
return Some(item.name)
}
}
None
}
///|
pub fn NameSection::render(self : NameSection) -> String {
let mut text = match self.module_name {
Some(name) => "module name: " + name + "\n"
None => "module name: \n"
}
text = text +
"function names: " +
self.function_names.length().to_string() +
"\n"
for item in self.function_names {
text = text + " [" + item.index.to_string() + "] " + item.name + "\n"
}
if self.unknown_subsection_ids.length() > 0 {
text = text +
"unknown name subsections: " +
self.unknown_subsection_ids.length().to_string() +
"\n"
}
text
}
///|
fn decode_function_names(
cursor : Cursor,
) -> Result[Array[FunctionName], DecodeError] {
let count = match cursor.read_var_u32() {
Ok(value) => value
Err(error) => return Err(error)
}
let names : Array[FunctionName] = []
for _ in 0.. value
Err(error) => return Err(error)
}
let name = match cursor.read_name() {
Ok(value) => value
Err(error) => return Err(error)
}
for prior in names {
if prior.index == index {
return Err(
ValidationError(
cursor.position,
"function name map contains duplicate index",
),
)
}
}
names.push({ index, name })
}
Ok(names)
}
///|
pub fn decode_name_section(
binary : Module,
) -> Result[NameSection?, DecodeError] {
let candidates = binary.custom_sections("name")
if candidates.length() == 0 {
return Ok(None)
}
if candidates.length() > 1 {
return Err(
ValidationError(
candidates[1].full.start,
"module contains more than one name custom section",
),
)
}
let section = candidates[0]
let outer = Cursor::with_bounds(
binary.bytes,
section.payload.start,
section.payload.end,
)
match outer.read_name() {
Ok(_) => ()
Err(error) => return Err(error)
}
let mut module_name : String? = None
let function_names : Array[FunctionName] = []
let unknown : Array[Int] = []
while !outer.is_finished() {
let id = match outer.read_u8() {
Ok(value) => value
Err(error) => return Err(error)
}
let size = match outer.read_var_u32() {
Ok(value) => value
Err(error) => return Err(error)
}
let payload = match outer.subcursor(size) {
Ok(value) => value
Err(_) =>
return Err(
ValidationError(
outer.position,
"name subsection extends past custom section",
),
)
}
match id {
0 => {
if module_name is Some(_) {
return Err(
ValidationError(
payload.position,
"duplicate module-name subsection",
),
)
}
module_name = match payload.read_name() {
Ok(value) => Some(value)
Err(error) => return Err(error)
}
if !payload.is_finished() {
return Err(
ValidationError(
payload.position,
"trailing bytes in module-name subsection",
),
)
}
}
1 => {
let decoded = match decode_function_names(payload) {
Ok(value) => value
Err(error) => return Err(error)
}
for item in decoded {
function_names.push(item)
}
if !payload.is_finished() {
return Err(
ValidationError(
payload.position,
"trailing bytes in function-name subsection",
),
)
}
}
_ => unknown.push(id)
}
}
Ok(Some({ module_name, function_names, unknown_subsection_ids: unknown }))
}