diff --git a/cranelift/entity/src/primary.rs b/cranelift/entity/src/primary.rs index c47df1c4804d..f3315dd6f35a 100644 --- a/cranelift/entity/src/primary.rs +++ b/cranelift/entity/src/primary.rs @@ -306,6 +306,18 @@ where } } +impl From> for PrimaryMap +where + K: EntityRef, +{ + fn from(elems: Vec) -> Self { + Self { + elems, + unused: PhantomData, + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -496,6 +508,19 @@ mod tests { } } + #[test] + fn from_vec() { + let mut m: PrimaryMap = PrimaryMap::new(); + m.push(12); + m.push(33); + + let n = PrimaryMap::::from(m.values().collect::>()); + assert!(m.len() == n.len()); + for (me, ne) in m.values().zip(n.values()) { + assert!(*me == **ne); + } + } + #[test] fn get_many_mut() { let mut m: PrimaryMap = PrimaryMap::new(); diff --git a/crates/wasmtime/src/component/instance.rs b/crates/wasmtime/src/component/instance.rs index b81afef82272..63f70dbab4f4 100644 --- a/crates/wasmtime/src/component/instance.rs +++ b/crates/wasmtime/src/component/instance.rs @@ -1,6 +1,8 @@ use crate::component::func::HostFunc; use crate::component::matching::InstanceType; -use crate::component::{Component, ComponentNamedList, Func, Lift, Lower, ResourceType, TypedFunc}; +use crate::component::{ + Component, ComponentNamedList, Func, Lift, Lower, ResourceImportIndex, ResourceType, TypedFunc, +}; use crate::instance::OwnedImports; use crate::linker::DefinitionType; use crate::store::{StoreOpaque, Stored}; @@ -520,6 +522,7 @@ impl<'a> Instantiator<'a> { pub struct InstancePre { component: Component, imports: Arc>, + resource_imports: Arc>>, _marker: marker::PhantomData T>, } @@ -529,6 +532,7 @@ impl Clone for InstancePre { Self { component: self.component.clone(), imports: self.imports.clone(), + resource_imports: self.resource_imports.clone(), _marker: self._marker, } } @@ -544,14 +548,28 @@ impl InstancePre { pub(crate) unsafe fn new_unchecked( component: Component, imports: PrimaryMap, + resource_imports: PrimaryMap>, ) -> InstancePre { InstancePre { component, imports: Arc::new(imports), + resource_imports: Arc::new(resource_imports), _marker: marker::PhantomData, } } + pub(crate) fn resource_import_index( + &self, + path: ResourceImportIndex, + ) -> Option { + *self.resource_imports.get(path)? + } + + pub(crate) fn resource_import(&self, path: ResourceImportIndex) -> Option<&RuntimeImport> { + let idx = self.resource_import_index(path)?; + self.imports.get(idx) + } + /// Returns the underlying component that will be instantiated. pub fn component(&self) -> &Component { &self.component diff --git a/crates/wasmtime/src/component/linker.rs b/crates/wasmtime/src/component/linker.rs index 58e6600449f9..6bc355f2ef1d 100644 --- a/crates/wasmtime/src/component/linker.rs +++ b/crates/wasmtime/src/component/linker.rs @@ -14,7 +14,7 @@ use std::ops::Deref; use std::pin::Pin; use std::sync::Arc; use wasmtime_environ::component::TypeDef; -use wasmtime_environ::PrimaryMap; +use wasmtime_environ::{EntityRef, PrimaryMap}; /// A type used to instantiate [`Component`]s. /// @@ -27,6 +27,7 @@ pub struct Linker { strings: Strings, map: NameMap, path: Vec, + resource_imports: usize, allow_shadowing: bool, _marker: marker::PhantomData T>, } @@ -38,6 +39,7 @@ impl Clone for Linker { strings: self.strings.clone(), map: self.map.clone(), path: self.path.clone(), + resource_imports: self.resource_imports.clone(), allow_shadowing: self.allow_shadowing, _marker: self._marker, } @@ -61,10 +63,50 @@ pub struct LinkerInstance<'a, T> { path_len: usize, strings: &'a mut Strings, map: &'a mut NameMap, + resource_imports: &'a mut usize, allow_shadowing: bool, _marker: marker::PhantomData T>, } +/// Index correlating a resource definition to the import path. +/// This is assigned by [`Linker::resource`] and may be used to associate it to +/// [`RuntimeImportIndex`](wasmtime_environ::component::RuntimeImportIndex) +/// at a later stage +/// +/// [`Linker::resource`]: crate::component::LinkerInstance::resource +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct ResourceImportIndex(usize); + +impl EntityRef for ResourceImportIndex { + fn new(idx: usize) -> Self { + Self(idx) + } + + fn index(self) -> usize { + self.0 + } +} + +impl Deref for ResourceImportIndex { + type Target = usize; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From for ResourceImportIndex { + fn from(idx: usize) -> Self { + Self(idx) + } +} + +impl From for usize { + fn from(idx: ResourceImportIndex) -> Self { + idx.0 + } +} + pub(crate) type NameMap = HashMap; #[derive(Clone)] @@ -72,7 +114,11 @@ pub(crate) enum Definition { Instance(NameMap), Func(Arc), Module(Module), - Resource(ResourceType, Arc), + Resource( + ResourceImportIndex, + ResourceType, + Arc, + ), } impl Linker { @@ -85,6 +131,7 @@ impl Linker { map: NameMap::default(), allow_shadowing: false, path: Vec::new(), + resource_imports: 0, _marker: marker::PhantomData, } } @@ -112,6 +159,7 @@ impl Linker { path_len: 0, strings: &mut self.strings, map: &mut self.map, + resource_imports: &mut self.resource_imports, allow_shadowing: self.allow_shadowing, _marker: self._marker, } @@ -170,6 +218,7 @@ impl Linker { // using the import map within the component created at // component-compile-time. let mut imports = PrimaryMap::with_capacity(env_component.imports.len()); + let mut resource_imports = PrimaryMap::from(vec![None; self.resource_imports]); for (idx, (import, names)) in env_component.imports.iter() { let (root, _) = &env_component.import_types[*import]; let root = self.strings.lookup(root).unwrap(); @@ -188,11 +237,14 @@ impl Linker { 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.clone(), - _dtor: dtor.clone(), - dtor_funcref: component.resource_drop_func_ref(dtor), - }, + Definition::Resource(res_idx, t, dtor) => { + resource_imports[*res_idx] = Some(idx); + RuntimeImport::Resource { + ty: t.clone(), + _dtor: dtor.clone(), + dtor_funcref: component.resource_drop_func_ref(dtor), + } + } // This is guaranteed by the compilation process that "leaf" // runtime imports are never instances. @@ -201,7 +253,7 @@ impl Linker { let i = imports.push(import); assert_eq!(i, idx); } - Ok(unsafe { InstancePre::new_unchecked(component.clone(), imports) }) + Ok(unsafe { InstancePre::new_unchecked(component.clone(), imports, resource_imports) }) } /// Instantiates the [`Component`] provided into the `store` specified. @@ -266,6 +318,7 @@ impl LinkerInstance<'_, T> { path_len: self.path_len, strings: self.strings, map: self.map, + resource_imports: self.resource_imports, allow_shadowing: self.allow_shadowing, _marker: self._marker, } @@ -444,13 +497,19 @@ impl LinkerInstance<'_, T> { name: &str, ty: ResourceType, dtor: impl Fn(StoreContextMut<'_, T>, u32) -> Result<()> + Send + Sync + 'static, - ) -> Result<()> { + ) -> Result { let name = self.strings.intern(name); 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)) + let idx = ResourceImportIndex::new(*self.resource_imports); + *self.resource_imports = self + .resource_imports + .checked_add(1) + .context("resource import count would overflow")?; + self.insert(name, Definition::Resource(idx, ty, dtor))?; + Ok(idx) } /// Defines a nested instance within this instance. diff --git a/crates/wasmtime/src/component/matching.rs b/crates/wasmtime/src/component/matching.rs index c68a4995d4b4..050fbe0d015f 100644 --- a/crates/wasmtime/src/component/matching.rs +++ b/crates/wasmtime/src/component/matching.rs @@ -53,7 +53,7 @@ impl TypeChecker<'_> { TypeDef::Resource(i) => { let i = self.types[i].ty; let actual = match actual { - Some(Definition::Resource(actual, _dtor)) => actual, + Some(Definition::Resource(_idx, actual, _dtor)) => actual, // If a resource is imported yet nothing was supplied then // that's only successful if the resource has itself diff --git a/crates/wasmtime/src/component/mod.rs b/crates/wasmtime/src/component/mod.rs index 364942b53c06..e820525e261a 100644 --- a/crates/wasmtime/src/component/mod.rs +++ b/crates/wasmtime/src/component/mod.rs @@ -21,7 +21,7 @@ pub use self::func::{ ComponentNamedList, ComponentType, Func, Lift, Lower, TypedFunc, WasmList, WasmStr, }; pub use self::instance::{ExportInstance, Exports, Instance, InstancePre}; -pub use self::linker::{Linker, LinkerInstance}; +pub use self::linker::{Linker, LinkerInstance, ResourceImportIndex}; pub use self::resource_table::{ResourceTable, ResourceTableError}; pub use self::resources::{Resource, ResourceAny}; pub use self::types::{ResourceType, Type}; diff --git a/crates/wasmtime/src/component/resources.rs b/crates/wasmtime/src/component/resources.rs index 9ed13faf3208..37d600f33ce6 100644 --- a/crates/wasmtime/src/component/resources.rs +++ b/crates/wasmtime/src/component/resources.rs @@ -1,9 +1,11 @@ use crate::component::func::{bad_type_info, desc, LiftContext, LowerContext}; +use crate::component::instance::RuntimeImport; +use crate::component::linker::ResourceImportIndex; use crate::component::matching::InstanceType; -use crate::component::{ComponentType, Lift, Lower}; +use crate::component::{ComponentType, InstancePre, Lift, Lower}; use crate::store::{StoreId, StoreOpaque}; use crate::{AsContextMut, StoreContextMut, Trap}; -use anyhow::{bail, Result}; +use anyhow::{bail, ensure, Context, Result}; use std::any::TypeId; use std::fmt; use std::marker; @@ -394,6 +396,16 @@ where _marker: marker::PhantomData, }) } + + /// See [`ResourceAny::try_from_resource`] + pub fn try_into_resource_any( + self, + store: impl AsContextMut, + instance: &InstancePre, + idx: ResourceImportIndex, + ) -> Result { + ResourceAny::try_from_resource(self, store, instance, idx) + } } unsafe impl ComponentType for Resource { @@ -470,9 +482,11 @@ impl fmt::Debug for Resource { /// This type is similar to [`Resource`] except that it can be used to represent /// any resource, either host or guest. This type cannot be directly constructed /// and is only available if the guest returns it to the host (e.g. a function -/// returning a guest-defined resource). This type also does not carry a static -/// type parameter `T` for example and does not have as much information about -/// its type. This means that it's possible to get runtime type-errors when +/// returning a guest-defined resource) or by a conversion from [`Resource`] via +/// [`ResourceAny::try_from_resource`]. +/// This type also does not carry a static type parameter `T` for example and +/// does not have as much information about its type. +/// This means that it's possible to get runtime type-errors when /// using this type because it cannot statically prevent mismatching resource /// types. /// @@ -501,6 +515,59 @@ struct OwnState { } impl ResourceAny { + /// Attempts to convert an imported [`Resource`] into [`ResourceAny`]. + /// `idx` is the [`ResourceImportIndex`] returned by [`Linker::resource`]. + /// + /// [`Linker::resource`]: crate::component::LinkerInstance::resource + pub fn try_from_resource( + Resource { rep, state, .. }: Resource, + mut store: impl AsContextMut, + instance_pre: &InstancePre, + idx: ResourceImportIndex, + ) -> Result { + let store = store.as_context_mut(); + let import = instance_pre + .resource_import(idx) + .context("import not found")?; + let RuntimeImport::Resource { + ty, dtor_funcref, .. + } = import + else { + bail!("import is not a resource") + }; + ensure!(*ty == ResourceType::host::(), "resource type mismatch"); + + let mut tables = host_resource_tables(store.0); + let (idx, own_state) = match state.load(Relaxed) { + BORROW => (tables.resource_lower_borrow(None, rep), None), + NOT_IN_TABLE => { + let idx = tables.resource_lower_own(None, rep); + ( + idx, + Some(OwnState { + dtor: Some(dtor_funcref.into()), + flags: None, + store: store.0.id(), + }), + ) + } + TAKEN => bail!("host resource already consumed"), + idx => ( + idx, + Some(OwnState { + dtor: Some(dtor_funcref.into()), + flags: None, + store: store.0.id(), + }), + ), + }; + Ok(Self { + idx, + ty: *ty, + own_state, + }) + } + /// Returns the corresponding type associated with this resource, either a /// host-defined type or a guest-defined type. /// diff --git a/tests/all/component_model/resources.rs b/tests/all/component_model/resources.rs index ae3893b12d19..2787d4cedd53 100644 --- a/tests/all/component_model/resources.rs +++ b/tests/all/component_model/resources.rs @@ -558,13 +558,16 @@ fn dynamic_val() -> Result<()> { let mut store = Store::new(&engine, ()); let mut linker = Linker::new(&engine); - linker + let idx = linker .root() .resource("t1", ResourceType::host::(), |_, _| Ok(()))?; - let i = linker.instantiate(&mut store, &c)?; + let i_pre = linker.instantiate_pre(&c)?; + let i = i_pre.instantiate(&mut store)?; let a = i.get_func(&mut store, "a").unwrap(); let a_typed = i.get_typed_func::<(Resource,), (ResourceAny,)>(&mut store, "a")?; + let a_typed_result = + i.get_typed_func::<(Resource,), (Resource,)>(&mut store, "a")?; let b = i.get_func(&mut store, "b").unwrap(); let t2 = i.get_resource(&mut store, "t2").unwrap(); @@ -583,6 +586,32 @@ fn dynamic_val() -> Result<()> { _ => unreachable!(), } + let t1_any = Resource::::new_own(100).try_into_resource_any(&mut store, &i_pre, idx)?; + let mut results = [Val::Bool(false)]; + a.call(&mut store, &[Val::Resource(t1_any)], &mut results)?; + a.post_return(&mut store)?; + match &results[0] { + Val::Resource(resource) => { + assert_eq!(resource.ty(), ResourceType::host::()); + } + _ => unreachable!(), + } + + let t1 = Resource::new_own(100); + let (t1,) = a_typed_result.call(&mut store, (t1,))?; + a_typed_result.post_return(&mut store)?; + + let t1_any = t1.try_into_resource_any(&mut store, &i_pre, idx)?; + let mut results = [Val::Bool(false)]; + a.call(&mut store, &[Val::Resource(t1_any)], &mut results)?; + a.post_return(&mut store)?; + match &results[0] { + Val::Resource(resource) => { + assert_eq!(resource.ty(), ResourceType::host::()); + } + _ => unreachable!(), + } + b.call(&mut store, &[Val::U32(200)], &mut results)?; match &results[0] { Val::Resource(resource) => {