use crate::error::{Error, Result, error_message};
use crate::value::{FuncType, GlobalType, MemoryType};
use fizzyx_sys as sys;
pub struct Module {
inner: *const sys::FizzyModule,
}
impl Module {
pub fn new(wasm: &[u8]) -> Result<Self> {
let mut error = sys::FizzyError::default();
let inner = unsafe { sys::fizzy_parse(wasm.as_ptr(), wasm.len(), &mut error) };
if inner.is_null() {
return Err(Error::Malformed(error_message(&error)));
}
Ok(Self { inner })
}
pub fn validate(wasm: &[u8]) -> Result<()> {
let mut error = sys::FizzyError::default();
let ok = unsafe { sys::fizzy_validate(wasm.as_ptr(), wasm.len(), &mut error) };
if ok {
Ok(())
} else {
Err(Error::Invalid(error_message(&error)))
}
}
pub fn has_start_function(&self) -> bool {
unsafe { sys::fizzy_module_has_start_function(self.inner) }
}
pub fn func_type(&self, name: &str) -> Option<FuncType> {
let c_name = std::ffi::CString::new(name).ok()?;
let mut idx = 0u32;
let found = unsafe {
sys::fizzy_find_exported_function_index(self.inner, c_name.as_ptr(), &mut idx)
};
if !found {
return None;
}
let raw = unsafe { sys::fizzy_get_function_type(self.inner, idx) };
FuncType::from_sys(&raw).ok()
}
pub fn imports(&self) -> Vec<ImportType> {
let count = unsafe { sys::fizzy_get_import_count(self.inner) };
let mut imports = Vec::with_capacity(count as usize);
for idx in 0..count {
let desc = unsafe { sys::fizzy_get_import_description(self.inner, idx) };
let Some(ty) = extern_type_from_import(&desc) else {
continue;
};
imports.push(ImportType {
module: cstr_to_string(desc.module),
name: cstr_to_string(desc.name),
ty,
});
}
imports
}
pub fn exports(&self) -> Vec<ExportType> {
let count = unsafe { sys::fizzy_get_export_count(self.inner) };
let mut exports = Vec::with_capacity(count as usize);
for idx in 0..count {
let desc = unsafe { sys::fizzy_get_export_description(self.inner, idx) };
let Some(kind) = ExternKind::from_sys(desc.kind) else {
continue;
};
exports.push(ExportType {
name: cstr_to_string(desc.name),
kind,
index: desc.index,
});
}
exports
}
pub(crate) fn clone_raw(&self) -> *const sys::FizzyModule {
unsafe { sys::fizzy_clone_module(self.inner) }
}
}
impl Drop for Module {
fn drop(&mut self) {
unsafe { sys::fizzy_free_module(self.inner) }
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum ExternKind {
Func,
Table,
Memory,
Global,
}
impl ExternKind {
fn from_sys(kind: sys::FizzyExternalKind) -> Option<Self> {
match kind {
sys::FizzyExternalKindFunction => Some(Self::Func),
sys::FizzyExternalKindTable => Some(Self::Table),
sys::FizzyExternalKindMemory => Some(Self::Memory),
sys::FizzyExternalKindGlobal => Some(Self::Global),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ExternType {
Func(FuncType),
Table,
Memory(MemoryType),
Global(GlobalType),
}
impl ExternType {
pub fn kind(&self) -> ExternKind {
match self {
Self::Func(_) => ExternKind::Func,
Self::Table => ExternKind::Table,
Self::Memory(_) => ExternKind::Memory,
Self::Global(_) => ExternKind::Global,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ImportType {
module: String,
name: String,
ty: ExternType,
}
impl ImportType {
pub fn module(&self) -> &str {
&self.module
}
pub fn name(&self) -> &str {
&self.name
}
pub fn ty(&self) -> &ExternType {
&self.ty
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExportType {
name: String,
kind: ExternKind,
index: u32,
}
impl ExportType {
pub fn name(&self) -> &str {
&self.name
}
pub fn kind(&self) -> ExternKind {
self.kind
}
pub fn index(&self) -> u32 {
self.index
}
}
fn extern_type_from_import(desc: &sys::FizzyImportDescription) -> Option<ExternType> {
unsafe {
match desc.kind {
sys::FizzyExternalKindFunction => FuncType::from_sys(&desc.desc.function_type)
.ok()
.map(ExternType::Func),
sys::FizzyExternalKindTable => Some(ExternType::Table),
sys::FizzyExternalKindMemory => Some(ExternType::Memory(MemoryType::from_sys(
desc.desc.memory_limits,
))),
sys::FizzyExternalKindGlobal => GlobalType::from_sys(desc.desc.global_type)
.ok()
.map(ExternType::Global),
_ => None,
}
}
}
fn cstr_to_string(ptr: *const core::ffi::c_char) -> String {
if ptr.is_null() {
return String::new();
}
unsafe { core::ffi::CStr::from_ptr(ptr) }
.to_string_lossy()
.into_owned()
}