#[cfg(feature = "component-model-async")]
use crate::component::concurrent::Accessor;
use crate::component::func::HostFunc;
use crate::component::instance::RuntimeImport;
use crate::component::matching::{InstanceType, TypeChecker};
use crate::component::types;
use crate::component::{
Component, ComponentNamedList, Instance, InstancePre, Lift, Lower, ResourceType, Val,
};
use crate::hash_map::HashMap;
use crate::prelude::*;
use crate::{AsContextMut, Engine, Module, StoreContextMut};
use alloc::sync::Arc;
use core::marker;
#[cfg(feature = "component-model-async")]
use core::pin::Pin;
use wasmtime_environ::PrimaryMap;
use wasmtime_environ::component::{NameMap, NameMapIntern};
pub struct Linker<T: 'static> {
engine: Engine,
strings: Strings,
map: NameMap<usize, Definition>,
path: Vec<usize>,
allow_shadowing: bool,
_marker: marker::PhantomData<fn() -> T>,
}
impl<T: 'static> Clone for Linker<T> {
fn clone(&self) -> Linker<T> {
Linker {
engine: self.engine.clone(),
strings: self.strings.clone(),
map: self.map.clone(),
path: self.path.clone(),
allow_shadowing: self.allow_shadowing,
_marker: self._marker,
}
}
}
#[derive(Clone, Default)]
pub struct Strings {
string2idx: HashMap<Arc<str>, usize>,
strings: Vec<Arc<str>>,
}
pub struct LinkerInstance<'a, T: 'static> {
engine: &'a Engine,
path: &'a mut Vec<usize>,
path_len: usize,
strings: &'a mut Strings,
map: &'a mut NameMap<usize, Definition>,
allow_shadowing: bool,
_marker: marker::PhantomData<fn() -> T>,
}
#[derive(Clone, Debug)]
pub(crate) enum Definition {
Instance(NameMap<usize, Definition>),
Func(Arc<HostFunc>),
Module(Module),
Resource(ResourceType, Arc<crate::func::HostFunc>),
}
impl<T: 'static> Linker<T> {
pub fn new(engine: &Engine) -> Linker<T> {
Linker {
engine: engine.clone(),
strings: Strings::default(),
map: NameMap::default(),
allow_shadowing: false,
path: Vec::new(),
_marker: marker::PhantomData,
}
}
pub fn engine(&self) -> &Engine {
&self.engine
}
pub fn allow_shadowing(&mut self, allow: bool) -> &mut Self {
self.allow_shadowing = allow;
self
}
pub fn root(&mut self) -> LinkerInstance<'_, T> {
LinkerInstance {
engine: &self.engine,
path: &mut self.path,
path_len: 0,
strings: &mut self.strings,
map: &mut self.map,
allow_shadowing: self.allow_shadowing,
_marker: self._marker,
}
}
pub fn instance(&mut self, name: &str) -> Result<LinkerInstance<'_, T>> {
self.root().into_instance(name)
}
fn typecheck<'a>(&'a self, component: &'a Component) -> Result<TypeChecker<'a>> {
let mut cx = TypeChecker {
engine: &self.engine,
types: component.types(),
strings: &self.strings,
imported_resources: Default::default(),
};
let env_component = component.env_component();
for (_idx, (name, ty)) in env_component.import_types.iter() {
let import = self.map.get(name, &self.strings);
cx.definition(ty, import)
.with_context(|| format!("component imports {desc} `{name}`, but a matching implementation was not found in the linker", desc = ty.desc()))?;
}
Ok(cx)
}
pub fn substituted_component_type(&self, component: &Component) -> Result<types::Component> {
let cx = self.typecheck(&component)?;
Ok(types::Component::from(
component.ty(),
&InstanceType {
types: cx.types,
resources: &cx.imported_resources,
},
))
}
pub fn instantiate_pre(&self, component: &Component) -> Result<InstancePre<T>> {
let cx = self.typecheck(&component)?;
let imported_resources = cx.imported_resources.clone();
let env_component = component.env_component();
let mut imports = PrimaryMap::with_capacity(env_component.imports.len());
for (idx, (import, names)) in env_component.imports.iter() {
let (root, _) = &env_component.import_types[*import];
let mut cur = self.map.get(root, &self.strings).unwrap();
for name in names {
cur = match cur {
Definition::Instance(map) => map.get(&name, &self.strings).unwrap(),
_ => unreachable!(),
};
}
let import = match cur {
Definition::Module(m) => RuntimeImport::Module(m.clone()),
Definition::Func(f) => RuntimeImport::Func(f.clone()),
Definition::Resource(t, dtor) => RuntimeImport::Resource {
ty: *t,
dtor: dtor.clone(),
dtor_funcref: component.resource_drop_func_ref(dtor),
},
Definition::Instance(_) => unreachable!(),
};
let i = imports.push(import);
assert_eq!(i, idx);
}
Ok(unsafe {
InstancePre::new_unchecked(component.clone(), Arc::new(imports), imported_resources)
})
}
pub fn instantiate(
&self,
mut store: impl AsContextMut<Data = T>,
component: &Component,
) -> Result<Instance> {
let store = store.as_context_mut();
store.0.validate_sync_call()?;
self.instantiate_pre(component)?.instantiate(store)
}
#[cfg(feature = "async")]
pub async fn instantiate_async(
&self,
store: impl AsContextMut<Data = T>,
component: &Component,
) -> Result<Instance>
where
T: Send,
{
self.instantiate_pre(component)?
.instantiate_async(store)
.await
}
pub fn define_unknown_imports_as_traps(&mut self, component: &Component) -> Result<()> {
use wasmtime_environ::component::ComponentTypes;
use wasmtime_environ::component::TypeDef;
fn stub_item<T>(
linker: &mut LinkerInstance<T>,
item_name: &str,
item_def: &TypeDef,
parent_instance: Option<&str>,
types: &ComponentTypes,
) -> Result<()> {
if !matches!(item_def, TypeDef::ComponentInstance(_)) && linker.get(item_name).is_some()
{
return Ok(());
}
match item_def {
TypeDef::ComponentFunc(_) => {
let fully_qualified_name = parent_instance
.map(|parent| format!("{parent}#{item_name}"))
.unwrap_or_else(|| item_name.to_owned());
linker.func_new(&item_name, move |_, _, _, _| {
bail!("unknown import: `{fully_qualified_name}` has not been defined")
})?;
}
TypeDef::ComponentInstance(i) => {
let instance = &types[*i];
let mut linker_instance = linker.instance(item_name)?;
for (export_name, export) in instance.exports.iter() {
stub_item(
&mut linker_instance,
export_name,
export,
Some(item_name),
types,
)?;
}
}
TypeDef::Resource(_) => {
let ty = crate::component::ResourceType::host::<()>();
linker.resource(item_name, ty, |_, _| Ok(()))?;
}
TypeDef::Component(_) | TypeDef::Module(_) => {
bail!("unable to define {} imports as traps", item_def.desc())
}
_ => {}
}
Ok(())
}
for (_, (import_name, import_type)) in &component.env_component().import_types {
stub_item(
&mut self.root(),
import_name,
import_type,
None,
component.types(),
)?;
}
Ok(())
}
}
impl<T: 'static> LinkerInstance<'_, T> {
fn as_mut(&mut self) -> LinkerInstance<'_, T> {
LinkerInstance {
engine: self.engine,
path: self.path,
path_len: self.path_len,
strings: self.strings,
map: self.map,
allow_shadowing: self.allow_shadowing,
_marker: self._marker,
}
}
pub fn func_wrap<F, Params, Return>(&mut self, name: &str, func: F) -> Result<()>
where
F: Fn(StoreContextMut<T>, Params) -> Result<Return> + Send + Sync + 'static,
Params: ComponentNamedList + Lift + 'static,
Return: ComponentNamedList + Lower + 'static,
{
self.insert(name, Definition::Func(HostFunc::func_wrap(func)))?;
Ok(())
}
#[cfg(feature = "async")]
pub fn func_wrap_async<Params, Return, F>(&mut self, name: &str, f: F) -> Result<()>
where
F: Fn(
StoreContextMut<'_, T>,
Params,
) -> Box<dyn Future<Output = Result<Return>> + Send + '_>
+ Send
+ Sync
+ 'static,
Params: ComponentNamedList + Lift + 'static,
Return: ComponentNamedList + Lower + 'static,
{
self.insert(name, Definition::Func(HostFunc::func_wrap_async(f)))?;
Ok(())
}
#[cfg(feature = "component-model-async")]
pub fn func_wrap_concurrent<Params, Return, F>(&mut self, name: &str, f: F) -> Result<()>
where
T: 'static,
F: Fn(&Accessor<T>, Params) -> Pin<Box<dyn Future<Output = Result<Return>> + Send + '_>>
+ Send
+ Sync
+ 'static,
Params: ComponentNamedList + Lift + 'static,
Return: ComponentNamedList + Lower + 'static,
{
if !self.engine.tunables().concurrency_support {
bail!("concurrent host functions require `Config::concurrency_support`");
}
self.insert(name, Definition::Func(HostFunc::func_wrap_concurrent(f)))?;
Ok(())
}
pub fn func_new(
&mut self,
name: &str,
func: impl Fn(StoreContextMut<'_, T>, types::ComponentFunc, &[Val], &mut [Val]) -> Result<()>
+ Send
+ Sync
+ 'static,
) -> Result<()> {
self.insert(name, Definition::Func(HostFunc::func_new(func)))?;
Ok(())
}
#[cfg(feature = "async")]
pub fn func_new_async<F>(&mut self, name: &str, func: F) -> Result<()>
where
F: for<'a> Fn(
StoreContextMut<'a, T>,
types::ComponentFunc,
&'a [Val],
&'a mut [Val],
) -> Box<dyn Future<Output = Result<()>> + Send + 'a>
+ Send
+ Sync
+ 'static,
{
self.insert(name, Definition::Func(HostFunc::func_new_async(func)))?;
Ok(())
}
#[cfg(feature = "component-model-async")]
pub fn func_new_concurrent<F>(&mut self, name: &str, f: F) -> Result<()>
where
T: 'static,
F: for<'a> Fn(
&'a Accessor<T>,
types::ComponentFunc,
&'a [Val],
&'a mut [Val],
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
+ Send
+ Sync
+ 'static,
{
if !self.engine.tunables().concurrency_support {
bail!("concurrent host functions require `Config::concurrency_support`");
}
self.insert(name, Definition::Func(HostFunc::func_new_concurrent(f)))?;
Ok(())
}
pub fn module(&mut self, name: &str, module: &Module) -> Result<()> {
self.insert(name, Definition::Module(module.clone()))?;
Ok(())
}
pub fn resource(
&mut self,
name: &str,
ty: ResourceType,
dtor: impl Fn(StoreContextMut<'_, T>, u32) -> Result<()> + Send + Sync + 'static,
) -> Result<()> {
let dtor = Arc::new(crate::func::HostFunc::wrap(
&self.engine,
move |mut cx: crate::Caller<'_, T>, (param,): (u32,)| dtor(cx.as_context_mut(), param),
));
self.insert(name, Definition::Resource(ty, dtor))?;
Ok(())
}
#[cfg(feature = "async")]
pub fn resource_async<F>(&mut self, name: &str, ty: ResourceType, dtor: F) -> Result<()>
where
T: Send,
F: Fn(StoreContextMut<'_, T>, u32) -> Box<dyn Future<Output = Result<()>> + Send + '_>
+ Send
+ Sync
+ 'static,
{
let dtor = Arc::new(crate::func::HostFunc::wrap_async(
&self.engine,
move |cx: crate::Caller<'_, T>, (param,): (u32,)| dtor(cx.into(), param),
));
self.insert(name, Definition::Resource(ty, dtor))?;
Ok(())
}
#[cfg(feature = "component-model-async")]
pub fn resource_concurrent<F>(&mut self, name: &str, ty: ResourceType, dtor: F) -> Result<()>
where
T: Send + 'static,
F: Fn(&Accessor<T>, u32) -> Pin<Box<dyn Future<Output = Result<()>> + Send + '_>>
+ Send
+ Sync
+ 'static,
{
if !self.engine.tunables().concurrency_support {
bail!("concurrent host functions require `Config::concurrency_support`");
}
let dtor = Arc::new(dtor);
let dtor = Arc::new(crate::func::HostFunc::wrap_async(
&self.engine,
move |mut cx: crate::Caller<'_, T>, (param,): (u32,)| {
let dtor = dtor.clone();
Box::new(async move {
let mut store = cx.as_context_mut();
let accessor =
&Accessor::new(crate::store::StoreToken::new(store.as_context_mut()));
let mut future = std::pin::pin!(dtor(accessor, param));
std::future::poll_fn(|cx| {
crate::component::concurrent::tls::set(store.0, || future.as_mut().poll(cx))
})
.await
})
},
));
self.insert(name, Definition::Resource(ty, dtor))?;
Ok(())
}
pub fn instance(&mut self, name: &str) -> Result<LinkerInstance<'_, T>> {
self.as_mut().into_instance(name)
}
pub fn into_instance(mut self, name: &str) -> Result<Self> {
let name = self.insert(name, Definition::Instance(NameMap::default()))?;
self.map = match self.map.raw_get_mut(&name) {
Some(Definition::Instance(map)) => map,
_ => unreachable!(),
};
self.path.truncate(self.path_len);
self.path.push(name);
self.path_len += 1;
Ok(self)
}
fn insert(&mut self, name: &str, item: Definition) -> Result<usize> {
self.map
.insert(name, self.strings, self.allow_shadowing, item)
}
fn get(&self, name: &str) -> Option<&Definition> {
self.map.get(name, self.strings)
}
}
impl NameMapIntern for Strings {
type Key = usize;
fn intern(&mut self, string: &str) -> usize {
if let Some(idx) = self.string2idx.get(string) {
return *idx;
}
let string: Arc<str> = string.into();
let idx = self.strings.len();
self.strings.push(string.clone());
self.string2idx.insert(string, idx);
idx
}
fn lookup(&self, string: &str) -> Option<usize> {
self.string2idx.get(string).cloned()
}
}