use crate::{
signatures::SignatureCollection,
types::{ExportType, ExternType, ImportType},
};
use crate::{Engine, ModuleType};
use anyhow::{bail, Context, Result};
use std::fs;
use std::mem;
use std::path::Path;
use std::sync::Arc;
use wasmparser::{Parser, ValidPayload, Validator};
use wasmtime_environ::{ModuleEnvironment, ModuleIndex, PrimaryMap};
use wasmtime_jit::{CompiledModule, CompiledModuleInfo, MmapVec, TypeTables};
mod registry;
mod serialization;
pub use registry::{FrameInfo, FrameSymbol, GlobalModuleRegistry, ModuleRegistry};
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>,
signatures: Arc<SignatureCollection>,
}
impl Module {
#[cfg(compiler)]
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] pub fn new(engine: &Engine, bytes: impl AsRef<[u8]>) -> Result<Module> {
let bytes = bytes.as_ref();
#[cfg(feature = "wat")]
let bytes = wat::parse_bytes(bytes)?;
Self::from_binary(engine, &bytes)
}
#[cfg(compiler)]
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] 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)
}
#[cfg(compiler)]
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] 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)
}
}
}
}
}
#[cfg(compiler)]
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] pub fn from_binary(engine: &Engine, binary: &[u8]) -> Result<Module> {
let target = engine.compiler().triple();
if *target != target_lexicon::Triple::host() {
bail!(
"target '{}' specified in the configuration does not match the host",
target
);
}
cfg_if::cfg_if! {
if #[cfg(feature = "cache")] {
let state = (HashedEngineCompileEnv(engine), binary);
let (main_module, artifacts, types) = wasmtime_cache::ModuleCacheEntry::new(
"wasmtime",
engine.cache_config(),
)
.get_data_raw(
&state,
|(engine, wasm)| Module::build_artifacts(engine.0, wasm),
|(engine, _wasm), (_, artifacts, types)| {
SerializedModule::from_artifacts(
engine.0,
artifacts.iter().map(|p| &p.0),
types,
).to_bytes(&engine.0.config().module_version).ok()
},
|(engine, _wasm), serialized_bytes| {
let (i, m, t, upvars) = SerializedModule::from_bytes(&serialized_bytes, &engine.0.config().module_version)
.ok()?
.into_parts(engine.0)
.ok()?;
assert!(upvars.is_empty());
Some((i, m, t))
},
)?;
} else {
let (main_module, artifacts, types) = Module::build_artifacts(engine, binary)?;
}
};
let modules = engine.run_maybe_parallel(artifacts, |(a, b)| {
CompiledModule::from_artifacts(a, b, &*engine.config().profiler)
})?;
Self::from_parts(engine, modules, main_module, Arc::new(types), &[])
}
#[cfg(compiler)]
pub(crate) fn build_artifacts(
engine: &Engine,
wasm: &[u8],
) -> Result<(
usize,
Vec<(MmapVec, Option<CompiledModuleInfo>)>,
TypeTables,
)> {
let tunables = &engine.config().tunables;
let (main_module, translations, types) =
ModuleEnvironment::new(tunables, &engine.config().features)
.translate(wasm)
.context("failed to parse WebAssembly module")?;
let list = engine.run_maybe_parallel(translations, |mut translation| -> Result<_> {
let functions = mem::take(&mut translation.function_body_inputs);
let functions = functions.into_iter().collect::<Vec<_>>();
let funcs = engine
.run_maybe_parallel(functions, |(index, func)| {
engine
.compiler()
.compile_function(&translation, index, func, tunables, &types)
})?
.into_iter()
.collect();
let mut obj = engine.compiler().object()?;
let (funcs, trampolines) =
engine
.compiler()
.emit_obj(&translation, &types, funcs, tunables, &mut obj)?;
if engine.config().paged_memory_initialization {
translation.try_paged_init();
}
let (mmap, info) =
wasmtime_jit::finish_compile(translation, obj, funcs, trampolines, tunables)?;
Ok((mmap, Some(info)))
})?;
Ok((
main_module,
list,
TypeTables {
wasm_signatures: types.wasm_signatures,
module_signatures: types.module_signatures,
instance_signatures: types.instance_signatures,
},
))
}
pub unsafe fn deserialize(engine: &Engine, bytes: impl AsRef<[u8]>) -> Result<Module> {
let module = SerializedModule::from_bytes(bytes.as_ref(), &engine.config().module_version)?;
module.into_module(engine)
}
pub unsafe fn deserialize_file(engine: &Engine, path: impl AsRef<Path>) -> Result<Module> {
let module = SerializedModule::from_file(path.as_ref(), &engine.config().module_version)?;
module.into_module(engine)
}
fn from_parts(
engine: &Engine,
mut modules: Vec<Arc<CompiledModule>>,
main_module: usize,
types: Arc<TypeTables>,
module_upvars: &[serialization::SerializedModuleUpvar],
) -> Result<Self> {
engine.allocator().validate(modules[main_module].module())?;
let signatures = Arc::new(SignatureCollection::new_for_module(
engine.signatures(),
&types.wasm_signatures,
modules
.iter()
.flat_map(|m| m.trampolines().map(|(idx, f, _)| (idx, f))),
));
let module = modules.remove(main_module);
let module_upvars = module_upvars
.iter()
.map(|m| {
mk(
engine,
&modules,
&types,
m.index,
&m.artifact_upvars,
&m.module_upvars,
&signatures,
)
})
.collect::<Result<Vec<_>>>()?;
return Ok(Self {
inner: Arc::new(ModuleInner {
engine: engine.clone(),
types,
module,
artifact_upvars: modules,
module_upvars,
signatures,
}),
});
fn mk(
engine: &Engine,
artifacts: &[Arc<CompiledModule>],
types: &Arc<TypeTables>,
module_index: usize,
artifact_upvars: &[usize],
module_upvars: &[serialization::SerializedModuleUpvar],
signatures: &Arc<SignatureCollection>,
) -> Result<Module> {
Ok(Module {
inner: Arc::new(ModuleInner {
engine: engine.clone(),
types: types.clone(),
module: artifacts[module_index].clone(),
artifact_upvars: artifact_upvars
.iter()
.map(|i| artifacts[*i].clone())
.collect(),
module_upvars: module_upvars
.into_iter()
.map(|m| {
mk(
engine,
artifacts,
types,
m.index,
&m.artifact_upvars,
&m.module_upvars,
signatures,
)
})
.collect::<Result<Vec<_>>>()?,
signatures: signatures.clone(),
}),
})
}
}
pub fn validate(engine: &Engine, binary: &[u8]) -> Result<()> {
let mut validator = Validator::new();
validator.wasm_features(engine.config().features);
let mut functions = Vec::new();
for payload in Parser::new(0).parse_all(binary) {
if let ValidPayload::Func(a, b) = validator.payload(&payload?)? {
functions.push((a, b));
}
}
engine.run_maybe_parallel(functions, |(mut validator, body)| validator.validate(&body))?;
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
}
#[cfg(compiler)]
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] pub fn serialize(&self) -> Result<Vec<u8>> {
SerializedModule::new(self).to_bytes(&self.inner.engine.config().module_version)
}
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.inner.types.clone(),
engine: self.inner.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(),
signatures: self.inner.signatures.clone(),
}),
}
}
pub(crate) fn compiled_module(&self) -> &Arc<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 signatures(&self) -> &Arc<SignatureCollection> {
&self.inner.signatures
}
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(&self, name: &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>();
}
#[cfg(all(feature = "cache", compiler))]
struct HashedEngineCompileEnv<'a>(&'a Engine);
#[cfg(all(feature = "cache", compiler))]
impl std::hash::Hash for HashedEngineCompileEnv<'_> {
fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
use std::collections::BTreeMap;
let compiler = self.0.compiler();
compiler.triple().hash(hasher);
compiler
.flags()
.into_iter()
.collect::<BTreeMap<_, _>>()
.hash(hasher);
compiler
.isa_flags()
.into_iter()
.collect::<BTreeMap<_, _>>()
.hash(hasher);
let config = self.0.config();
config.tunables.hash(hasher);
config.features.hash(hasher);
env!("CARGO_PKG_VERSION").hash(hasher);
}
}