use crate::types::{ExportType, ExternType, ImportType};
use crate::{Engine, ModuleType};
use anyhow::{bail, Context, Result};
use std::fs;
use std::path::Path;
use std::sync::Arc;
use wasmparser::Validator;
#[cfg(feature = "cache")]
use wasmtime_cache::ModuleCacheEntry;
use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::wasm::ModuleIndex;
use wasmtime_jit::{CompilationArtifacts, CompiledModule, TypeTables};
mod serialization;
pub use serialization::SerializedModule;
#[derive(Clone)]
pub struct Module {
inner: Arc<ModuleInner>,
}
struct ModuleInner {
engine: Engine,
module: Arc<CompiledModule>,
artifact_upvars: Vec<Arc<CompiledModule>>,
module_upvars: Vec<Module>,
types: Arc<TypeTables>,
}
impl Module {
pub fn new(engine: &Engine, bytes: impl AsRef<[u8]>) -> Result<Module> {
let bytes = bytes.as_ref();
if let Some(module) = SerializedModule::from_bytes(bytes)? {
return module.into_module(engine);
}
#[cfg(feature = "wat")]
let bytes = wat::parse_bytes(bytes)?;
Self::from_binary(engine, &bytes)
}
pub fn new_with_name(engine: &Engine, bytes: impl AsRef<[u8]>, name: &str) -> Result<Module> {
let mut module = Self::new(engine, bytes.as_ref())?;
Arc::get_mut(&mut Arc::get_mut(&mut module.inner).unwrap().module)
.unwrap()
.module_mut()
.expect("mutable module")
.name = Some(name.to_string());
Ok(module)
}
pub fn from_file(engine: &Engine, file: impl AsRef<Path>) -> Result<Module> {
match Self::new(
engine,
&fs::read(&file).with_context(|| "failed to read input file")?,
) {
Ok(m) => Ok(m),
Err(e) => {
cfg_if::cfg_if! {
if #[cfg(feature = "wat")] {
let mut e = e.downcast::<wat::Error>()?;
e.set_path(file);
bail!(e)
} else {
Err(e)
}
}
}
}
}
pub fn from_binary(engine: &Engine, binary: &[u8]) -> Result<Module> {
if let Some(module) = SerializedModule::from_bytes(binary)? {
return module.into_module(engine);
}
let target = engine.config().isa_flags.triple();
if *target != target_lexicon::Triple::host() {
bail!(
"target '{}' specified in the configuration does not match the host",
target
);
}
const USE_PAGED_MEM_INIT: bool = cfg!(all(feature = "uffd", target_os = "linux"));
cfg_if::cfg_if! {
if #[cfg(feature = "cache")] {
let (main_module, artifacts, types) = ModuleCacheEntry::new(
"wasmtime",
engine.cache_config(),
)
.get_data((engine.compiler(), binary), |(compiler, binary)| {
CompilationArtifacts::build(compiler, binary, USE_PAGED_MEM_INIT)
})?;
} else {
let (main_module, artifacts, types) =
CompilationArtifacts::build(engine.compiler(), binary, USE_PAGED_MEM_INIT)?;
}
};
let mut modules = CompiledModule::from_artifacts_list(
artifacts,
engine.compiler().isa(),
&*engine.config().profiler,
)?;
let module = modules.remove(main_module);
engine.allocator().validate(module.module())?;
Ok(Module {
inner: Arc::new(ModuleInner {
engine: engine.clone(),
module,
types: Arc::new(types),
artifact_upvars: modules,
module_upvars: Vec::new(),
}),
})
}
pub fn validate(engine: &Engine, binary: &[u8]) -> Result<()> {
let mut validator = Validator::new();
validator.wasm_features(engine.config().features);
validator.validate_all(binary)?;
Ok(())
}
pub fn ty(&self) -> ModuleType {
let mut sig = ModuleType::new();
let env_module = self.compiled_module().module();
let types = self.types();
for (module, field, ty) in env_module.imports() {
sig.add_named_import(module, field, ExternType::from_wasmtime(types, &ty));
}
for (name, index) in env_module.exports.iter() {
sig.add_named_export(
name,
ExternType::from_wasmtime(types, &env_module.type_of(*index)),
);
}
sig
}
pub fn serialize(&self) -> Result<Vec<u8>> {
SerializedModule::new(self).to_bytes()
}
pub(crate) fn create_submodule(
&self,
artifact_index: usize,
artifact_upvars: &[usize],
module_upvars: &[wasmtime_environ::ModuleUpvar],
modules: &PrimaryMap<ModuleIndex, Module>,
) -> Module {
Module {
inner: Arc::new(ModuleInner {
types: self.types().clone(),
engine: self.engine().clone(),
module: self.inner.artifact_upvars[artifact_index].clone(),
artifact_upvars: artifact_upvars
.iter()
.map(|i| self.inner.artifact_upvars[*i].clone())
.collect(),
module_upvars: module_upvars
.iter()
.map(|i| match *i {
wasmtime_environ::ModuleUpvar::Inherit(i) => {
self.inner.module_upvars[i].clone()
}
wasmtime_environ::ModuleUpvar::Local(i) => modules[i].clone(),
})
.collect(),
}),
}
}
pub(crate) fn compiled_module(&self) -> &CompiledModule {
&self.inner.module
}
pub(crate) fn env_module(&self) -> &wasmtime_environ::Module {
self.compiled_module().module()
}
pub(crate) fn types(&self) -> &Arc<TypeTables> {
&self.inner.types
}
pub(crate) fn module_upvar(&self, index: usize) -> &Module {
&self.inner.module_upvars[index]
}
pub fn name(&self) -> Option<&str> {
self.compiled_module().module().name.as_deref()
}
pub fn imports<'module>(
&'module self,
) -> impl ExactSizeIterator<Item = ImportType<'module>> + 'module {
let module = self.compiled_module().module();
let types = self.types();
module
.imports()
.map(move |(module, field, ty)| ImportType::new(module, field, ty, types))
.collect::<Vec<_>>()
.into_iter()
}
pub fn exports<'module>(
&'module self,
) -> impl ExactSizeIterator<Item = ExportType<'module>> + 'module {
let module = self.compiled_module().module();
let types = self.types();
module.exports.iter().map(move |(name, entity_index)| {
ExportType::new(name, module.type_of(*entity_index), types)
})
}
pub fn get_export<'module>(&'module self, name: &'module str) -> Option<ExternType> {
let module = self.compiled_module().module();
let entity_index = module.exports.get(name)?;
Some(ExternType::from_wasmtime(
self.types(),
&module.type_of(*entity_index),
))
}
pub fn engine(&self) -> &Engine {
&self.inner.engine
}
}
fn _assert_send_sync() {
fn _assert<T: Send + Sync>() {}
_assert::<Module>();
}