use crate::code::CodeObject;
use crate::signatures::SignatureCollection;
use crate::{Engine, Module};
use anyhow::{bail, Context, Result};
use serde::{Deserialize, Serialize};
use std::fs;
use std::mem;
use std::path::Path;
use std::ptr::NonNull;
use std::sync::Arc;
use wasmtime_environ::component::{
AllCallFunc, ComponentTypes, LoweredIndex, RuntimeAlwaysTrapIndex, RuntimeTranscoderIndex,
StaticModuleIndex, Translator,
};
use wasmtime_environ::{FunctionLoc, ObjectKind, PrimaryMap, ScopeVec};
use wasmtime_jit::{CodeMemory, CompiledModuleInfo};
use wasmtime_runtime::{
MmapVec, VMArrayCallFunction, VMFunctionBody, VMNativeCallFunction, VMWasmCallFunction,
};
#[derive(Clone)]
pub struct Component {
inner: Arc<ComponentInner>,
}
struct ComponentInner {
static_modules: PrimaryMap<StaticModuleIndex, Module>,
code: Arc<CodeObject>,
info: CompiledComponentInfo,
}
#[derive(Serialize, Deserialize)]
struct CompiledComponentInfo {
component: wasmtime_environ::component::Component,
lowerings: PrimaryMap<LoweredIndex, AllCallFunc<FunctionLoc>>,
always_trap: PrimaryMap<RuntimeAlwaysTrapIndex, AllCallFunc<FunctionLoc>>,
transcoders: PrimaryMap<RuntimeTranscoderIndex, AllCallFunc<FunctionLoc>>,
}
pub(crate) struct AllCallFuncPointers {
pub wasm_call: NonNull<VMWasmCallFunction>,
pub array_call: VMArrayCallFunction,
pub native_call: NonNull<VMNativeCallFunction>,
}
#[derive(Serialize, Deserialize)]
pub(crate) struct ComponentArtifacts {
info: CompiledComponentInfo,
types: ComponentTypes,
static_modules: PrimaryMap<StaticModuleIndex, CompiledModuleInfo>,
}
impl Component {
#[cfg(any(feature = "cranelift", feature = "winch"))]
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))]
pub fn new(engine: &Engine, bytes: impl AsRef<[u8]>) -> Result<Component> {
let bytes = bytes.as_ref();
#[cfg(feature = "wat")]
let bytes = wat::parse_bytes(bytes)?;
Component::from_binary(engine, &bytes)
}
#[cfg(any(feature = "cranelift", feature = "winch"))]
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))]
pub fn from_file(engine: &Engine, file: impl AsRef<Path>) -> Result<Component> {
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(any(feature = "cranelift", feature = "winch"))]
#[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))]
pub fn from_binary(engine: &Engine, binary: &[u8]) -> Result<Component> {
engine
.check_compatible_with_native_host()
.context("compilation settings are not compatible with the native host")?;
let (mmap, artifacts) = Component::build_artifacts(engine, binary)?;
let mut code_memory = CodeMemory::new(mmap)?;
code_memory.publish()?;
Component::from_parts(engine, Arc::new(code_memory), Some(artifacts))
}
pub unsafe fn deserialize(engine: &Engine, bytes: impl AsRef<[u8]>) -> Result<Component> {
let code = engine.load_code_bytes(bytes.as_ref(), ObjectKind::Component)?;
Component::from_parts(engine, code, None)
}
pub unsafe fn deserialize_file(engine: &Engine, path: impl AsRef<Path>) -> Result<Component> {
let code = engine.load_code_file(path.as_ref(), ObjectKind::Component)?;
Component::from_parts(engine, code, None)
}
#[cfg(any(feature = "cranelift", feature = "winch"))]
pub(crate) fn build_artifacts(
engine: &Engine,
binary: &[u8],
) -> Result<(MmapVec, ComponentArtifacts)> {
use crate::compiler::CompileInputs;
let tunables = &engine.config().tunables;
let compiler = engine.compiler();
let scope = ScopeVec::new();
let mut validator =
wasmparser::Validator::new_with_features(engine.config().features.clone());
let mut types = Default::default();
let (component, mut module_translations) =
Translator::new(tunables, &mut validator, &mut types, &scope)
.translate(binary)
.context("failed to parse WebAssembly module")?;
let types = types.finish();
let compile_inputs = CompileInputs::for_component(
&types,
&component,
module_translations.iter_mut().map(|(i, translation)| {
let functions = mem::take(&mut translation.function_body_inputs);
(i, &*translation, functions)
}),
);
let unlinked_compile_outputs = compile_inputs.compile(&engine)?;
let (compiled_funcs, function_indices) = unlinked_compile_outputs.pre_link();
let mut object = compiler.object(ObjectKind::Component)?;
engine.append_compiler_info(&mut object);
engine.append_bti(&mut object);
let (mut object, compilation_artifacts) = function_indices.link_and_append_code(
object,
tunables,
compiler,
compiled_funcs,
module_translations,
)?;
let info = CompiledComponentInfo {
component,
always_trap: compilation_artifacts.always_traps,
lowerings: compilation_artifacts.lowerings,
transcoders: compilation_artifacts.transcoders,
};
let artifacts = ComponentArtifacts {
info,
types,
static_modules: compilation_artifacts.modules,
};
object.serialize_info(&artifacts);
let mmap = object.finish()?;
Ok((mmap, artifacts))
}
fn from_parts(
engine: &Engine,
code_memory: Arc<CodeMemory>,
artifacts: Option<ComponentArtifacts>,
) -> Result<Component> {
let ComponentArtifacts {
info,
types,
static_modules,
} = match artifacts {
Some(artifacts) => artifacts,
None => bincode::deserialize(code_memory.wasmtime_info())?,
};
let signatures =
SignatureCollection::new_for_module(engine.signatures(), types.module_types());
let types = Arc::new(types);
let code = Arc::new(CodeObject::new(code_memory, signatures, types.into()));
let static_modules = static_modules
.into_iter()
.map(|(_, info)| Module::from_parts_raw(engine, code.clone(), info, false))
.collect::<Result<_>>()?;
Ok(Component {
inner: Arc::new(ComponentInner {
static_modules,
code,
info,
}),
})
}
pub(crate) fn env_component(&self) -> &wasmtime_environ::component::Component {
&self.inner.info.component
}
pub(crate) fn static_module(&self, idx: StaticModuleIndex) -> &Module {
&self.inner.static_modules[idx]
}
pub(crate) fn types(&self) -> &Arc<ComponentTypes> {
match self.inner.code.types() {
crate::code::Types::Component(types) => types,
crate::code::Types::Module(_) => unreachable!(),
}
}
pub(crate) fn signatures(&self) -> &SignatureCollection {
self.inner.code.signatures()
}
pub(crate) fn text(&self) -> &[u8] {
self.inner.code.code_memory().text()
}
pub(crate) fn lowering_ptrs(&self, index: LoweredIndex) -> AllCallFuncPointers {
let AllCallFunc {
wasm_call,
array_call,
native_call,
} = &self.inner.info.lowerings[index];
AllCallFuncPointers {
wasm_call: self.func(wasm_call).cast(),
array_call: unsafe {
mem::transmute::<NonNull<VMFunctionBody>, VMArrayCallFunction>(
self.func(array_call),
)
},
native_call: self.func(native_call).cast(),
}
}
pub(crate) fn always_trap_ptrs(&self, index: RuntimeAlwaysTrapIndex) -> AllCallFuncPointers {
let AllCallFunc {
wasm_call,
array_call,
native_call,
} = &self.inner.info.always_trap[index];
AllCallFuncPointers {
wasm_call: self.func(wasm_call).cast(),
array_call: unsafe {
mem::transmute::<NonNull<VMFunctionBody>, VMArrayCallFunction>(
self.func(array_call),
)
},
native_call: self.func(native_call).cast(),
}
}
pub(crate) fn transcoder_ptrs(&self, index: RuntimeTranscoderIndex) -> AllCallFuncPointers {
let AllCallFunc {
wasm_call,
array_call,
native_call,
} = &self.inner.info.transcoders[index];
AllCallFuncPointers {
wasm_call: self.func(wasm_call).cast(),
array_call: unsafe {
mem::transmute::<NonNull<VMFunctionBody>, VMArrayCallFunction>(
self.func(array_call),
)
},
native_call: self.func(native_call).cast(),
}
}
fn func(&self, loc: &FunctionLoc) -> NonNull<VMFunctionBody> {
let text = self.text();
let trampoline = &text[loc.start as usize..][..loc.length as usize];
NonNull::new(trampoline.as_ptr() as *mut VMFunctionBody).unwrap()
}
pub(crate) fn code_object(&self) -> &Arc<CodeObject> {
&self.inner.code
}
pub fn serialize(&self) -> Result<Vec<u8>> {
Ok(self.code_object().code_memory().mmap().to_vec())
}
}