use crate::CodeMemory;
use crate::UniversalArtifact;
use loupe::MemoryUsage;
use std::sync::{Arc, Mutex};
#[cfg(feature = "compiler")]
use wasmer_compiler::Compiler;
use wasmer_compiler::{
CompileError, CustomSection, CustomSectionProtection, FunctionBody, SectionIndex, Target,
};
use wasmer_engine::{Artifact, DeserializeError, Engine, EngineId, FunctionExtent, Tunables};
use wasmer_engine_universal_artifact::UniversalEngineBuilder;
use wasmer_types::entity::PrimaryMap;
use wasmer_types::{
Features, FunctionIndex, FunctionType, LocalFunctionIndex, ModuleInfo, SignatureIndex,
};
use wasmer_vm::{
FuncDataRegistry, FunctionBodyPtr, SectionBodyPtr, SignatureRegistry, VMCallerCheckedAnyfunc,
VMFuncRef, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline,
};
#[derive(Clone, MemoryUsage)]
pub struct UniversalEngine {
inner: Arc<Mutex<UniversalEngineInner>>,
target: Arc<Target>,
engine_id: EngineId,
}
impl UniversalEngine {
#[cfg(feature = "compiler")]
pub fn new(compiler: Box<dyn Compiler>, target: Target, features: Features) -> Self {
Self {
inner: Arc::new(Mutex::new(UniversalEngineInner {
builder: UniversalEngineBuilder::new(Some(compiler), features),
code_memory: vec![],
signatures: SignatureRegistry::new(),
func_data: Arc::new(FuncDataRegistry::new()),
})),
target: Arc::new(target),
engine_id: EngineId::default(),
}
}
pub fn headless() -> Self {
Self {
inner: Arc::new(Mutex::new(UniversalEngineInner {
builder: UniversalEngineBuilder::new(None, Features::default()),
code_memory: vec![],
signatures: SignatureRegistry::new(),
func_data: Arc::new(FuncDataRegistry::new()),
})),
target: Arc::new(Target::default()),
engine_id: EngineId::default(),
}
}
pub(crate) fn inner(&self) -> std::sync::MutexGuard<'_, UniversalEngineInner> {
self.inner.lock().unwrap()
}
pub(crate) fn inner_mut(&self) -> std::sync::MutexGuard<'_, UniversalEngineInner> {
self.inner.lock().unwrap()
}
}
impl Engine for UniversalEngine {
fn target(&self) -> &Target {
&self.target
}
fn register_signature(&self, func_type: &FunctionType) -> VMSharedSignatureIndex {
let compiler = self.inner();
compiler.signatures().register(func_type)
}
fn register_function_metadata(&self, func_data: VMCallerCheckedAnyfunc) -> VMFuncRef {
let compiler = self.inner();
compiler.func_data().register(func_data)
}
fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option<FunctionType> {
let compiler = self.inner();
compiler.signatures().lookup(sig)
}
fn validate(&self, binary: &[u8]) -> Result<(), CompileError> {
self.inner().validate(binary)
}
#[cfg(feature = "compiler")]
fn compile(
&self,
binary: &[u8],
tunables: &dyn Tunables,
) -> Result<Arc<dyn Artifact>, CompileError> {
Ok(Arc::new(UniversalArtifact::new(&self, binary, tunables)?))
}
#[cfg(not(feature = "compiler"))]
fn compile(
&self,
_binary: &[u8],
_tunables: &dyn Tunables,
) -> Result<Arc<dyn Artifact>, CompileError> {
Err(CompileError::Codegen(
"The UniversalEngine is operating in headless mode, so it can not compile Modules."
.to_string(),
))
}
unsafe fn deserialize(&self, bytes: &[u8]) -> Result<Arc<dyn Artifact>, DeserializeError> {
Ok(Arc::new(UniversalArtifact::deserialize(&self, &bytes)?))
}
fn id(&self) -> &EngineId {
&self.engine_id
}
fn cloned(&self) -> Arc<dyn Engine + Send + Sync> {
Arc::new(self.clone())
}
}
#[derive(MemoryUsage)]
pub struct UniversalEngineInner {
builder: UniversalEngineBuilder,
code_memory: Vec<CodeMemory>,
signatures: SignatureRegistry,
func_data: Arc<FuncDataRegistry>,
}
impl UniversalEngineInner {
#[cfg(feature = "compiler")]
pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> {
self.builder.compiler()
}
pub fn validate<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> {
self.builder.validate(data)
}
pub fn features(&self) -> &Features {
self.builder.features()
}
pub fn builder_mut(&mut self) -> &mut UniversalEngineBuilder {
&mut self.builder
}
#[allow(clippy::type_complexity)]
pub(crate) fn allocate(
&mut self,
_module: &ModuleInfo,
functions: &PrimaryMap<LocalFunctionIndex, FunctionBody>,
function_call_trampolines: &PrimaryMap<SignatureIndex, FunctionBody>,
dynamic_function_trampolines: &PrimaryMap<FunctionIndex, FunctionBody>,
custom_sections: &PrimaryMap<SectionIndex, CustomSection>,
) -> Result<
(
PrimaryMap<LocalFunctionIndex, FunctionExtent>,
PrimaryMap<SignatureIndex, VMTrampoline>,
PrimaryMap<FunctionIndex, FunctionBodyPtr>,
PrimaryMap<SectionIndex, SectionBodyPtr>,
),
CompileError,
> {
let function_bodies = functions
.values()
.chain(function_call_trampolines.values())
.chain(dynamic_function_trampolines.values())
.collect::<Vec<_>>();
let (executable_sections, data_sections): (Vec<_>, _) = custom_sections
.values()
.partition(|section| section.protection == CustomSectionProtection::ReadExecute);
self.code_memory.push(CodeMemory::new());
let (mut allocated_functions, allocated_executable_sections, allocated_data_sections) =
self.code_memory
.last_mut()
.unwrap()
.allocate(
function_bodies.as_slice(),
executable_sections.as_slice(),
data_sections.as_slice(),
)
.map_err(|message| {
CompileError::Resource(format!(
"failed to allocate memory for functions: {}",
message
))
})?;
let allocated_functions_result = allocated_functions
.drain(0..functions.len())
.map(|slice| FunctionExtent {
ptr: FunctionBodyPtr(slice.as_ptr()),
length: slice.len(),
})
.collect::<PrimaryMap<LocalFunctionIndex, _>>();
let mut allocated_function_call_trampolines: PrimaryMap<SignatureIndex, VMTrampoline> =
PrimaryMap::new();
for ptr in allocated_functions
.drain(0..function_call_trampolines.len())
.map(|slice| slice.as_ptr())
{
let trampoline =
unsafe { std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(ptr) };
allocated_function_call_trampolines.push(trampoline);
}
let allocated_dynamic_function_trampolines = allocated_functions
.drain(..)
.map(|slice| FunctionBodyPtr(slice.as_ptr()))
.collect::<PrimaryMap<FunctionIndex, _>>();
let mut exec_iter = allocated_executable_sections.iter();
let mut data_iter = allocated_data_sections.iter();
let allocated_custom_sections = custom_sections
.iter()
.map(|(_, section)| {
SectionBodyPtr(
if section.protection == CustomSectionProtection::ReadExecute {
exec_iter.next()
} else {
data_iter.next()
}
.unwrap()
.as_ptr(),
)
})
.collect::<PrimaryMap<SectionIndex, _>>();
Ok((
allocated_functions_result,
allocated_function_call_trampolines,
allocated_dynamic_function_trampolines,
allocated_custom_sections,
))
}
pub(crate) fn publish_compiled_code(&mut self) {
self.code_memory.last_mut().unwrap().publish();
}
pub(crate) fn publish_eh_frame(&mut self, eh_frame: Option<&[u8]>) -> Result<(), CompileError> {
self.code_memory
.last_mut()
.unwrap()
.unwind_registry_mut()
.publish(eh_frame)
.map_err(|e| {
CompileError::Resource(format!("Error while publishing the unwind code: {}", e))
})?;
Ok(())
}
pub fn signatures(&self) -> &SignatureRegistry {
&self.signatures
}
pub(crate) fn func_data(&self) -> &Arc<FuncDataRegistry> {
&self.func_data
}
}