use std::{path::Path, sync::Arc};
use crate::{
AsEngineRef, BackendModule, IntoBytes, Store, backend::v8::bindings::*,
v8::utils::convert::IntoWasmerExternType,
};
use bytes::Bytes;
use wasmer_types::{
CompileError, DeserializeError, ExportType, ExportsIterator, ExternType, FunctionType,
GlobalType, ImportType, ImportsIterator, MemoryType, ModuleInfo, Mutability, Pages,
SerializeError, TableType, Type,
};
#[derive(Debug)]
pub(crate) struct ModuleHandle {
pub(crate) v8_shared_module_handle: *mut wasm_shared_module_t,
pub(crate) orig_store: Store,
}
impl PartialEq for ModuleHandle {
fn eq(&self, other: &Self) -> bool {
unsafe {
wasm_module_same(
wasm_module_obtain(self.orig_store.as_v8().inner, self.v8_shared_module_handle),
wasm_module_obtain(
other.orig_store.as_v8().inner,
other.v8_shared_module_handle,
),
)
}
}
}
impl Eq for ModuleHandle {}
impl ModuleHandle {
fn new(engine: &impl AsEngineRef, binary: &[u8]) -> Result<Self, CompileError> {
let bytes = wasm_byte_vec_t {
size: binary.len(),
data: binary.as_ptr() as _,
};
let engine = engine.as_engine_ref().engine().clone();
let store = Store::new(engine.clone());
let engine = engine.as_v8().inner.engine;
let inner = unsafe { wasm_module_new(store.as_v8().inner, &bytes as *const _) };
if inner.is_null() {
return Err(CompileError::Validate(
"Failed to create V8 module: null module reference returned from V8".to_string(),
));
}
let inner = unsafe { wasm_module_share(inner) };
if inner.is_null() {
return Err(CompileError::Validate(
"Failed to create V8 module: null module reference returned from V8".to_string(),
));
}
Ok(Self {
v8_shared_module_handle: inner,
orig_store: store,
})
}
fn deserialize(engine: &impl AsEngineRef, binary: &[u8]) -> Result<Self, CompileError> {
let bytes = wasm_byte_vec_t {
size: binary.len(),
data: binary.as_ptr() as _,
};
let engine = engine.as_engine_ref().engine().clone();
let store = Store::new(engine.clone());
let inner = unsafe { wasm_module_deserialize(store.as_v8().inner, &bytes as *const _) };
if inner.is_null() {
return Err(CompileError::Validate(
"Failed to deserialize V8 module: null module reference returned from V8"
.to_string(),
));
}
let inner = unsafe { wasm_module_share(inner) };
if inner.is_null() {
return Err(CompileError::Validate(
"Failed to create V8 module: null module reference returned from V8".to_string(),
));
}
Ok(Self {
v8_shared_module_handle: inner,
orig_store: store,
})
}
#[tracing::instrument(skip(self))]
pub fn serialize(&self) -> Result<Vec<u8>, SerializeError> {
let handle = unsafe {
wasm_module_obtain(
self.orig_store.as_v8().inner,
self.v8_shared_module_handle as *const _,
)
};
let mut bytes = wasm_byte_vec_t {
size: 0,
data: std::ptr::null_mut(),
};
let bytes = unsafe {
wasm_module_serialize(handle, &mut bytes as *mut _);
if bytes.data.is_null() || bytes.size == 0 {
return Err(SerializeError::Generic(String::from(
"V8 returned an empty vector as serialized module",
)));
}
std::slice::from_raw_parts(bytes.data as *mut u8, bytes.size)
};
Ok(bytes.to_vec())
}
}
impl Drop for ModuleHandle {
fn drop(&mut self) {
unsafe { wasm_shared_module_delete(self.v8_shared_module_handle) }
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct Module {
pub(crate) handle: Arc<ModuleHandle>,
name: Option<String>,
}
unsafe impl Send for Module {}
unsafe impl Sync for Module {}
impl Module {
#[tracing::instrument(skip(engine, binary))]
pub(crate) fn from_binary(
engine: &impl AsEngineRef,
binary: &[u8],
) -> Result<Self, CompileError> {
tracing::info!("Creating module from binary");
unsafe { Self::from_binary_unchecked(engine, binary) }
}
#[allow(clippy::arc_with_non_send_sync)]
#[tracing::instrument(skip(engine, binary))]
pub(crate) unsafe fn from_binary_unchecked(
engine: &impl AsEngineRef,
binary: &[u8],
) -> Result<Self, CompileError> {
tracing::info!("Creating module from binary unchecked");
let mut binary = binary.to_vec();
let binary = binary.into_bytes();
let module = ModuleHandle::new(engine, &binary)?;
let info = crate::utils::polyfill::translate_module(&binary[..])
.unwrap()
.info;
Ok(Self {
handle: Arc::new(module),
name: info.name,
})
}
pub fn validate(engine: &impl AsEngineRef, binary: &[u8]) -> Result<(), CompileError> {
let engine = engine.as_engine_ref().engine().clone();
let store = super::store::Store::new(engine);
let bytes = wasm_byte_vec_t {
size: binary.len(),
data: binary.as_ptr() as _,
};
let store = store.inner;
unsafe {
if !wasm_module_validate(store, &bytes as *const _) {
return Err(CompileError::Validate(String::from(
"V8 could not validate the given module",
)));
}
}
Ok(())
}
pub fn name(&self) -> Option<&str> {
self.name.as_ref().map(|s| s.as_ref())
}
#[tracing::instrument(skip(self))]
pub fn serialize(&self) -> Result<Bytes, SerializeError> {
let mut raw_bytes = self
.name
.clone()
.unwrap_or_default()
.bytes()
.collect::<Vec<u8>>();
let raw_bytes_off = raw_bytes.len();
let mut v8_module_bytes = self.handle.serialize()?;
let mut data = raw_bytes_off.to_ne_bytes().to_vec();
data.append(&mut raw_bytes);
data.append(&mut v8_module_bytes);
Ok(data.into())
}
#[allow(clippy::arc_with_non_send_sync)]
pub unsafe fn deserialize_unchecked(
engine: &impl AsEngineRef,
bytes: impl IntoBytes,
) -> Result<Self, DeserializeError> {
tracing::info!("Creating module from deserialize_unchecked");
let binary = bytes.into_bytes();
let off = &binary[0..8];
let off = usize::from_ne_bytes(off.try_into().unwrap());
let name_bytes = &binary[8..(8 + off)];
let name = String::from_utf8_lossy(name_bytes).to_string();
let mod_bytes = &binary[(8 + off)..];
let module = ModuleHandle::deserialize(engine, mod_bytes)?;
Ok(Self {
handle: Arc::new(module),
name: Some(name),
})
}
pub unsafe fn deserialize(
engine: &impl AsEngineRef,
bytes: impl IntoBytes,
) -> Result<Self, DeserializeError> {
unsafe { Self::deserialize_unchecked(engine, bytes) }
}
pub unsafe fn deserialize_from_file_unchecked(
engine: &impl AsEngineRef,
path: impl AsRef<Path>,
) -> Result<Self, DeserializeError> {
let bytes = std::fs::read(path.as_ref())?;
unsafe { Self::deserialize_unchecked(engine, bytes) }
}
pub unsafe fn deserialize_from_file(
engine: &impl AsEngineRef,
path: impl AsRef<Path>,
) -> Result<Self, DeserializeError> {
let bytes = std::fs::read(path.as_ref())?;
unsafe { Self::deserialize(engine, bytes) }
}
pub fn set_name(&mut self, name: &str) -> bool {
self.name = Some(name.to_string());
true
}
pub fn imports<'a>(&'a self) -> ImportsIterator<Box<dyn Iterator<Item = ImportType> + 'a>> {
let mut imports = wasm_importtype_vec_t {
size: 0,
data: std::ptr::null_mut(),
};
let store = self.handle.orig_store.as_v8().inner;
let shared_handle = self.handle.v8_shared_module_handle;
let imports = unsafe {
let module = wasm_module_obtain(store, shared_handle);
if module.is_null() {
panic!("Could not get imports: underlying module is null!");
}
wasm_module_imports(module as *const _, &mut imports as *mut _);
let imports =
if imports.data.is_null() || !imports.data.is_aligned() || imports.size == 0 {
vec![]
} else {
std::slice::from_raw_parts(imports.data, imports.size).to_vec()
};
let mut wasmer_imports = vec![];
for i in imports.into_iter() {
if i.is_null() {
panic!("null import returned from V8!");
}
let name = wasm_importtype_name(i as *const _);
let name = std::slice::from_raw_parts((*name).data as *const u8, (*name).size);
let name_str = String::from_utf8_lossy(name).to_string();
let module = wasm_importtype_module(i as *const _);
let module_str = if module.is_null()
|| (*module).data.is_null()
|| !(*module).data.is_aligned()
|| (*module).size == 0
{
String::new()
} else {
let str =
std::slice::from_raw_parts((*module).data as *const u8, (*module).size);
String::from_utf8_lossy(str).to_string()
};
let ty = IntoWasmerExternType::into_wextt(wasm_importtype_type(i as *const _));
if let Err(err) = ty {
panic!("{err}");
}
let ty = ty.unwrap();
wasmer_imports.push(ImportType::new(&module_str, &name_str, ty))
}
wasmer_imports
};
let len = imports.len();
wasmer_types::ImportsIterator::new(Box::new(imports.into_iter()), len)
}
pub fn exports<'a>(&'a self) -> ExportsIterator<Box<dyn Iterator<Item = ExportType> + 'a>> {
let mut exports = wasm_exporttype_vec_t {
size: 0,
data: std::ptr::null_mut(),
};
let store = self.handle.orig_store.as_v8().inner;
let shared_handle = self.handle.v8_shared_module_handle;
let exports = unsafe {
let module = wasm_module_obtain(store, shared_handle);
if module.is_null() {
panic!("Could not get imports: underlying module is null!");
}
wasm_module_exports(module as *const _, &mut exports as *mut _);
let exports = std::slice::from_raw_parts(exports.data, exports.size).to_vec();
let mut wasmer_exports = vec![];
for e in exports.into_iter() {
if e.is_null() {
panic!("null import returned from V8!");
}
let name = wasm_exporttype_name(e as *const _);
let name = std::slice::from_raw_parts((*name).data as *const u8, (*name).size);
let name_str = String::from_utf8_lossy(name).to_string();
let ty = IntoWasmerExternType::into_wextt(wasm_exporttype_type(e as *const _));
if let Err(err) = ty {
panic!("{err}");
}
let ty = ty.unwrap();
wasmer_exports.push(ExportType::new(&name_str, ty))
}
wasmer_exports
};
let len = exports.len();
wasmer_types::ExportsIterator::new(Box::new(exports.into_iter()), len)
}
pub fn custom_sections<'a>(
&'a self,
name: &'a str,
) -> Box<dyn Iterator<Item = Box<[u8]>> + 'a> {
Box::new(vec![].into_iter())
}
pub(crate) fn info(&self) -> &ModuleInfo {
panic!("no info for V8 modules")
}
}
impl crate::Module {
pub fn into_v8(self) -> crate::backend::v8::module::Module {
match self.0 {
BackendModule::V8(s) => s,
_ => panic!("Not a `v8` module!"),
}
}
pub fn as_v8(&self) -> &crate::backend::v8::module::Module {
match self.0 {
BackendModule::V8(ref s) => s,
_ => panic!("Not a `v8` module!"),
}
}
pub fn as_v8_mut(&mut self) -> &mut crate::backend::v8::module::Module {
match self.0 {
BackendModule::V8(ref mut s) => s,
_ => panic!("Not a `v8` module!"),
}
}
}