use crate::component::matching::InstanceType;
use crate::component::types;
use crate::prelude::*;
use crate::runtime::vm::component::ComponentRuntimeInfo;
use crate::runtime::vm::{VMArrayCallFunction, VMFuncRef, VMFunctionBody, VMWasmCallFunction};
use crate::{
code::CodeObject, code_memory::CodeMemory, type_registry::TypeCollection, Engine, Module,
ResourcesRequired,
};
use crate::{FuncType, ValType};
use alloc::sync::Arc;
use anyhow::Result;
use core::any::Any;
use core::mem;
use core::ops::Range;
use core::ptr::NonNull;
#[cfg(feature = "std")]
use std::path::Path;
use wasmtime_environ::component::{
AllCallFunc, CompiledComponentInfo, ComponentArtifacts, ComponentTypes, GlobalInitializer,
InstantiateModule, StaticModuleIndex, TrampolineIndex, TypeComponentIndex, VMComponentOffsets,
};
use wasmtime_environ::{FunctionLoc, HostPtr, ObjectKind, PrimaryMap};
#[derive(Clone)]
pub struct Component {
inner: Arc<ComponentInner>,
}
struct ComponentInner {
ty: TypeComponentIndex,
static_modules: PrimaryMap<StaticModuleIndex, Module>,
code: Arc<CodeObject>,
info: CompiledComponentInfo,
realloc_func_type: Arc<dyn Any + Send + Sync>,
}
pub(crate) struct AllCallFuncPointers {
pub wasm_call: NonNull<VMWasmCallFunction>,
pub array_call: VMArrayCallFunction,
}
impl Component {
#[cfg(any(feature = "cranelift", feature = "winch"))]
pub fn new(engine: &Engine, bytes: impl AsRef<[u8]>) -> Result<Component> {
crate::CodeBuilder::new(engine)
.wasm(bytes.as_ref(), None)?
.compile_component()
}
#[cfg(all(feature = "std", any(feature = "cranelift", feature = "winch")))]
pub fn from_file(engine: &Engine, file: impl AsRef<Path>) -> Result<Component> {
crate::CodeBuilder::new(engine)
.wasm_file(file.as_ref())?
.compile_component()
}
#[cfg(any(feature = "cranelift", feature = "winch"))]
pub fn from_binary(engine: &Engine, binary: &[u8]) -> Result<Component> {
crate::CodeBuilder::new(engine)
.wasm(binary, None)?
.wat(false)?
.compile_component()
}
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)
}
#[cfg(feature = "std")]
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)
}
pub fn component_type(&self) -> types::Component {
let resources = Arc::new(PrimaryMap::new());
types::Component::from(
self.inner.ty,
&InstanceType {
types: self.types(),
resources: &resources,
},
)
}
pub(crate) fn from_parts(
engine: &Engine,
code_memory: Arc<CodeMemory>,
artifacts: Option<ComponentArtifacts>,
) -> Result<Component> {
let ComponentArtifacts {
ty,
info,
types,
static_modules,
} = match artifacts {
Some(artifacts) => artifacts,
None => postcard::from_bytes(code_memory.wasmtime_info()).err2anyhow()?,
};
engine.allocator().validate_component(
&info.component,
&VMComponentOffsets::new(HostPtr, &info.component),
&|module_index| &static_modules[module_index].module,
)?;
let signatures = TypeCollection::new_for_module(engine, 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<_>>()?;
let realloc_func_type = Arc::new(FuncType::new(
engine,
[ValType::I32, ValType::I32, ValType::I32, ValType::I32],
[ValType::I32],
)) as _;
Ok(Component {
inner: Arc::new(ComponentInner {
ty,
static_modules,
code,
info,
realloc_func_type,
}),
})
}
pub(crate) fn ty(&self) -> TypeComponentIndex {
self.inner.ty
}
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]
}
#[inline]
pub(crate) fn types(&self) -> &Arc<ComponentTypes> {
self.inner.component_types()
}
pub(crate) fn signatures(&self) -> &TypeCollection {
self.inner.code.signatures()
}
pub(crate) fn text(&self) -> &[u8] {
self.inner.code.code_memory().text()
}
pub(crate) fn trampoline_ptrs(&self, index: TrampolineIndex) -> AllCallFuncPointers {
let AllCallFunc {
wasm_call,
array_call,
} = &self.inner.info.trampolines[index];
AllCallFuncPointers {
wasm_call: self.func(wasm_call).cast(),
array_call: unsafe {
mem::transmute::<NonNull<VMFunctionBody>, VMArrayCallFunction>(
self.func(array_call),
)
},
}
}
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())
}
pub(crate) fn runtime_info(&self) -> Arc<dyn ComponentRuntimeInfo> {
self.inner.clone()
}
pub(crate) fn resource_drop_func_ref(&self, dtor: &crate::func::HostFunc) -> VMFuncRef {
assert!(dtor.func_ref().wasm_call.is_none());
let wasm_call = self
.inner
.info
.resource_drop_wasm_to_array_trampoline
.as_ref()
.map(|i| self.func(i).cast());
VMFuncRef {
wasm_call,
..*dtor.func_ref()
}
}
pub fn resources_required(&self) -> Option<ResourcesRequired> {
let mut resources = ResourcesRequired {
num_memories: 0,
max_initial_memory_size: None,
num_tables: 0,
max_initial_table_size: None,
};
for init in &self.env_component().initializers {
match init {
GlobalInitializer::InstantiateModule(inst) => match inst {
InstantiateModule::Static(index, _) => {
let module = self.static_module(*index);
resources.add(&module.resources_required());
}
InstantiateModule::Import(_, _) => {
return None;
}
},
GlobalInitializer::LowerImport { .. }
| GlobalInitializer::ExtractMemory(_)
| GlobalInitializer::ExtractRealloc(_)
| GlobalInitializer::ExtractPostReturn(_)
| GlobalInitializer::Resource(_) => {}
}
}
Some(resources)
}
pub fn image_range(&self) -> Range<*const u8> {
self.inner.code.code_memory().mmap().image_range()
}
}
impl ComponentRuntimeInfo for ComponentInner {
fn component(&self) -> &wasmtime_environ::component::Component {
&self.info.component
}
fn component_types(&self) -> &Arc<ComponentTypes> {
match self.code.types() {
crate::code::Types::Component(types) => types,
crate::code::Types::Module(_) => unreachable!(),
}
}
fn realloc_func_type(&self) -> &Arc<dyn Any + Send + Sync> {
&self.realloc_func_type
}
}
#[cfg(test)]
mod tests {
use crate::component::Component;
use crate::{Config, Engine};
use wasmtime_environ::MemoryInitialization;
#[test]
fn cow_on_by_default() {
let mut config = Config::new();
config.wasm_component_model(true);
let engine = Engine::new(&config).unwrap();
let component = Component::new(
&engine,
r#"
(component
(core module
(memory 1)
(data (i32.const 100) "abcd")
)
)
"#,
)
.unwrap();
for (_, module) in component.inner.static_modules.iter() {
let init = &module.env_module().memory_initialization;
assert!(matches!(init, MemoryInitialization::Static { .. }));
}
}
}