#![deny(warnings)]
#![forbid(unsafe_code)]
#![warn(missing_docs)]
#![warn(clippy::missing_docs_in_private_items)]
mod abi;
mod func;
mod identifier;
mod require_matches;
mod types;
mod values;
use std::any::*;
use std::sync::atomic::*;
use std::sync::*;
use anyhow::*;
use fxhash::*;
use id_arena::*;
use slab::*;
pub use wasm_runtime_layer::Engine;
use wasm_runtime_layer::*;
use wasmtime_environ::component::*;
use wit_component::*;
use wit_parser::*;
pub use crate::func::Func;
pub use crate::func::*;
pub use crate::identifier::PackageName;
pub use crate::identifier::*;
use crate::require_matches::*;
pub use crate::types::*;
pub use crate::types::{FuncType, ValueType, VariantCase};
pub use crate::values::*;
pub use crate::values::{Enum, Flags, Record, Tuple, Value, Variant};
#[derive(Clone, Debug)]
pub struct Component(Arc<ComponentInner>);
impl Component {
pub fn new<E: backend::WasmEngine>(engine: &Engine<E>, bytes: &[u8]) -> Result<Self> {
let (inner, types) = Self::generate_component(engine, bytes)?;
Ok(Self(Arc::new(Self::generate_resources(
Self::load_exports(Self::extract_initializers(inner, &types)?, &types)?,
)?)))
}
pub fn exports(&self) -> &ComponentTypes {
&self.0.export_types
}
pub fn imports(&self) -> &ComponentTypes {
&self.0.import_types
}
pub fn package(&self) -> &PackageIdentifier {
&self.0.package
}
fn generate_component<E: backend::WasmEngine>(
engine: &Engine<E>,
bytes: &[u8],
) -> Result<(ComponentInner, wasmtime_environ::component::ComponentTypes)> {
static ID_COUNTER: AtomicU64 = AtomicU64::new(0);
let decoded = wit_component::decode(bytes)
.context("Could not decode component information from bytes.")?;
let (mut resolve, world_id) = match decoded {
DecodedWasm::WitPackage(..) => bail!("Cannot instantiate WIT package as module."),
DecodedWasm::Component(resolve, id) => (resolve, id),
};
let adapter_vec = wasmtime_environ::ScopeVec::new();
let (translation, module_data, component_types) =
Self::translate_modules(bytes, &adapter_vec)?;
let export_mapping = Self::generate_export_mapping(&module_data);
let mut modules =
FxHashMap::with_capacity_and_hasher(module_data.len(), Default::default());
for (id, module) in module_data {
modules.insert(
id,
ModuleTranslation {
module: Module::new(engine, std::io::Cursor::new(module.wasm))?,
translation: module.module,
},
);
}
let mut size_align = SizeAlign::default();
size_align.fill(&resolve);
let package = (&resolve.packages[resolve.worlds[world_id]
.package
.context("No package associated with world.")?]
.name)
.into();
let package_identifiers = Self::generate_package_identifiers(&resolve)?;
let interface_identifiers =
Self::generate_interface_identifiers(&resolve, &package_identifiers)?;
let type_identifiers =
Self::generate_type_identifiers(&mut resolve, &interface_identifiers);
Ok((
ComponentInner {
export_mapping,
export_names: FxHashMap::default(),
import_types: ComponentTypes::new(),
export_types: ComponentTypes::new(),
export_info: ExportTypes::default(),
extracted_memories: FxHashMap::default(),
extracted_reallocs: FxHashMap::default(),
extracted_post_returns: FxHashMap::default(),
id: ID_COUNTER.fetch_add(1, Ordering::AcqRel),
generated_trampolines: FxHashMap::default(),
instance_modules: wasmtime_environ::PrimaryMap::default(),
interface_identifiers,
type_identifiers,
modules,
resource_map: vec![
TypeResourceTableIndex::from_u32(u32::MAX - 1);
resolve.types.len()
],
resolve,
size_align,
translation,
world_id,
package,
},
component_types,
))
}
fn generate_type_identifiers(
resolve: &mut Resolve,
interface_ids: &[InterfaceIdentifier],
) -> Vec<Option<TypeIdentifier>> {
let mut ids = Vec::with_capacity(resolve.types.len());
for (_, def) in &mut resolve.types {
if let Some(name) = std::mem::take(&mut def.name) {
ids.push(Some(TypeIdentifier::new(
name,
match &def.owner {
TypeOwner::Interface(x) => Some(interface_ids[x.index()].clone()),
_ => None,
},
)));
} else {
ids.push(None);
}
}
ids
}
fn generate_export_mapping(
module_data: &wasmtime_environ::PrimaryMap<
StaticModuleIndex,
wasmtime_environ::ModuleTranslation,
>,
) -> FxHashMap<StaticModuleIndex, FxHashMap<wasmtime_environ::EntityIndex, String>> {
let mut export_mapping =
FxHashMap::with_capacity_and_hasher(module_data.len(), Default::default());
for (idx, module) in module_data {
let entry: &mut FxHashMap<wasmtime_environ::EntityIndex, String> =
export_mapping.entry(idx).or_default();
for (name, index) in module.module.exports.clone() {
entry.insert(index, name);
}
}
export_mapping
}
fn generate_resources(mut inner: ComponentInner) -> Result<ComponentInner> {
for (_key, item) in &inner.resolve.worlds[inner.world_id].imports {
match item {
WorldItem::Type(x) => {
if inner.resolve.types[*x].kind == TypeDefKind::Resource {
if let Some(name) = &inner.resolve.types[*x].name {
ensure!(
inner
.import_types
.root
.resources
.insert(
name.as_str().into(),
ResourceType::from_resolve(
inner.type_identifiers[x.index()].clone(),
*x,
&inner,
None
)?
)
.is_none(),
"Duplicate resource import."
);
}
}
}
WorldItem::Interface(x) => {
for (name, ty) in &inner.resolve.interfaces[*x].types {
if inner.resolve.types[*ty].kind == TypeDefKind::Resource {
let ty = ResourceType::from_resolve(
inner.type_identifiers[ty.index()].clone(),
*ty,
&inner,
None,
)?;
let entry = inner
.import_types
.instances
.entry(inner.interface_identifiers[x.index()].clone())
.or_insert_with(ComponentTypesInstance::new);
ensure!(
entry.resources.insert(name.as_str().into(), ty).is_none(),
"Duplicate resource import."
);
}
}
}
_ => {}
}
}
for (_key, item) in &inner.resolve.worlds[inner.world_id].exports {
match item {
WorldItem::Type(x) => {
if inner.resolve.types[*x].kind == TypeDefKind::Resource {
if let Some(name) = &inner.resolve.types[*x].name {
ensure!(
inner
.export_types
.root
.resources
.insert(
name.as_str().into(),
ResourceType::from_resolve(
inner.type_identifiers[x.index()].clone(),
*x,
&inner,
None
)?
)
.is_none(),
"Duplicate resource export."
);
}
}
}
WorldItem::Interface(x) => {
for (name, ty) in &inner.resolve.interfaces[*x].types {
if inner.resolve.types[*ty].kind == TypeDefKind::Resource {
let ty = ResourceType::from_resolve(
inner.type_identifiers[ty.index()].clone(),
*ty,
&inner,
None,
)?;
let entry = inner
.export_types
.instances
.entry(inner.interface_identifiers[x.index()].clone())
.or_insert_with(ComponentTypesInstance::new);
ensure!(
entry.resources.insert(name.as_str().into(), ty).is_none(),
"Duplicate resource export."
);
}
}
}
_ => {}
}
}
Ok(inner)
}
fn generate_package_identifiers(resolve: &Resolve) -> Result<Vec<PackageIdentifier>> {
let mut res = Vec::with_capacity(resolve.packages.len());
for (_, pkg) in &resolve.packages {
res.push(PackageIdentifier::from(&pkg.name));
}
Ok(res)
}
fn generate_interface_identifiers(
resolve: &Resolve,
packages: &[PackageIdentifier],
) -> Result<Vec<InterfaceIdentifier>> {
let mut res = Vec::with_capacity(resolve.interfaces.len());
for (_, iface) in &resolve.interfaces {
let pkg = iface
.package
.context("Interface did not have associated package.")?;
res.push(InterfaceIdentifier::new(
packages[pkg.index()].clone(),
iface
.name
.as_deref()
.context("Exported interface did not have valid name.")?,
));
}
Ok(res)
}
fn extract_initializers(
mut inner: ComponentInner,
types: &wasmtime_environ::component::ComponentTypes,
) -> Result<ComponentInner> {
let lowering_options = Self::get_lowering_options_and_extract_trampolines(
&inner.translation.trampolines,
&mut inner.generated_trampolines,
)?;
let mut imports = FxHashMap::default();
for (key, _) in &inner.resolve.worlds[inner.world_id].imports {
let name = inner.resolve.name_world_key(key);
imports.insert(name, key.clone());
}
let _root_name = Arc::<str>::from("$root");
let mut destructors = FxHashMap::default();
for initializer in &inner.translation.component.initializers {
match initializer {
GlobalInitializer::InstantiateModule(InstantiateModule::Static(idx, _def)) => {
inner.instance_modules.push(*idx);
}
GlobalInitializer::ExtractMemory(ExtractMemory { index, export }) => {
ensure!(
inner
.extracted_memories
.insert(*index, export.clone())
.is_none(),
"Extracted the same memory more than once."
);
}
GlobalInitializer::ExtractRealloc(ExtractRealloc { index, def }) => {
if let CoreDef::Export(export) = def {
ensure!(
inner
.extracted_reallocs
.insert(*index, export.clone())
.is_none(),
"Extracted the same memory more than once."
);
} else {
bail!("Unexpected post return definition type.");
}
}
GlobalInitializer::ExtractPostReturn(ExtractPostReturn { index, def }) => {
if let CoreDef::Export(export) = def {
ensure!(
inner
.extracted_post_returns
.insert(*index, export.clone())
.is_none(),
"Extracted the same memory more than once."
);
} else {
bail!("Unexpected post return definition type.");
}
}
GlobalInitializer::LowerImport { index, import } => {
let (idx, lowering_opts, index_ty) = lowering_options[*index];
let (import_index, path) = &inner.translation.component.imports[*import];
let (import_name, _) = &inner.translation.component.import_types[*import_index];
let world_key = &imports[import_name];
let imp = match &inner.resolve.worlds[inner.world_id].imports[world_key] {
WorldItem::Function(func) => {
assert_eq!(path.len(), 0);
ComponentImport {
instance: None,
name: import_name.as_str().into(),
func: func.clone(),
options: lowering_opts.clone(),
}
}
WorldItem::Interface(i) => {
assert_eq!(path.len(), 1);
let iface = &inner.resolve.interfaces[*i];
let func = &iface.functions[&path[0]];
ComponentImport {
instance: Some(inner.interface_identifiers[i.index()].clone()),
name: path[0].as_str().into(),
func: func.clone(),
options: lowering_opts.clone(),
}
}
WorldItem::Type(_) => unreachable!(),
};
let ty = crate::types::FuncType::from_component(&imp.func, &inner, None)?;
let inst = if let Some(inst) = &imp.instance {
inner
.import_types
.instances
.entry(inst.clone())
.or_insert_with(ComponentTypesInstance::new)
} else {
&mut inner.import_types.root
};
Self::update_resource_map(
&inner.resolve,
types,
&imp.func,
index_ty,
&mut inner.resource_map,
);
ensure!(
inst.functions.insert(imp.name.clone(), ty).is_none(),
"Attempted to insert duplicate import."
);
ensure!(
inner
.generated_trampolines
.insert(idx, GeneratedTrampoline::ImportedFunction(imp))
.is_none(),
"Attempted to insert duplicate import."
);
}
GlobalInitializer::Resource(x) => {
if let Some(destructor) = x.dtor.clone() {
ensure!(
destructors.insert(x.index, destructor).is_none(),
"Attempted to define duplicate resource."
);
}
}
_ => bail!("Not yet implemented {initializer:?}."),
}
}
for trampoline in inner.generated_trampolines.values_mut() {
if let GeneratedTrampoline::ResourceDrop(x, destructor) = trampoline {
let resource = &types[*x];
if let Some(resource_idx) = inner
.translation
.component
.defined_resource_index(resource.ty)
{
*destructor = destructors.remove(&resource_idx);
}
}
}
Ok(inner)
}
fn get_lowering_options_and_extract_trampolines<'a>(
trampolines: &'a wasmtime_environ::PrimaryMap<TrampolineIndex, Trampoline>,
output_trampolines: &mut FxHashMap<TrampolineIndex, GeneratedTrampoline>,
) -> Result<
wasmtime_environ::PrimaryMap<
LoweredIndex,
(TrampolineIndex, &'a CanonicalOptions, TypeFuncIndex),
>,
> {
let mut lowers = wasmtime_environ::PrimaryMap::default();
for (idx, trampoline) in trampolines {
match trampoline {
Trampoline::LowerImport {
index,
lower_ty,
options,
} => assert!(
lowers.push((idx, options, *lower_ty)) == *index,
"Indices did not match."
),
Trampoline::ResourceNew(x) => {
output_trampolines.insert(idx, GeneratedTrampoline::ResourceNew(*x));
}
Trampoline::ResourceRep(x) => {
output_trampolines.insert(idx, GeneratedTrampoline::ResourceRep(*x));
}
Trampoline::ResourceDrop(x) => {
output_trampolines.insert(idx, GeneratedTrampoline::ResourceDrop(*x, None));
}
_ => bail!("Trampoline not implemented."),
}
}
Ok(lowers)
}
fn translate_modules<'a>(
bytes: &'a [u8],
scope: &'a wasmtime_environ::ScopeVec<u8>,
) -> Result<(
ComponentTranslation,
wasmtime_environ::PrimaryMap<StaticModuleIndex, wasmtime_environ::ModuleTranslation<'a>>,
wasmtime_environ::component::ComponentTypes,
)> {
let tunables = wasmtime_environ::Tunables::default_u32();
let mut types = ComponentTypesBuilder::default();
let mut validator = Self::create_component_validator();
let (translation, modules) = Translator::new(&tunables, &mut validator, &mut types, scope)
.translate(bytes)
.context("Could not translate input component to core WASM.")?;
Ok((
translation,
modules,
types.finish(&Default::default(), [], []).0,
))
}
fn load_exports(
mut inner: ComponentInner,
types: &wasmtime_environ::component::ComponentTypes,
) -> Result<ComponentInner> {
Self::export_names(&mut inner);
for (export_name, export) in &inner.translation.component.exports {
let world_key = &inner.export_names[export_name];
let item = &inner.resolve.worlds[inner.world_id].exports[world_key];
match export {
wasmtime_environ::component::Export::LiftedFunction { ty, func, options } => {
let f = match item {
WorldItem::Function(f) => f,
WorldItem::Interface(_) | WorldItem::Type(_) => unreachable!(),
};
Self::update_resource_map(
&inner.resolve,
types,
f,
*ty,
&mut inner.resource_map,
);
let export_name = Arc::<str>::from(export_name.as_str());
let ty = crate::types::FuncType::from_component(f, &inner, None)?;
ensure!(
inner
.export_types
.root
.functions
.insert(export_name.clone(), ty.clone())
.is_none(),
"Duplicate function definition."
);
ensure!(
inner
.export_info
.root
.functions
.insert(
export_name,
ComponentExport {
options: options.clone(),
def: match func {
CoreDef::Export(x) => x.clone(),
_ => unreachable!(),
},
func: f.clone(),
ty
}
)
.is_none(),
"Duplicate function definition."
);
}
wasmtime_environ::component::Export::Instance { exports, .. } => {
let id = match item {
WorldItem::Interface(id) => *id,
WorldItem::Function(_) | WorldItem::Type(_) => unreachable!(),
};
for (func_name, export) in exports {
let (func, options, ty) = match export {
wasmtime_environ::component::Export::LiftedFunction {
func,
options,
ty,
} => (func, options, ty),
wasmtime_environ::component::Export::Type(_) => continue, _ => unreachable!(),
};
let f = &inner.resolve.interfaces[id].functions[func_name];
Self::update_resource_map(
&inner.resolve,
types,
f,
*ty,
&mut inner.resource_map,
);
let exp = ComponentExport {
options: options.clone(),
def: match func {
CoreDef::Export(x) => x.clone(),
_ => unreachable!(),
},
func: f.clone(),
ty: crate::types::FuncType::from_component(f, &inner, None)?,
};
let func_name = Arc::<str>::from(func_name.as_str());
ensure!(
inner
.export_types
.instances
.entry(inner.interface_identifiers[id.index()].clone())
.or_insert_with(ComponentTypesInstance::new)
.functions
.insert(func_name.clone(), exp.ty.clone())
.is_none(),
"Duplicate function definition."
);
ensure!(
inner
.export_info
.instances
.entry(inner.interface_identifiers[id.index()].clone())
.or_default()
.functions
.insert(func_name, exp)
.is_none(),
"Duplicate function definition."
);
}
}
wasmtime_environ::component::Export::Type(_) => {}
wasmtime_environ::component::Export::ModuleStatic(_) => {
bail!("Not yet implemented.")
}
wasmtime_environ::component::Export::ModuleImport { .. } => {
bail!("Not yet implemented.")
}
}
}
Ok(inner)
}
fn export_names(inner: &mut ComponentInner) {
let to_iter = &inner.resolve.worlds[inner.world_id].exports;
let mut exports = FxHashMap::with_capacity_and_hasher(to_iter.len(), Default::default());
for (key, _) in to_iter {
let name = inner.resolve.name_world_key(key);
exports.insert(name, key.clone());
}
inner.export_names = exports;
}
fn update_resource_map(
resolve: &Resolve,
types: &wasmtime_environ::component::ComponentTypes,
func: &Function,
ty_func_idx: TypeFuncIndex,
map: &mut Vec<TypeResourceTableIndex>,
) {
let params_ty = &types[types[ty_func_idx].params];
for ((_, ty), iface_ty) in func.params.iter().zip(params_ty.types.iter()) {
Self::connect_resources(resolve, types, ty, iface_ty, map);
}
let results_ty = &types[types[ty_func_idx].results];
for (ty, iface_ty) in func.results.iter_types().zip(results_ty.types.iter()) {
Self::connect_resources(resolve, types, ty, iface_ty, map);
}
}
fn connect_resources(
resolve: &Resolve,
types: &wasmtime_environ::component::ComponentTypes,
ty: &Type,
iface_ty: &InterfaceType,
map: &mut Vec<TypeResourceTableIndex>,
) {
let Type::Id(id) = ty else { return };
match (&resolve.types[*id].kind, iface_ty) {
(TypeDefKind::Flags(_), InterfaceType::Flags(_))
| (TypeDefKind::Enum(_), InterfaceType::Enum(_)) => {}
(TypeDefKind::Record(t1), InterfaceType::Record(t2)) => {
let t2 = &types[*t2];
for (f1, f2) in t1.fields.iter().zip(t2.fields.iter()) {
Self::connect_resources(resolve, types, &f1.ty, &f2.ty, map);
}
}
(
TypeDefKind::Handle(Handle::Own(t1) | Handle::Borrow(t1)),
InterfaceType::Own(t2) | InterfaceType::Borrow(t2),
) => {
map[t1.index()] = *t2;
}
(TypeDefKind::Tuple(t1), InterfaceType::Tuple(t2)) => {
let t2 = &types[*t2];
for (f1, f2) in t1.types.iter().zip(t2.types.iter()) {
Self::connect_resources(resolve, types, f1, f2, map);
}
}
(TypeDefKind::Variant(t1), InterfaceType::Variant(t2)) => {
let t2 = &types[*t2];
for (f1, f2) in t1.cases.iter().zip(t2.cases.iter()) {
if let Some(t1) = &f1.ty {
Self::connect_resources(resolve, types, t1, f2.ty.as_ref().unwrap(), map);
}
}
}
(TypeDefKind::Option(t1), InterfaceType::Option(t2)) => {
let t2 = &types[*t2];
Self::connect_resources(resolve, types, t1, &t2.ty, map);
}
(TypeDefKind::Result(t1), InterfaceType::Result(t2)) => {
let t2 = &types[*t2];
if let Some(t1) = &t1.ok {
Self::connect_resources(resolve, types, t1, &t2.ok.unwrap(), map);
}
if let Some(t1) = &t1.err {
Self::connect_resources(resolve, types, t1, &t2.err.unwrap(), map);
}
}
(TypeDefKind::List(t1), InterfaceType::List(t2)) => {
let t2 = &types[*t2];
Self::connect_resources(resolve, types, t1, &t2.element, map);
}
(TypeDefKind::Type(ty), _) => {
Self::connect_resources(resolve, types, ty, iface_ty, map);
}
(_, _) => unreachable!(),
}
}
fn create_component_validator() -> wasmtime_environ::wasmparser::Validator {
wasmtime_environ::wasmparser::Validator::new_with_features(
wasmtime_environ::wasmparser::WasmFeatures::all(),
)
}
}
struct ComponentInner {
pub export_mapping:
FxHashMap<StaticModuleIndex, FxHashMap<wasmtime_environ::EntityIndex, String>>,
pub export_names: FxHashMap<String, WorldKey>,
pub export_types: ComponentTypes,
pub export_info: ExportTypes,
pub extracted_memories: FxHashMap<RuntimeMemoryIndex, CoreExport<MemoryIndex>>,
pub extracted_reallocs:
FxHashMap<RuntimeReallocIndex, CoreExport<wasmtime_environ::EntityIndex>>,
pub extracted_post_returns:
FxHashMap<RuntimePostReturnIndex, CoreExport<wasmtime_environ::EntityIndex>>,
pub resource_map: Vec<TypeResourceTableIndex>,
pub generated_trampolines: FxHashMap<TrampolineIndex, GeneratedTrampoline>,
pub id: u64,
pub import_types: ComponentTypes,
pub instance_modules: wasmtime_environ::PrimaryMap<RuntimeInstanceIndex, StaticModuleIndex>,
pub interface_identifiers: Vec<InterfaceIdentifier>,
pub type_identifiers: Vec<Option<TypeIdentifier>>,
pub modules: FxHashMap<StaticModuleIndex, ModuleTranslation>,
pub resolve: Resolve,
pub size_align: SizeAlign,
pub translation: ComponentTranslation,
pub world_id: Id<World>,
pub package: PackageIdentifier,
}
impl std::fmt::Debug for ComponentInner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ComponentInner").finish()
}
}
struct ModuleTranslation {
pub module: Module,
pub translation: wasmtime_environ::Module,
}
#[derive(Debug, Default)]
struct ExportTypes {
root: ExportTypesInstance,
instances: FxHashMap<InterfaceIdentifier, ExportTypesInstance>,
}
#[derive(Debug, Default)]
struct ExportTypesInstance {
functions: FxHashMap<Arc<str>, ComponentExport>,
}
#[derive(Debug)]
pub struct ComponentTypes {
root: ComponentTypesInstance,
instances: FxHashMap<InterfaceIdentifier, ComponentTypesInstance>,
}
impl ComponentTypes {
pub(crate) fn new() -> Self {
Self {
root: ComponentTypesInstance::new(),
instances: FxHashMap::default(),
}
}
pub fn root(&self) -> &ComponentTypesInstance {
&self.root
}
pub fn instance(&self, name: &InterfaceIdentifier) -> Option<&ComponentTypesInstance> {
self.instances.get(name)
}
pub fn instances(
&self,
) -> impl Iterator<Item = (&'_ InterfaceIdentifier, &'_ ComponentTypesInstance)> {
self.instances.iter()
}
}
#[derive(Debug)]
pub struct ComponentTypesInstance {
functions: FxHashMap<Arc<str>, crate::types::FuncType>,
resources: FxHashMap<Arc<str>, ResourceType>,
}
impl ComponentTypesInstance {
pub(crate) fn new() -> Self {
Self {
functions: FxHashMap::default(),
resources: FxHashMap::default(),
}
}
pub fn func(&self, name: impl AsRef<str>) -> Option<crate::types::FuncType> {
self.functions.get(name.as_ref()).cloned()
}
pub fn funcs(&self) -> impl Iterator<Item = (&'_ str, crate::types::FuncType)> {
self.functions.iter().map(|(k, v)| (&**k, v.clone()))
}
pub fn resource(&self, name: impl AsRef<str>) -> Option<ResourceType> {
self.resources.get(name.as_ref()).cloned()
}
pub fn resources(&self) -> impl Iterator<Item = (&'_ str, crate::types::ResourceType)> {
self.resources.iter().map(|(k, v)| (&**k, v.clone()))
}
}
#[derive(Clone, Debug, Default)]
pub struct Linker {
root: LinkerInstance,
instances: FxHashMap<InterfaceIdentifier, LinkerInstance>,
}
impl Linker {
pub fn root(&self) -> &LinkerInstance {
&self.root
}
pub fn root_mut(&mut self) -> &mut LinkerInstance {
&mut self.root
}
pub fn define_instance(&mut self, name: InterfaceIdentifier) -> Result<&mut LinkerInstance> {
if self.instance(&name).is_none() {
Ok(self.instances.entry(name).or_default())
} else {
bail!("Duplicate instance definition.");
}
}
pub fn instance(&self, name: &InterfaceIdentifier) -> Option<&LinkerInstance> {
self.instances.get(name)
}
pub fn instance_mut(&mut self, name: &InterfaceIdentifier) -> Option<&mut LinkerInstance> {
self.instances.get_mut(name)
}
pub fn instances(
&self,
) -> impl ExactSizeIterator<Item = (&'_ InterfaceIdentifier, &'_ LinkerInstance)> {
self.instances.iter()
}
pub fn instances_mut(
&mut self,
) -> impl ExactSizeIterator<Item = (&'_ InterfaceIdentifier, &'_ mut LinkerInstance)> {
self.instances.iter_mut()
}
pub fn instantiate(&self, ctx: impl AsContextMut, component: &Component) -> Result<Instance> {
Instance::new(ctx, component, self)
}
}
#[derive(Clone, Debug, Default)]
pub struct LinkerInstance {
functions: FxHashMap<Arc<str>, crate::func::Func>,
resources: FxHashMap<Arc<str>, ResourceType>,
}
impl LinkerInstance {
pub fn define_func(
&mut self,
name: impl Into<Arc<str>>,
func: crate::func::Func,
) -> Result<()> {
let n = Into::<Arc<str>>::into(name);
if self.functions.contains_key(&n) {
bail!("Duplicate function definition.");
}
self.functions.insert(n, func);
Ok(())
}
pub fn func(&self, name: impl AsRef<str>) -> Option<crate::func::Func> {
self.functions.get(name.as_ref()).cloned()
}
pub fn define_resource(
&mut self,
name: impl Into<Arc<str>>,
resource_ty: ResourceType,
) -> Result<()> {
ensure!(
resource_ty.is_instantiated(),
"Cannot link with abstract resource type."
);
let n = Into::<Arc<str>>::into(name);
if self.resources.contains_key(&n) {
bail!("Duplicate resource definition.");
}
self.resources.insert(n, resource_ty);
Ok(())
}
pub fn resource(&self, name: impl AsRef<str>) -> Option<ResourceType> {
self.resources.get(name.as_ref()).cloned()
}
pub fn funcs(&self) -> impl Iterator<Item = (&'_ str, crate::func::Func)> {
self.functions.iter().map(|(k, v)| (&**k, v.clone()))
}
pub fn resources(&self) -> impl Iterator<Item = (&'_ str, ResourceType)> {
self.resources.iter().map(|(k, v)| (&**k, v.clone()))
}
}
#[derive(Clone, Debug)]
pub struct Instance(Arc<InstanceInner>);
impl Instance {
pub(crate) fn new(
mut ctx: impl AsContextMut,
component: &Component,
linker: &Linker,
) -> Result<Self> {
static ID_COUNTER: AtomicU64 = AtomicU64::new(0);
let mut instance_flags = wasmtime_environ::PrimaryMap::default();
for _i in 0..component.0.instance_modules.len() {
instance_flags.push(Global::new(
ctx.as_context_mut().inner,
wasm_runtime_layer::Value::I32(
wasmtime_environ::component::FLAG_MAY_LEAVE
| wasmtime_environ::component::FLAG_MAY_ENTER,
),
true,
));
}
let id = ID_COUNTER.fetch_add(1, Ordering::AcqRel);
let map = Self::create_resource_instantiation_map(id, component, linker)?;
let types = Self::generate_types(component, &map)?;
let resource_tables = Mutex::new(vec![
HandleTable::default();
component.0.translation.component.num_resource_tables
]);
let instance = InstanceInner {
component: component.clone(),
exports: Exports::new(),
id,
instances: Default::default(),
instance_flags,
state_table: Arc::new(StateTable {
dropped: AtomicBool::new(false),
resource_tables,
}),
types,
store_id: ctx.as_context().inner.data().id,
};
let initialized = Self::global_initialize(instance, &mut ctx, linker, &map)?;
let exported = Self::load_exports(initialized, &ctx, &map)?;
Ok(Self(Arc::new_cyclic(|w| {
Self::fill_exports(exported, w.clone())
})))
}
pub fn component(&self) -> &Component {
&self.0.component
}
pub fn exports(&self) -> &Exports {
&self.0.exports
}
pub fn drop<T, E: backend::WasmEngine>(&self, ctx: &mut Store<T, E>) -> Result<Vec<Error>> {
ensure!(self.0.store_id == ctx.inner.data().id, "Incorrect store.");
self.0.state_table.dropped.store(true, Ordering::Release);
let mut errors = Vec::new();
let mut tables = self
.0
.state_table
.resource_tables
.try_lock()
.expect("Could not lock resource tables.");
for table in &mut *tables {
if let Some(destructor) = table.destructor.as_ref() {
for (_, val) in table.array.iter() {
if let Err(x) = destructor.call(
&mut ctx.inner,
&[wasm_runtime_layer::Value::I32(val.rep)],
&mut [],
) {
errors.push(x);
}
}
}
}
Ok(errors)
}
fn fill_exports(mut inner: InstanceInner, final_ptr: Weak<InstanceInner>) -> InstanceInner {
for inst in inner.exports.instances.values_mut() {
inst.instance = final_ptr.clone();
}
inner.exports.root.instance = final_ptr;
inner
}
fn generate_types(
component: &Component,
map: &FxHashMap<ResourceType, ResourceType>,
) -> Result<Arc<[crate::types::ValueType]>> {
let mut types = Vec::with_capacity(component.0.resolve.types.len());
for (mut id, mut val) in &component.0.resolve.types {
assert!(
types.len() == id.index(),
"Type definition IDs were not equal."
);
while let TypeDefKind::Type(Type::Id(id_ref)) = val.kind {
id = id_ref;
val = &component.0.resolve.types[id_ref];
}
if val.kind == TypeDefKind::Resource {
types.push(crate::types::ValueType::Bool);
} else {
types.push(crate::types::ValueType::from_component_typedef(
id,
&component.0,
Some(map),
)?);
}
}
Ok(types.into())
}
fn create_resource_instantiation_map(
instance_id: u64,
component: &Component,
linker: &Linker,
) -> Result<FxHashMap<ResourceType, ResourceType>> {
let mut types = FxHashMap::default();
for (name, resource) in component.imports().root().resources() {
let instantiated = linker
.root()
.resource(name)
.ok_or_else(|| Error::msg(format!("Could not find resource {name} in linker.")))?;
types.insert(resource, instantiated);
}
for (id, interface) in component.imports().instances() {
for (name, resource) in interface.resources() {
let instantiated = linker
.instance(id)
.and_then(|x| x.resource(name))
.ok_or_else(|| {
Error::msg(format!(
"Could not find resource {name} from interface {id:?} in linker."
))
})?;
types.insert(resource, instantiated);
}
}
for (_, resource) in component
.exports()
.instances()
.flat_map(|(_, x)| x.resources())
.chain(component.exports().root().resources())
{
let instantiated = resource.instantiate(instance_id)?;
types.insert(resource, instantiated);
}
Ok(types)
}
fn load_exports(
mut inner: InstanceInner,
ctx: impl AsContext,
map: &FxHashMap<ResourceType, ResourceType>,
) -> Result<InstanceInner> {
for (name, func) in &inner.component.0.export_info.root.functions {
inner.exports.root.functions.insert(
name.clone(),
Self::export_function(
&inner,
&ctx,
&func.def,
&func.options,
&func.func,
map,
None,
)?,
);
}
for (name, res) in &inner.component.0.export_types.root.resources {
inner
.exports
.root
.resources
.insert(name.clone(), res.instantiate(inner.id)?);
}
let mut generated_functions = Vec::new();
for (inst_name, inst) in &inner.component.0.export_info.instances {
for (name, func) in &inst.functions {
let export = Self::export_function(
&inner,
&ctx,
&func.def,
&func.options,
&func.func,
map,
Some(inst_name.clone()),
)?;
generated_functions.push((inst_name.clone(), name.clone(), export));
}
}
for (inst_name, inst) in &inner.component.0.export_types.instances {
for (name, res) in &inst.resources {
inner
.exports
.instances
.entry(inst_name.clone())
.or_insert_with(ExportInstance::new)
.resources
.insert(name.clone(), res.instantiate(inner.id)?);
}
}
for (inst_name, name, func) in generated_functions {
let interface = inner
.exports
.instances
.entry(inst_name)
.or_insert_with(ExportInstance::new);
interface.functions.insert(name, func);
}
Ok(inner)
}
fn import_function(
inner: &InstanceInner,
ctx: impl AsContext,
options: &CanonicalOptions,
func: &Function,
) -> GuestInvokeOptions {
let memory = options.memory.map(|idx| {
Self::core_export(inner, &ctx, &inner.component.0.extracted_memories[&idx])
.expect("Could not get runtime memory export.")
.into_memory()
.expect("Export was not of memory type.")
});
let realloc = options.realloc.map(|idx| {
Self::core_export(inner, &ctx, &inner.component.0.extracted_reallocs[&idx])
.expect("Could not get runtime realloc export.")
.into_func()
.expect("Export was not of func type.")
});
let post_return = options.post_return.map(|idx| {
Self::core_export(inner, &ctx, &inner.component.0.extracted_post_returns[&idx])
.expect("Could not get runtime post return export.")
.into_func()
.expect("Export was not of func type.")
});
GuestInvokeOptions {
component: inner.component.0.clone(),
encoding: options.string_encoding,
function: func.clone(),
memory,
realloc,
state_table: inner.state_table.clone(),
post_return,
types: inner.types.clone(),
instance_id: inner.id,
store_id: ctx.as_context().inner.data().id,
}
}
fn export_function(
inner: &InstanceInner,
ctx: impl AsContext,
def: &CoreExport<wasmtime_environ::EntityIndex>,
options: &CanonicalOptions,
func: &Function,
mapping: &FxHashMap<ResourceType, ResourceType>,
interface_id: Option<InterfaceIdentifier>,
) -> Result<crate::func::Func> {
let callee = Self::core_export(inner, &ctx, def)
.expect("Could not get callee export.")
.into_func()
.expect("Export was not of func type.");
let memory = options.memory.map(|idx| {
Self::core_export(inner, &ctx, &inner.component.0.extracted_memories[&idx])
.expect("Could not get runtime memory export.")
.into_memory()
.expect("Export was not of memory type.")
});
let realloc = options.realloc.map(|idx| {
Self::core_export(inner, &ctx, &inner.component.0.extracted_reallocs[&idx])
.expect("Could not get runtime realloc export.")
.into_func()
.expect("Export was not of func type.")
});
let post_return = options.post_return.map(|idx| {
Self::core_export(inner, &ctx, &inner.component.0.extracted_post_returns[&idx])
.expect("Could not get runtime post return export.")
.into_func()
.expect("Export was not of func type.")
});
Ok(crate::func::Func {
store_id: ctx.as_context().inner.data().id,
ty: crate::types::FuncType::from_component(func, &inner.component.0, Some(mapping))?,
backing: FuncImpl::GuestFunc(
None,
Arc::new(GuestFunc {
callee,
component: inner.component.0.clone(),
encoding: options.string_encoding,
function: func.clone(),
memory,
realloc,
state_table: inner.state_table.clone(),
post_return,
types: inner.types.clone(),
instance_id: inner.id,
interface_id,
}),
),
})
}
fn core_import(
inner: &InstanceInner,
mut ctx: impl AsContextMut,
def: &CoreDef,
linker: &Linker,
ty: ExternType,
destructors: &mut Vec<TrampolineIndex>,
resource_map: &FxHashMap<ResourceType, ResourceType>,
) -> Result<Extern> {
match def {
CoreDef::Export(x) => {
Self::core_export(inner, ctx, x).context("Could not find exported function.")
}
CoreDef::Trampoline(x) => {
let ty = if let ExternType::Func(x) = ty {
x
} else {
bail!("Incorrect extern type.")
};
match inner
.component
.0
.generated_trampolines
.get(x)
.context("Could not find exported trampoline.")?
{
GeneratedTrampoline::ImportedFunction(component_import) => {
let expected = crate::types::FuncType::from_component(
&component_import.func,
&inner.component.0,
Some(resource_map),
)?;
let func = Self::get_component_import(component_import, linker)?;
ensure!(
func.ty() == expected,
"Function import {} had type {}, but expected {expected}",
component_import.name,
func.ty()
);
let guest_options = Self::import_function(
inner,
&ctx,
&component_import.options,
&component_import.func,
);
let ty = ty.with_name(component_import.name.clone());
Ok(Extern::Func(wasm_runtime_layer::Func::new(
ctx.as_context_mut().inner,
ty,
move |ctx, args, results| {
let ctx = StoreContextMut { inner: ctx };
func.call_from_guest(ctx, &guest_options, args, results)
},
)))
}
GeneratedTrampoline::ResourceNew(x) => {
let x = x.as_u32();
let tables = inner.state_table.clone();
let ty = ty.with_name(format!("resource-new-{}", x));
Ok(Extern::Func(wasm_runtime_layer::Func::new(
ctx.as_context_mut().inner,
ty,
move |_ctx, args, results| {
let rep =
require_matches!(args[0], wasm_runtime_layer::Value::I32(x), x);
let mut table_array = tables
.resource_tables
.try_lock()
.expect("Could not get mutual reference to table.");
results[0] = wasm_runtime_layer::Value::I32(
table_array[x as usize].add(HandleElement {
rep,
own: true,
lend_count: 0,
}),
);
Ok(())
},
)))
}
GeneratedTrampoline::ResourceRep(x) => {
let x = x.as_u32();
let tables = inner.state_table.clone();
let ty = ty.with_name(format!("resource-rep-{}", x));
Ok(Extern::Func(wasm_runtime_layer::Func::new(
ctx.as_context_mut().inner,
ty,
move |_ctx, args, results| {
let idx =
require_matches!(args[0], wasm_runtime_layer::Value::I32(x), x);
let table_array = tables
.resource_tables
.try_lock()
.expect("Could not get mutual reference to table.");
results[0] = wasm_runtime_layer::Value::I32(
table_array[x as usize].get(idx)?.rep,
);
Ok(())
},
)))
}
GeneratedTrampoline::ResourceDrop(y, _) => {
destructors.push(*x);
let x = y.as_u32();
let tables = inner.state_table.clone();
let ty = ty.with_name(format!("resource-drop-{}", x));
Ok(Extern::Func(wasm_runtime_layer::Func::new(
ctx.as_context_mut().inner,
ty,
move |ctx, args, _results| {
let idx =
require_matches!(args[0], wasm_runtime_layer::Value::I32(x), x);
let mut table_array = tables
.resource_tables
.try_lock()
.expect("Could not get mutual reference to table.");
let current_table = &mut table_array[x as usize];
let elem_borrow = current_table.get(idx)?;
if elem_borrow.own {
ensure!(
elem_borrow.lend_count == 0,
"Attempted to drop loaned resource."
);
let elem = current_table.remove(idx)?;
if let Some(destructor) =
table_array[x as usize].destructor().cloned()
{
drop(table_array);
destructor.call(
ctx,
&[wasm_runtime_layer::Value::I32(elem.rep)],
&mut [],
)?;
}
}
Ok(())
},
)))
}
}
}
CoreDef::InstanceFlags(i) => Ok(Extern::Global(inner.instance_flags[*i].clone())),
}
}
fn core_export<T: Copy + Into<wasmtime_environ::EntityIndex>>(
inner: &InstanceInner,
ctx: impl AsContext,
export: &CoreExport<T>,
) -> Option<Extern> {
let name = match &export.item {
ExportItem::Index(idx) => {
&inner.component.0.export_mapping
[&inner.component.0.instance_modules[export.instance]][&(*idx).into()]
}
ExportItem::Name(s) => s,
};
inner.instances[export.instance].get_export(&ctx.as_context().inner, name)
}
fn global_initialize(
mut inner: InstanceInner,
mut ctx: impl AsContextMut,
linker: &Linker,
resource_map: &FxHashMap<ResourceType, ResourceType>,
) -> Result<InstanceInner> {
let mut destructors = Vec::new();
for initializer in &inner.component.0.translation.component.initializers {
match initializer {
GlobalInitializer::InstantiateModule(InstantiateModule::Static(idx, def)) => {
let module = &inner.component.0.modules[idx];
let imports = Self::generate_imports(
&inner,
&mut ctx,
linker,
module,
def,
&mut destructors,
resource_map,
)?;
let instance = wasm_runtime_layer::Instance::new(
&mut ctx.as_context_mut().inner,
&module.module,
&imports,
)?;
inner.instances.push(instance);
}
GlobalInitializer::ExtractMemory(_) => {}
GlobalInitializer::ExtractRealloc(_) => {}
GlobalInitializer::ExtractPostReturn(_) => {}
GlobalInitializer::LowerImport { .. } => {}
GlobalInitializer::Resource(_) => {}
_ => bail!("Not yet implemented {initializer:?}."),
}
}
Self::fill_destructors(inner, ctx, destructors, resource_map)
}
fn generate_imports(
inner: &InstanceInner,
mut store: impl AsContextMut,
linker: &Linker,
module: &ModuleTranslation,
defs: &[CoreDef],
destructors: &mut Vec<TrampolineIndex>,
resource_map: &FxHashMap<ResourceType, ResourceType>,
) -> Result<Imports> {
let mut import_ty_map = FxHashMap::default();
let engine = store.as_context().engine().clone();
for import in module.module.imports(&engine) {
import_ty_map.insert((import.module, import.name), import.ty.clone());
}
let mut imports = Imports::default();
for (host, name, def) in module
.translation
.imports()
.zip(defs)
.map(|((module, name, _), arg)| (module, name, arg))
{
let ty = import_ty_map
.get(&(host, name))
.context("Unrecognized import.")?
.clone();
imports.define(
host,
name,
Self::core_import(
inner,
&mut store,
def,
linker,
ty,
destructors,
resource_map,
)?,
);
}
Ok(imports)
}
fn get_component_import(
import: &ComponentImport,
linker: &Linker,
) -> Result<crate::func::Func> {
let inst = if let Some(name) = &import.instance {
linker
.instance(name)
.ok_or_else(|| Error::msg(format!("Could not find imported interface {name:?}")))?
} else {
linker.root()
};
inst.func(&import.name)
.ok_or_else(|| Error::msg(format!("Could not find function import {}", import.name)))
}
fn fill_destructors(
inner: InstanceInner,
ctx: impl AsContext,
destructors: Vec<TrampolineIndex>,
resource_map: &FxHashMap<ResourceType, ResourceType>,
) -> Result<InstanceInner> {
let mut tables = inner
.state_table
.resource_tables
.try_lock()
.expect("Could not get access to resource tables.");
for index in destructors {
let (x, def) = require_matches!(
&inner.component.0.generated_trampolines[&index],
GeneratedTrampoline::ResourceDrop(x, def),
(x, def)
);
if let Some(def) = def {
let export = require_matches!(def, CoreDef::Export(x), x);
tables[x.as_u32() as usize].set_destructor(Some(require_matches!(
Self::core_export(&inner, &ctx, export),
Some(Extern::Func(x)),
x
)));
}
}
for (id, idx) in inner.component.0.resolve.types.iter().filter_map(|(i, _)| {
let val = inner.component.0.resource_map[i.index()];
(val.as_u32() < u32::MAX - 1).then_some((i, val))
}) {
let res = ResourceType::from_resolve(
inner.component.0.type_identifiers[id.index()].clone(),
id,
&inner.component.0,
Some(resource_map),
)?;
match res.host_destructor() {
Some(Some(func)) => tables[idx.as_u32() as usize].set_destructor(Some(func)),
Some(None) => tables[idx.as_u32() as usize]
.set_destructor(ctx.as_context().inner.data().drop_host_resource.clone()),
_ => {}
}
}
drop(tables);
Ok(inner)
}
}
#[derive(Debug)]
pub struct Exports {
root: ExportInstance,
instances: FxHashMap<InterfaceIdentifier, ExportInstance>,
}
impl Exports {
pub(crate) fn new() -> Self {
Self {
root: ExportInstance::new(),
instances: FxHashMap::default(),
}
}
pub fn root(&self) -> &ExportInstance {
&self.root
}
pub fn instance(&self, name: &InterfaceIdentifier) -> Option<&ExportInstance> {
self.instances.get(name)
}
pub fn instances(&self) -> impl Iterator<Item = (&'_ InterfaceIdentifier, &'_ ExportInstance)> {
self.instances.iter()
}
}
#[derive(Debug)]
pub struct ExportInstance {
functions: FxHashMap<Arc<str>, crate::func::Func>,
resources: FxHashMap<Arc<str>, ResourceType>,
instance: Weak<InstanceInner>,
}
impl ExportInstance {
pub(crate) fn new() -> Self {
Self {
functions: FxHashMap::default(),
resources: FxHashMap::default(),
instance: Weak::new(),
}
}
pub fn func(&self, name: impl AsRef<str>) -> Option<crate::func::Func> {
self.functions.get(name.as_ref()).map(|x| {
x.instantiate(Instance(
self.instance.upgrade().expect("Instance did not exist."),
))
})
}
pub fn funcs(&self) -> impl Iterator<Item = (&'_ str, crate::func::Func)> {
let inst = self.instance.upgrade().expect("Instance did not exist.");
self.functions
.iter()
.map(move |(k, v)| (&**k, v.instantiate(Instance(inst.clone()))))
}
pub fn resource(&self, name: impl AsRef<str>) -> Option<ResourceType> {
self.resources.get(name.as_ref()).cloned()
}
pub fn resources(&self) -> impl Iterator<Item = (&'_ str, ResourceType)> {
self.resources.iter().map(|(k, v)| (&**k, v.clone()))
}
}
#[derive(Debug)]
struct InstanceInner {
pub component: Component,
pub exports: Exports,
pub id: u64,
pub instance_flags: wasmtime_environ::PrimaryMap<RuntimeComponentInstanceIndex, Global>,
pub instances: wasmtime_environ::PrimaryMap<RuntimeInstanceIndex, wasm_runtime_layer::Instance>,
pub state_table: Arc<StateTable>,
pub types: Arc<[crate::types::ValueType]>,
pub store_id: u64,
}
#[derive(Debug)]
struct StateTable {
pub dropped: AtomicBool,
pub resource_tables: Mutex<Vec<HandleTable>>,
}
#[derive(Clone, Debug)]
struct ComponentImport {
pub instance: Option<InterfaceIdentifier>,
pub name: Arc<str>,
pub func: Function,
pub options: CanonicalOptions,
}
#[derive(Clone, Debug)]
struct ComponentExport {
pub options: CanonicalOptions,
pub func: Function,
pub def: CoreExport<wasmtime_environ::EntityIndex>,
pub ty: crate::types::FuncType,
}
pub struct Store<T, E: backend::WasmEngine> {
inner: wasm_runtime_layer::Store<StoreInner<T, E>, E>,
}
impl<T, E: backend::WasmEngine> Store<T, E> {
pub fn new(engine: &Engine<E>, data: T) -> Self {
static ID_COUNTER: AtomicU64 = AtomicU64::new(0);
let mut inner = wasm_runtime_layer::Store::new(
engine,
StoreInner {
id: ID_COUNTER.fetch_add(1, Ordering::AcqRel),
data,
host_functions: FuncVec::default(),
host_resources: Slab::default(),
drop_host_resource: None,
},
);
inner.data_mut().drop_host_resource = Some(wasm_runtime_layer::Func::new(
&mut inner,
wasm_runtime_layer::FuncType::new([wasm_runtime_layer::ValueType::I32], [])
.with_name("drop-host-resources"),
|mut ctx, args, _| {
if let wasm_runtime_layer::Value::I32(index) = &args[0] {
ctx.data_mut().host_resources.remove(*index as usize);
Ok(())
} else {
bail!("Could not drop resource.");
}
},
));
Self { inner }
}
pub fn engine(&self) -> &Engine<E> {
self.inner.engine()
}
pub fn data(&self) -> &T {
&self.inner.data().data
}
pub fn data_mut(&mut self) -> &mut T {
&mut self.inner.data_mut().data
}
pub fn into_data(self) -> T {
self.inner.into_data().data
}
}
pub struct StoreContext<'a, T: 'a, E: backend::WasmEngine> {
inner: wasm_runtime_layer::StoreContext<'a, StoreInner<T, E>, E>,
}
impl<'a, T: 'a, E: backend::WasmEngine> StoreContext<'a, T, E> {
pub fn engine(&self) -> &Engine<E> {
self.inner.engine()
}
pub fn data(&self) -> &T {
&self.inner.data().data
}
}
pub struct StoreContextMut<'a, T: 'a, E: backend::WasmEngine> {
inner: wasm_runtime_layer::StoreContextMut<'a, StoreInner<T, E>, E>,
}
impl<'a, T: 'a, E: backend::WasmEngine> StoreContextMut<'a, T, E> {
pub fn engine(&self) -> &Engine<E> {
self.inner.engine()
}
pub fn data(&self) -> &T {
&self.inner.data().data
}
pub fn data_mut(&mut self) -> &mut T {
&mut self.inner.data_mut().data
}
}
pub trait AsContext {
type Engine: backend::WasmEngine;
type UserState;
fn as_context(&self) -> StoreContext<Self::UserState, Self::Engine>;
}
pub trait AsContextMut: AsContext {
fn as_context_mut(&mut self) -> StoreContextMut<Self::UserState, Self::Engine>;
}
impl<T, E: backend::WasmEngine> AsContext for Store<T, E> {
type Engine = E;
type UserState = T;
fn as_context(&self) -> StoreContext<Self::UserState, Self::Engine> {
StoreContext {
inner: wasm_runtime_layer::AsContext::as_context(&self.inner),
}
}
}
impl<T, E: backend::WasmEngine> AsContextMut for Store<T, E> {
fn as_context_mut(&mut self) -> StoreContextMut<Self::UserState, Self::Engine> {
StoreContextMut {
inner: wasm_runtime_layer::AsContextMut::as_context_mut(&mut self.inner),
}
}
}
impl<T: AsContext> AsContext for &T {
type Engine = T::Engine;
type UserState = T::UserState;
fn as_context(&self) -> StoreContext<Self::UserState, Self::Engine> {
(**self).as_context()
}
}
impl<T: AsContext> AsContext for &mut T {
type Engine = T::Engine;
type UserState = T::UserState;
fn as_context(&self) -> StoreContext<Self::UserState, Self::Engine> {
(**self).as_context()
}
}
impl<T: AsContextMut> AsContextMut for &mut T {
fn as_context_mut(&mut self) -> StoreContextMut<Self::UserState, Self::Engine> {
(**self).as_context_mut()
}
}
impl<'a, T: 'a, E: backend::WasmEngine> AsContext for StoreContext<'a, T, E> {
type Engine = E;
type UserState = T;
fn as_context(&self) -> StoreContext<Self::UserState, Self::Engine> {
StoreContext {
inner: wasm_runtime_layer::AsContext::as_context(&self.inner),
}
}
}
impl<'a, T: 'a, E: backend::WasmEngine> AsContext for StoreContextMut<'a, T, E> {
type Engine = E;
type UserState = T;
fn as_context(&self) -> StoreContext<Self::UserState, Self::Engine> {
StoreContext {
inner: wasm_runtime_layer::AsContext::as_context(&self.inner),
}
}
}
impl<'a, T: 'a, E: backend::WasmEngine> AsContextMut for StoreContextMut<'a, T, E> {
fn as_context_mut(&mut self) -> StoreContextMut<Self::UserState, Self::Engine> {
StoreContextMut {
inner: wasm_runtime_layer::AsContextMut::as_context_mut(&mut self.inner),
}
}
}
struct StoreInner<T, E: backend::WasmEngine> {
pub id: u64,
pub data: T,
pub host_functions: FuncVec<T, E>,
pub host_resources: Slab<Box<dyn Any + Send + Sync>>,
pub drop_host_resource: Option<wasm_runtime_layer::Func>,
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug)]
enum GeneratedTrampoline {
ImportedFunction(ComponentImport),
ResourceNew(TypeResourceTableIndex),
ResourceRep(TypeResourceTableIndex),
ResourceDrop(TypeResourceTableIndex, Option<CoreDef>),
}
#[derive(Copy, Clone, Debug, Default)]
struct HandleElement {
pub rep: i32,
pub own: bool,
pub lend_count: i32,
}
#[derive(Clone, Debug, Default)]
struct HandleTable {
array: Slab<HandleElement>,
destructor: Option<wasm_runtime_layer::Func>,
}
impl HandleTable {
pub fn destructor(&self) -> Option<&wasm_runtime_layer::Func> {
self.destructor.as_ref()
}
pub fn set_destructor(&mut self, destructor: Option<wasm_runtime_layer::Func>) {
self.destructor = destructor;
}
pub fn get(&self, i: i32) -> Result<&HandleElement> {
self.array.get(i as usize).context("Invalid handle index.")
}
pub fn set(&mut self, i: i32, element: HandleElement) {
*self
.array
.get_mut(i as usize)
.expect("Invalid handle index.") = element;
}
pub fn add(&mut self, handle: HandleElement) -> i32 {
self.array.insert(handle) as i32
}
pub fn remove(&mut self, i: i32) -> Result<HandleElement> {
Ok(self.array.remove(i as usize))
}
}