use crate::compiler::Compiler;
use crate::imports::resolve_imports;
use crate::link::link_module;
use crate::resolver::Resolver;
use std::io::Write;
use std::sync::{Arc, Mutex};
use thiserror::Error;
use wasmtime_debug::read_debuginfo;
use wasmtime_environ::entity::{BoxedSlice, PrimaryMap};
use wasmtime_environ::wasm::{DefinedFuncIndex, SignatureIndex};
use wasmtime_environ::{
CompileError, DataInitializer, DataInitializerLocation, Module, ModuleEnvironment,
};
use wasmtime_profiling::ProfilingAgent;
use wasmtime_runtime::{
GdbJitImageRegistration, InstanceHandle, InstantiationError, TrapRegistration, VMFunctionBody,
VMSharedSignatureIndex,
};
#[derive(Error, Debug)]
pub enum SetupError {
#[error("Validation error: {0}")]
Validate(String),
#[error("WebAssembly failed to compile")]
Compile(#[from] CompileError),
#[error("Instantiation failed during setup")]
Instantiate(#[from] InstantiationError),
#[error("Debug information error")]
DebugInfo(#[from] anyhow::Error),
}
struct FinishedFunctions(BoxedSlice<DefinedFuncIndex, *const [VMFunctionBody]>);
unsafe impl Send for FinishedFunctions {}
unsafe impl Sync for FinishedFunctions {}
struct RawCompiledModule<'data> {
module: Module,
finished_functions: BoxedSlice<DefinedFuncIndex, *const [VMFunctionBody]>,
data_initializers: Box<[DataInitializer<'data>]>,
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
dbg_jit_registration: Option<GdbJitImageRegistration>,
trap_registration: TrapRegistration,
}
impl<'data> RawCompiledModule<'data> {
fn new(
compiler: &Compiler,
data: &'data [u8],
debug_info: bool,
profiler: Option<&Arc<Mutex<Box<dyn ProfilingAgent + Send>>>>,
) -> Result<Self, SetupError> {
let environ = ModuleEnvironment::new(compiler.frontend_config(), compiler.tunables());
let translation = environ
.translate(data)
.map_err(|error| SetupError::Compile(CompileError::Wasm(error)))?;
let debug_data = if debug_info {
Some(read_debuginfo(&data))
} else {
None
};
let (finished_functions, jt_offsets, relocations, dbg_image, trap_registration) = compiler
.compile(
&translation.module,
translation.module_translation.as_ref().unwrap(),
translation.function_body_inputs,
debug_data,
)?;
link_module(
&translation.module,
&finished_functions,
&jt_offsets,
relocations,
);
let signatures = {
let signature_registry = compiler.signatures();
translation
.module
.local
.signatures
.values()
.map(|sig| signature_registry.register(sig))
.collect::<PrimaryMap<_, _>>()
};
compiler.publish_compiled_code();
match profiler {
Some(_) => {
let region_name = String::from("wasm_module");
let mut profiler = profiler.unwrap().lock().unwrap();
match &dbg_image {
Some(dbg) => {
compiler.profiler_module_load(&mut profiler, ®ion_name, Some(&dbg))
}
_ => compiler.profiler_module_load(&mut profiler, ®ion_name, None),
};
}
_ => (),
};
let dbg_jit_registration = if let Some(img) = dbg_image {
let mut bytes = Vec::new();
bytes.write_all(&img).expect("all written");
let reg = GdbJitImageRegistration::register(bytes);
Some(reg)
} else {
None
};
Ok(Self {
module: translation.module,
finished_functions: finished_functions.into_boxed_slice(),
data_initializers: translation.data_initializers.into_boxed_slice(),
signatures: signatures.into_boxed_slice(),
dbg_jit_registration,
trap_registration,
})
}
}
pub struct CompiledModule {
module: Arc<Module>,
finished_functions: FinishedFunctions,
data_initializers: Box<[OwnedDataInitializer]>,
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
dbg_jit_registration: Option<Arc<GdbJitImageRegistration>>,
trap_registration: TrapRegistration,
}
impl CompiledModule {
pub fn new<'data>(
compiler: &Compiler,
data: &'data [u8],
debug_info: bool,
profiler: Option<&Arc<Mutex<Box<dyn ProfilingAgent + Send>>>>,
) -> Result<Self, SetupError> {
let raw = RawCompiledModule::<'data>::new(compiler, data, debug_info, profiler)?;
Ok(Self::from_parts(
raw.module,
raw.finished_functions,
raw.data_initializers
.iter()
.map(OwnedDataInitializer::new)
.collect::<Vec<_>>()
.into_boxed_slice(),
raw.signatures.clone(),
raw.dbg_jit_registration,
raw.trap_registration,
))
}
pub fn from_parts(
module: Module,
finished_functions: BoxedSlice<DefinedFuncIndex, *const [VMFunctionBody]>,
data_initializers: Box<[OwnedDataInitializer]>,
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
dbg_jit_registration: Option<GdbJitImageRegistration>,
trap_registration: TrapRegistration,
) -> Self {
Self {
module: Arc::new(module),
finished_functions: FinishedFunctions(finished_functions),
data_initializers,
signatures,
dbg_jit_registration: dbg_jit_registration.map(Arc::new),
trap_registration,
}
}
pub unsafe fn instantiate(
&self,
resolver: &mut dyn Resolver,
) -> Result<InstanceHandle, InstantiationError> {
let data_initializers = self
.data_initializers
.iter()
.map(|init| DataInitializer {
location: init.location.clone(),
data: &*init.data,
})
.collect::<Vec<_>>();
let imports = resolve_imports(&self.module, resolver)?;
InstanceHandle::new(
Arc::clone(&self.module),
self.trap_registration.clone(),
self.finished_functions.0.clone(),
imports,
&data_initializers,
self.signatures.clone(),
self.dbg_jit_registration.as_ref().map(|r| Arc::clone(&r)),
Box::new(()),
)
}
pub fn module(&self) -> &Arc<Module> {
&self.module
}
pub fn module_ref(&self) -> &Module {
&self.module
}
pub fn finished_functions(&self) -> &BoxedSlice<DefinedFuncIndex, *const [VMFunctionBody]> {
&self.finished_functions.0
}
}
pub struct OwnedDataInitializer {
location: DataInitializerLocation,
data: Box<[u8]>,
}
impl OwnedDataInitializer {
fn new(borrowed: &DataInitializer<'_>) -> Self {
Self {
location: borrowed.location.clone(),
data: borrowed.data.to_vec().into_boxed_slice(),
}
}
}
#[allow(clippy::implicit_hasher)]
pub unsafe fn instantiate(
compiler: &mut Compiler,
data: &[u8],
resolver: &mut dyn Resolver,
debug_info: bool,
profiler: Option<&Arc<Mutex<Box<dyn ProfilingAgent + Send>>>>,
) -> Result<InstanceHandle, SetupError> {
let instance =
CompiledModule::new(compiler, data, debug_info, profiler)?.instantiate(resolver)?;
Ok(instance)
}