use anyhow::Context;
use serde::{Deserialize, Serialize};
use wasmparser::{ExternalKind, Parser, Payload};
use crate::UsedExtension;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ProducerValue {
pub name: String,
pub version: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ProducerField {
pub name: String,
pub values: Vec<ProducerValue>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModuleInfo {
pub cel_source: Option<String>,
pub vap_source: Option<String>,
pub extensions: Vec<UsedExtension>,
pub producers: Vec<ProducerField>,
pub exports: Vec<String>,
}
pub fn inspect(wasm: &[u8]) -> Result<ModuleInfo, anyhow::Error> {
let mut cel_source: Option<String> = None;
let mut vap_source: Option<String> = None;
let mut extensions: Vec<UsedExtension> = Vec::new();
let mut producers: Vec<ProducerField> = Vec::new();
let mut exports: Vec<String> = Vec::new();
for payload in Parser::new(0).parse_all(wasm) {
let payload = payload.context("Failed to parse Wasm module")?;
match payload {
Payload::CustomSection(reader) => {
let name = reader.name();
let data = reader.data();
match name {
"ferricel.cel-source" => {
cel_source = Some(
String::from_utf8(data.to_vec())
.context("ferricel.cel-source is not valid UTF-8")?,
);
}
"ferricel.vap-source" => {
vap_source = Some(
String::from_utf8(data.to_vec())
.context("ferricel.vap-source is not valid UTF-8")?,
);
}
"ferricel.extensions" => {
extensions = serde_json::from_slice(data)
.context("Failed to deserialize ferricel.extensions")?;
}
"producers" => {
producers = parse_producers(data)?;
}
_ => {}
}
}
Payload::ExportSection(reader) => {
for export in reader {
let export = export.context("Failed to read export")?;
if export.kind == ExternalKind::Func {
exports.push(export.name.to_string());
}
}
}
_ => {}
}
}
exports.sort();
Ok(ModuleInfo {
cel_source,
vap_source,
extensions,
producers,
exports,
})
}
fn parse_producers(data: &[u8]) -> Result<Vec<ProducerField>, anyhow::Error> {
use wasmparser::{BinaryReader, ProducersSectionReader};
let reader = BinaryReader::new(data, 0);
let section =
ProducersSectionReader::new(reader).context("Failed to create producers reader")?;
let mut fields = Vec::new();
for field in section {
let field = field.context("Failed to read producers field")?;
let mut values = Vec::new();
for value in field.values {
let value = value.context("Failed to read producers value")?;
values.push(ProducerValue {
name: value.name.to_string(),
version: value.version.to_string(),
});
}
fields.push(ProducerField {
name: field.name.to_string(),
values,
});
}
Ok(fields)
}