diff --git a/lib/api/src/env.rs b/lib/api/src/env.rs index 9a4f7e0885e..30206ada2bc 100644 --- a/lib/api/src/env.rs +++ b/lib/api/src/env.rs @@ -1,4 +1,5 @@ -use crate::{ExportError, Instance}; +use crate::{ExportError, Instance, WeakExports, Exportable, WasmTypeList}; +use crate::exports::ExportableWithGenerics; use thiserror::Error; /// An error while initializing the user supplied host env with the `WasmerEnv` trait. @@ -218,3 +219,31 @@ impl Default for LazyInit { unsafe impl Send for LazyInit {} // I thought we could opt out of sync..., look into this // unsafe impl !Sync for InitWithInstance {} + +/// Test struct to access with weak instance ref +pub struct InstanceExport { + /// Marker + _pd: std::marker::PhantomData, + exports_ref: WeakExports, +} + +impl InstanceExport { + /// TODO: document + pub fn new(weak_instance: WeakExports) -> Self { + Self { + _pd: std::marker::PhantomData, + exports_ref: weak_instance, + } + } + + /// TODO: document + pub fn get_with_generics(&self, name: &str) -> Result + where + Args: WasmTypeList, + Rets: WasmTypeList, + T2: ExportableWithGenerics, + { + let exports = self.exports_ref.upgrade().unwrap(); + exports.get_with_generics::(name) + } +} diff --git a/lib/api/src/exports.rs b/lib/api/src/exports.rs index 4bfea97ede7..7da0240a75f 100644 --- a/lib/api/src/exports.rs +++ b/lib/api/src/exports.rs @@ -6,7 +6,7 @@ use indexmap::IndexMap; use loupe::MemoryUsage; use std::fmt; use std::iter::{ExactSizeIterator, FromIterator}; -use std::sync::Arc; +use std::sync::{Arc, Weak}; use thiserror::Error; use wasmer_engine::Export; @@ -67,6 +67,19 @@ pub struct Exports { map: Arc>, } +/// TODO: document this +#[derive(Clone, Default)] +pub struct WeakExports(Weak>); + +impl WeakExports { + /// TODO: document this + pub fn upgrade(&self) -> Option { + Some(Exports { + map: self.0.upgrade()?, + }) + } +} + impl Exports { /// Creates a new `Exports`. pub fn new() -> Self { @@ -112,7 +125,7 @@ impl Exports { /// /// If you want to get an export dynamically handling manually /// type checking manually, please use `get_extern`. - pub fn get<'a, T: Exportable<'a>>(&'a self, name: &str) -> Result<&'a T, ExportError> { + pub fn get<'a, T: Exportable>(&'a self, name: &str) -> Result<&'a T, ExportError> { match self.map.get(name) { None => Err(ExportError::Missing(name.to_string())), Some(extern_) => T::get_self_from_extern(extern_), @@ -154,11 +167,11 @@ impl Exports { } /// Hack to get this working with nativefunc too - pub fn get_with_generics<'a, T, Args, Rets>(&'a self, name: &str) -> Result + pub fn get_with_generics(&self, name: &str) -> Result where Args: WasmTypeList, Rets: WasmTypeList, - T: ExportableWithGenerics<'a, Args, Rets>, + T: ExportableWithGenerics, { match self.map.get(name) { None => Err(ExportError::Missing(name.to_string())), @@ -166,6 +179,19 @@ impl Exports { } } + /// Like `get_with_generics` but with a WeakReference to the `InstanceRef` internally. + /// This is useful for passing data into `WasmerEnv`, for example. + pub fn get_with_generics_weak(&self, name: &str) -> Result + where + Args: WasmTypeList, + Rets: WasmTypeList, + T: ExportableWithGenerics, + { + let mut out: T = self.get_with_generics(name)?; + out.into_weak_instance_ref(); + Ok(out) + } + /// Get an export as an `Extern`. pub fn get_extern(&self, name: &str) -> Option<&Extern> { self.map.get(name) @@ -185,6 +211,11 @@ impl Exports { iter: self.map.iter(), } } + + /// TODO: document this + pub fn downgrade(&self) -> WeakExports { + WeakExports(Arc::downgrade(&self.map)) + } } impl fmt::Debug for Exports { @@ -282,7 +313,7 @@ impl LikeNamespace for Exports { /// This trait is used to mark types as gettable from an [`Instance`]. /// /// [`Instance`]: crate::Instance -pub trait Exportable<'a>: Sized { +pub trait Exportable: Sized { /// This function is used when providedd the [`Extern`] as exportable, so it /// can be used while instantiating the [`Module`]. /// @@ -293,21 +324,33 @@ pub trait Exportable<'a>: Sized { /// from an [`Instance`] by name. /// /// [`Instance`]: crate::Instance - fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError>; + fn get_self_from_extern<'a>(_extern: &'a Extern) -> Result<&'a Self, ExportError>; + + /// TODO: this method doesn't belong here + fn into_weak_instance_ref(&mut self) { + todo!("into_weak_instance_ref") + } } /// A trait for accessing exports (like [`Exportable`]) but it takes generic /// `Args` and `Rets` parameters so that `NativeFunc` can be accessed directly /// as well. -pub trait ExportableWithGenerics<'a, Args: WasmTypeList, Rets: WasmTypeList>: Sized { +pub trait ExportableWithGenerics: Sized { /// Get an export with the given generics. - fn get_self_from_extern_with_generics(_extern: &'a Extern) -> Result; + fn get_self_from_extern_with_generics(_extern: &Extern) -> Result; + /// TODO: this method doesn't belong here + fn into_weak_instance_ref(&mut self); } /// We implement it for all concrete [`Exportable`] types (that are `Clone`) /// with empty `Args` and `Rets`. -impl<'a, T: Exportable<'a> + Clone + 'static> ExportableWithGenerics<'a, (), ()> for T { - fn get_self_from_extern_with_generics(_extern: &'a Extern) -> Result { - T::get_self_from_extern(_extern).map(|i| i.clone()) +impl ExportableWithGenerics<(), ()> for T { + fn get_self_from_extern_with_generics(_extern: &Extern) -> Result { + Self::get_self_from_extern(_extern).map(|i| i.clone()) + } + + /// TODO: this method doesn't belong here + fn into_weak_instance_ref(&mut self) { + ::into_weak_instance_ref(self); } } diff --git a/lib/api/src/externals/function.rs b/lib/api/src/externals/function.rs index aaf91d78bda..7809b06a4ef 100644 --- a/lib/api/src/externals/function.rs +++ b/lib/api/src/externals/function.rs @@ -712,19 +712,38 @@ impl Function { fn closures_unsupported_panic() -> ! { unimplemented!("Closures (functions with captured environments) are currently unsupported with native functions. See: https://github.com/wasmerio/wasmer/issues/1840") } + + /// Check if the function holds a strong `InstanceRef`. + /// None means there's no `InstanceRef`, strong or weak. + // TODO: maybe feature gate this, we only need it for tests... + pub fn is_strong_instance_ref(&self) -> Option { + self.exported + .vm_function + .instance_ref + .as_ref() + .map(|v| v.is_strong()) + } } -impl<'a> Exportable<'a> for Function { +impl Exportable for Function { fn to_export(&self) -> Export { self.exported.clone().into() } - fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { + fn get_self_from_extern<'a>(_extern: &'a Extern) -> Result<&'a Self, ExportError> { match _extern { Extern::Function(func) => Ok(func), _ => Err(ExportError::IncompatibleType), } } + + fn into_weak_instance_ref(&mut self) { + self.exported + .vm_function + .instance_ref + .as_mut() + .map(|v| v.into_weak()); + } } impl fmt::Debug for Function { diff --git a/lib/api/src/externals/global.rs b/lib/api/src/externals/global.rs index 1d03cc10a24..69a3e2d79eb 100644 --- a/lib/api/src/externals/global.rs +++ b/lib/api/src/externals/global.rs @@ -208,6 +208,13 @@ impl Global { pub fn same(&self, other: &Self) -> bool { Arc::ptr_eq(&self.vm_global.from, &other.vm_global.from) } + + /// Check if the global holds a strong `InstanceRef`. + /// None means there's no `InstanceRef`, strong or weak. + // TODO: maybe feature gate this, we only need it for tests... + pub fn is_strong_instance_ref(&self) -> Option { + self.vm_global.instance_ref.as_ref().map(|v| v.is_strong()) + } } impl fmt::Debug for Global { @@ -220,15 +227,19 @@ impl fmt::Debug for Global { } } -impl<'a> Exportable<'a> for Global { +impl Exportable for Global { fn to_export(&self) -> Export { self.vm_global.clone().into() } - fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { + fn get_self_from_extern<'a>(_extern: &'a Extern) -> Result<&'a Self, ExportError> { match _extern { Extern::Global(global) => Ok(global), _ => Err(ExportError::IncompatibleType), } } + + fn into_weak_instance_ref(&mut self) { + self.vm_global.instance_ref.as_mut().map(|v| v.into_weak()); + } } diff --git a/lib/api/src/externals/memory.rs b/lib/api/src/externals/memory.rs index f621751d2f8..ad58b7556e1 100644 --- a/lib/api/src/externals/memory.rs +++ b/lib/api/src/externals/memory.rs @@ -248,17 +248,28 @@ impl Memory { pub fn same(&self, other: &Self) -> bool { Arc::ptr_eq(&self.vm_memory.from, &other.vm_memory.from) } + + /// Check if the memory holds a strong `InstanceRef`. + /// None means there's no `InstanceRef`, strong or weak. + // TODO: maybe feature gate this, we only need it for tests... + pub fn is_strong_instance_ref(&self) -> Option { + self.vm_memory.instance_ref.as_ref().map(|v| v.is_strong()) + } } -impl<'a> Exportable<'a> for Memory { +impl Exportable for Memory { fn to_export(&self) -> Export { self.vm_memory.clone().into() } - fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { + fn get_self_from_extern<'a>(_extern: &'a Extern) -> Result<&'a Self, ExportError> { match _extern { Extern::Memory(memory) => Ok(memory), _ => Err(ExportError::IncompatibleType), } } + + fn into_weak_instance_ref(&mut self) { + self.vm_memory.instance_ref.as_mut().map(|v| v.into_weak()); + } } diff --git a/lib/api/src/externals/mod.rs b/lib/api/src/externals/mod.rs index 174a175e341..0085bd50740 100644 --- a/lib/api/src/externals/mod.rs +++ b/lib/api/src/externals/mod.rs @@ -58,7 +58,7 @@ impl Extern { } } -impl<'a> Exportable<'a> for Extern { +impl Exportable for Extern { fn to_export(&self) -> Export { match self { Self::Function(f) => f.to_export(), @@ -68,10 +68,19 @@ impl<'a> Exportable<'a> for Extern { } } - fn get_self_from_extern(_extern: &'a Self) -> Result<&'a Self, ExportError> { + fn get_self_from_extern<'a>(_extern: &'a Self) -> Result<&'a Self, ExportError> { // Since this is already an extern, we can just return it. Ok(_extern) } + + fn into_weak_instance_ref(&mut self) { + match self { + Self::Function(f) => f.into_weak_instance_ref(), + Self::Global(g) => g.into_weak_instance_ref(), + Self::Memory(m) => m.into_weak_instance_ref(), + Self::Table(t) => t.into_weak_instance_ref(), + } + } } impl StoreObject for Extern { diff --git a/lib/api/src/externals/table.rs b/lib/api/src/externals/table.rs index d959212ba31..5face991a0b 100644 --- a/lib/api/src/externals/table.rs +++ b/lib/api/src/externals/table.rs @@ -146,17 +146,28 @@ impl Table { pub fn same(&self, other: &Self) -> bool { Arc::ptr_eq(&self.vm_table.from, &other.vm_table.from) } + + /// Check if the table holds a strong `InstanceRef`. + /// None means there's no `InstanceRef`, strong or weak. + // TODO: maybe feature gate this, we only need it for tests... + pub fn is_strong_instance_ref(&self) -> Option { + self.vm_table.instance_ref.as_ref().map(|v| v.is_strong()) + } } -impl<'a> Exportable<'a> for Table { +impl Exportable for Table { fn to_export(&self) -> Export { self.vm_table.clone().into() } - fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError> { + fn get_self_from_extern<'a>(_extern: &'a Extern) -> Result<&'a Self, ExportError> { match _extern { Extern::Table(table) => Ok(table), _ => Err(ExportError::IncompatibleType), } } + + fn into_weak_instance_ref(&mut self) { + self.vm_table.instance_ref.as_mut().map(|v| v.into_weak()); + } } diff --git a/lib/api/src/lib.rs b/lib/api/src/lib.rs index cb178282f81..255ca636b68 100644 --- a/lib/api/src/lib.rs +++ b/lib/api/src/lib.rs @@ -283,8 +283,8 @@ pub mod internals { pub use crate::externals::{WithEnv, WithoutEnv}; } -pub use crate::env::{HostEnvInitError, LazyInit, WasmerEnv}; -pub use crate::exports::{ExportError, Exportable, Exports, ExportsIterator}; +pub use crate::env::{HostEnvInitError, LazyInit, WasmerEnv, InstanceExport}; +pub use crate::exports::{ExportError, Exportable, Exports, ExportsIterator, WeakExports}; pub use crate::externals::{ Extern, FromToNativeWasmType, Function, Global, HostFunction, Memory, Table, WasmTypeList, }; @@ -322,7 +322,7 @@ pub use wasmer_types::{ }; // TODO: should those be moved into wasmer::vm as well? -pub use wasmer_vm::{raise_user_trap, MemoryError}; +pub use wasmer_vm::{raise_user_trap, MemoryError, WeakInstanceRef}; pub mod vm { //! The vm module re-exports wasmer-vm types. diff --git a/lib/api/src/native.rs b/lib/api/src/native.rs index 3744f559edc..44e93aecf63 100644 --- a/lib/api/src/native.rs +++ b/lib/api/src/native.rs @@ -196,10 +196,16 @@ macro_rules! impl_native_traits { } } + /// Check if the function holds a strong `InstanceRef`. + /// None means there's no `InstanceRef`, strong or weak. + // TODO: maybe feature gate this, we only need it for tests... + pub fn is_strong_instance_ref(&self) -> Option { + self.exported.vm_function.instance_ref.as_ref().map(|v| v.is_strong()) + } } #[allow(unused_parens)] - impl<'a, $( $x, )* Rets> crate::exports::ExportableWithGenerics<'a, ($( $x ),*), Rets> for NativeFunc<( $( $x ),* ), Rets> + impl<$( $x, )* Rets> crate::exports::ExportableWithGenerics<($( $x ),*), Rets> for NativeFunc<( $( $x ),* ), Rets> where $( $x: FromToNativeWasmType, )* Rets: WasmTypeList, @@ -208,6 +214,10 @@ macro_rules! impl_native_traits { use crate::exports::Exportable; crate::Function::get_self_from_extern(_extern)?.native().map_err(|_| crate::exports::ExportError::IncompatibleType) } + + fn into_weak_instance_ref(&mut self) { + self.exported.vm_function.instance_ref.as_mut().map(|v| v.into_weak()); + } } }; } diff --git a/lib/api/tests/export.rs b/lib/api/tests/export.rs new file mode 100644 index 00000000000..646a109a269 --- /dev/null +++ b/lib/api/tests/export.rs @@ -0,0 +1,264 @@ +use anyhow::Result; +use wasmer::*; + +const MEM_WAT: &str = " + (module + (func $host_fn (import \"env\" \"host_fn\") (param) (result)) + (func (export \"call_host_fn\") (param) (result) + (call $host_fn)) + + (memory $mem 0) + (export \"memory\" (memory $mem)) + ) +"; + +const GLOBAL_WAT: &str = " + (module + (func $host_fn (import \"env\" \"host_fn\") (param) (result)) + (func (export \"call_host_fn\") (param) (result) + (call $host_fn)) + + (global $global i32 (i32.const 11)) + (export \"global\" (global $global)) + ) +"; + +const TABLE_WAT: &str = " + (module + (func $host_fn (import \"env\" \"host_fn\") (param) (result)) + (func (export \"call_host_fn\") (param) (result) + (call $host_fn)) + + (table $table 4 4 funcref) + (export \"table\" (table $table)) + ) +"; + +const FUNCTION_WAT: &str = " + (module + (func $host_fn (import \"env\" \"host_fn\") (param) (result)) + (func (export \"call_host_fn\") (param) (result) + (call $host_fn)) + ) +"; + +#[test] +fn strong_weak_behavior_works_memory() -> Result<()> { + #[derive(Clone, Debug, WasmerEnv, Default)] + struct MemEnv { + #[wasmer(export)] + memory: LazyInit, + } + + let host_fn = |env: &MemEnv| { + let mem = env.memory_ref().unwrap(); + assert_eq!(mem.is_strong_instance_ref(), Some(false)); + let mem_clone = mem.clone(); + assert_eq!(mem_clone.is_strong_instance_ref(), Some(true)); + assert_eq!(mem.is_strong_instance_ref(), Some(false)); + }; + + let f: NativeFunc<(), ()> = { + let store = Store::default(); + let module = Module::new(&store, MEM_WAT)?; + let env = MemEnv::default(); + + let instance = Instance::new( + &module, + &imports! { + "env" => { + "host_fn" => Function::new_native_with_env(&store, env, host_fn) + } + }, + )?; + + { + let mem = instance.exports.get_memory("memory")?; + assert_eq!(mem.is_strong_instance_ref(), Some(true)); + } + + let f: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_fn")?; + f.call()?; + f + }; + f.call()?; + + Ok(()) +} + +#[test] +fn strong_weak_behavior_works_global() -> Result<()> { + #[derive(Clone, Debug, WasmerEnv, Default)] + struct GlobalEnv { + #[wasmer(export)] + global: LazyInit, + } + + let host_fn = |env: &GlobalEnv| { + let global = env.global_ref().unwrap(); + assert_eq!(global.is_strong_instance_ref(), Some(false)); + let global_clone = global.clone(); + assert_eq!(global_clone.is_strong_instance_ref(), Some(true)); + assert_eq!(global.is_strong_instance_ref(), Some(false)); + }; + + let f: NativeFunc<(), ()> = { + let store = Store::default(); + let module = Module::new(&store, GLOBAL_WAT)?; + let env = GlobalEnv::default(); + + let instance = Instance::new( + &module, + &imports! { + "env" => { + "host_fn" => Function::new_native_with_env(&store, env, host_fn) + } + }, + )?; + + { + let global = instance.exports.get_global("global")?; + assert_eq!(global.is_strong_instance_ref(), Some(true)); + } + + let f: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_fn")?; + f.call()?; + f + }; + f.call()?; + + Ok(()) +} + +#[test] +fn strong_weak_behavior_works_table() -> Result<()> { + #[derive(Clone, WasmerEnv, Default)] + struct TableEnv { + #[wasmer(export)] + table: LazyInit, + } + + let host_fn = |env: &TableEnv| { + let table = env.table_ref().unwrap(); + assert_eq!(table.is_strong_instance_ref(), Some(false)); + let table_clone = table.clone(); + assert_eq!(table_clone.is_strong_instance_ref(), Some(true)); + assert_eq!(table.is_strong_instance_ref(), Some(false)); + }; + + let f: NativeFunc<(), ()> = { + let store = Store::default(); + let module = Module::new(&store, TABLE_WAT)?; + let env = TableEnv::default(); + + let instance = Instance::new( + &module, + &imports! { + "env" => { + "host_fn" => Function::new_native_with_env(&store, env, host_fn) + } + }, + )?; + + { + let table = instance.exports.get_table("table")?; + assert_eq!(table.is_strong_instance_ref(), Some(true)); + } + + let f: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_fn")?; + f.call()?; + f + }; + f.call()?; + + Ok(()) +} + +#[test] +fn strong_weak_behavior_works_function() -> Result<()> { + #[derive(Clone, WasmerEnv, Default)] + struct FunctionEnv { + #[wasmer(export)] + call_host_fn: LazyInit, + } + + let host_fn = |env: &FunctionEnv| { + let function = env.call_host_fn_ref().unwrap(); + assert_eq!(function.is_strong_instance_ref(), Some(false)); + let function_clone = function.clone(); + assert_eq!(function_clone.is_strong_instance_ref(), Some(true)); + assert_eq!(function.is_strong_instance_ref(), Some(false)); + }; + + let f: NativeFunc<(), ()> = { + let store = Store::default(); + let module = Module::new(&store, FUNCTION_WAT)?; + let env = FunctionEnv::default(); + + let instance = Instance::new( + &module, + &imports! { + "env" => { + "host_fn" => Function::new_native_with_env(&store, env, host_fn) + } + }, + )?; + + { + let function = instance.exports.get_function("call_host_fn")?; + assert_eq!(function.is_strong_instance_ref(), Some(true)); + } + + let f: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_fn")?; + f.call()?; + f + }; + f.call()?; + + Ok(()) +} + +#[test] +fn strong_weak_behavior_works_native_function() -> Result<()> { + #[derive(Clone, WasmerEnv, Default)] + struct FunctionEnv { + #[wasmer(export)] + call_host_fn: LazyInit>, + } + + let host_fn = |env: &FunctionEnv| { + let function = env.call_host_fn_ref().unwrap(); + assert_eq!(function.is_strong_instance_ref(), Some(false)); + let function_clone = function.clone(); + assert_eq!(function_clone.is_strong_instance_ref(), Some(true)); + assert_eq!(function.is_strong_instance_ref(), Some(false)); + }; + + let f: NativeFunc<(), ()> = { + let store = Store::default(); + let module = Module::new(&store, FUNCTION_WAT)?; + let env = FunctionEnv::default(); + + let instance = Instance::new( + &module, + &imports! { + "env" => { + "host_fn" => Function::new_native_with_env(&store, env, host_fn) + } + }, + )?; + + { + let function: NativeFunc<(), ()> = + instance.exports.get_native_function("call_host_fn")?; + assert_eq!(function.is_strong_instance_ref(), Some(true)); + } + + let f: NativeFunc<(), ()> = instance.exports.get_native_function("call_host_fn")?; + f.call()?; + f + }; + f.call()?; + + Ok(()) +} diff --git a/lib/derive/src/lib.rs b/lib/derive/src/lib.rs index 22fcca78291..d5739eeb9b1 100644 --- a/lib/derive/src/lib.rs +++ b/lib/derive/src/lib.rs @@ -136,12 +136,15 @@ fn derive_struct_fields(data: &DataStruct) -> (TokenStream, TokenStream) { identifier.unwrap_or_else(|| LitStr::new(&name_str, name.span())); let mut access_expr = quote_spanned! { f.span() => - instance.exports.get_with_generics::<#inner_type, _, _>(#item_name) + wasmer::InstanceExport::new(instance.exports.downgrade()) + //instance.exports.get_with_generics_weak::<#inner_type, _, _>(#item_name) }; for alias in aliases { access_expr = quote_spanned! { f.span()=> - #access_expr .or_else(|_| instance.exports.get_with_generics::<#inner_type, _, _>(#alias)) + + wasmer::InstanceExport::new(instance.exports.downgrade()) + //#access_expr .or_else(|_| instance.exports.get_with_generics_weak::<#inner_type, _, _>(#alias)) }; } if optional { @@ -163,12 +166,12 @@ fn derive_struct_fields(data: &DataStruct) -> (TokenStream, TokenStream) { if let Some(identifier) = identifier { let mut access_expr = quote_spanned! { f.span() => - instance.exports.get_with_generics::<#inner_type, _, _>(#identifier) + instance.exports.get_with_generics_weak::<#inner_type, _, _>(#identifier) }; for alias in aliases { access_expr = quote_spanned! { f.span()=> - #access_expr .or_else(|_| instance.exports.get_with_generics::<#inner_type, _, _>(#alias)) + #access_expr .or_else(|_| instance.exports.get_with_generics_weak::<#inner_type, _, _>(#alias)) }; } let local_var = @@ -200,6 +203,20 @@ fn derive_struct_fields(data: &DataStruct) -> (TokenStream, TokenStream) { finish.push(finish_tokens); } + WasmerAttr::Instance { span } => { + let tokens = if let Some(name) = name { + quote_spanned! { + span => + self.#name.initialize(instance.weak_instance_ref()); + } + } else { + quote_spanned! { + span => + self.#field_idx.initialize(instance.exports.downgrade()); + } + }; + finish.push(tokens); + } } } } diff --git a/lib/derive/src/parse.rs b/lib/derive/src/parse.rs index a8feb9e9488..b0312eaafbe 100644 --- a/lib/derive/src/parse.rs +++ b/lib/derive/src/parse.rs @@ -15,6 +15,9 @@ pub enum WasmerAttr { aliases: Vec, span: Span, }, + Instance { + span: Span, + } } #[derive(Debug)] @@ -30,6 +33,8 @@ struct ExportOptions { optional: bool, aliases: Vec, } + + impl Parse for ExportOptions { fn parse(input: ParseStream<'_>) -> syn::Result { let mut name = None; @@ -97,6 +102,9 @@ impl Parse for ExportExpr { } } +#[derive(Debug)] +struct InstanceExpr; + // allows us to handle parens more cleanly struct WasmerAttrInner(WasmerAttr); @@ -124,6 +132,11 @@ impl Parse for WasmerAttrInner { span, } } + "instance" => { + WasmerAttr::Instance { + span + } + } otherwise => abort!( ident, "Unexpected identifier `{}`. Expected `export`.", diff --git a/lib/vm/src/export.rs b/lib/vm/src/export.rs index 075f4703d34..4374622652f 100644 --- a/lib/vm/src/export.rs +++ b/lib/vm/src/export.rs @@ -2,7 +2,7 @@ // Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md use crate::global::Global; -use crate::instance::InstanceRef; +use crate::instance::WeakOrStrongInstanceRef; use crate::memory::{Memory, MemoryStyle}; use crate::table::{Table, TableStyle}; use crate::vmcontext::{VMFunctionBody, VMFunctionEnvironment, VMFunctionKind, VMTrampoline}; @@ -27,7 +27,7 @@ pub enum VMExtern { } /// A function export value. -#[derive(Debug, Clone, PartialEq, MemoryUsage)] +#[derive(Debug, PartialEq, MemoryUsage)] pub struct VMFunction { /// The address of the native-code function. pub address: *const VMFunctionBody, @@ -52,7 +52,27 @@ pub struct VMFunction { /// A “reference” to the instance through the /// `InstanceRef`. `None` if it is a host function. - pub instance_ref: Option, + pub instance_ref: Option, +} + +// Is this just a bad idea? We want the default behavior to be converting from weak +// to strong... but is this just a footgun? +impl Clone for VMFunction { + fn clone(&self) -> Self { + // REVIEW: panicking in clone, etc etc. There's probably a much more elegant + // way to express this. + Self { + address: self.address.clone(), + vmctx: self.vmctx.clone(), + signature: self.signature.clone(), + kind: self.kind.clone(), + call_trampoline: self.call_trampoline.clone(), + instance_ref: self + .instance_ref + .as_ref() + .map(|v| WeakOrStrongInstanceRef::Strong(v.get_strong().unwrap())), + } + } } /// # Safety @@ -70,14 +90,30 @@ impl From for VMExtern { } /// A table export value. -#[derive(Debug, Clone, MemoryUsage)] +#[derive(Debug, MemoryUsage)] pub struct VMTable { /// Pointer to the containing `Table`. pub from: Arc, /// A “reference” to the instance through the /// `InstanceRef`. `None` if it is a host table. - pub instance_ref: Option, + pub instance_ref: Option, +} + +// Is this just a bad idea? We want the default behavior to be converting from weak +// to strong... but is this just a footgun? +impl Clone for VMTable { + fn clone(&self) -> Self { + // REVIEW: panicking in clone, etc etc. There's probably a much more elegant + // way to express this. + Self { + from: self.from.clone(), + instance_ref: self + .instance_ref + .as_ref() + .map(|v| WeakOrStrongInstanceRef::Strong(v.get_strong().unwrap())), + } + } } /// # Safety @@ -116,14 +152,30 @@ impl From for VMExtern { } /// A memory export value. -#[derive(Debug, Clone, MemoryUsage)] +#[derive(Debug, MemoryUsage)] pub struct VMMemory { /// Pointer to the containing `Memory`. pub from: Arc, /// A “reference” to the instance through the /// `InstanceRef`. `None` if it is a host memory. - pub instance_ref: Option, + pub instance_ref: Option, +} + +// Is this just a bad idea? We want the default behavior to be converting from weak +// to strong... but is this just a footgun? +impl Clone for VMMemory { + fn clone(&self) -> Self { + // REVIEW: panicking in clone, etc etc. There's probably a much more elegant + // way to express this. + Self { + from: self.from.clone(), + instance_ref: self + .instance_ref + .as_ref() + .map(|v| WeakOrStrongInstanceRef::Strong(v.get_strong().unwrap())), + } + } } /// # Safety @@ -162,14 +214,30 @@ impl From for VMExtern { } /// A global export value. -#[derive(Debug, Clone, MemoryUsage)] +#[derive(Debug, MemoryUsage)] pub struct VMGlobal { /// The global declaration, used for compatibility checking. pub from: Arc, /// A “reference” to the instance through the /// `InstanceRef`. `None` if it is a host global. - pub instance_ref: Option, + pub instance_ref: Option, +} + +// Is this just a bad idea? We want the default behavior to be converting from weak +// to strong... but is this just a footgun? +impl Clone for VMGlobal { + fn clone(&self) -> Self { + // REVIEW: panicking in clone, etc etc. There's probably a much more elegant + // way to express this. + Self { + from: self.from.clone(), + instance_ref: self + .instance_ref + .as_ref() + .map(|v| WeakOrStrongInstanceRef::Strong(v.get_strong().unwrap())), + } + } } /// # Safety diff --git a/lib/vm/src/instance/mod.rs b/lib/vm/src/instance/mod.rs index 423ca991653..1edf13fe1f0 100644 --- a/lib/vm/src/instance/mod.rs +++ b/lib/vm/src/instance/mod.rs @@ -11,7 +11,7 @@ mod allocator; mod r#ref; pub use allocator::InstanceAllocator; -pub use r#ref::InstanceRef; +pub use r#ref::{InstanceRef, WeakInstanceRef, WeakOrStrongInstanceRef}; use crate::export::VMExtern; use crate::func_data_registry::{FuncDataRegistry, VMFuncRef}; @@ -1008,6 +1008,11 @@ impl InstanceHandle { &self.instance } + /// TODO: document this + pub fn weak_ref(&self) -> WeakInstanceRef { + self.instance.downgrade() + } + /// Finishes the instantiation process started by `Instance::new`. /// /// # Safety @@ -1100,7 +1105,7 @@ impl InstanceHandle { signature, vmctx, call_trampoline, - instance_ref: Some(instance), + instance_ref: Some(WeakOrStrongInstanceRef::Strong(instance)), } .into() } @@ -1113,7 +1118,7 @@ impl InstanceHandle { }; VMTable { from, - instance_ref: Some(instance), + instance_ref: Some(WeakOrStrongInstanceRef::Strong(instance)), } .into() } @@ -1126,7 +1131,7 @@ impl InstanceHandle { }; VMMemory { from, - instance_ref: Some(instance), + instance_ref: Some(WeakOrStrongInstanceRef::Strong(instance)), } .into() } @@ -1141,7 +1146,7 @@ impl InstanceHandle { }; VMGlobal { from, - instance_ref: Some(instance), + instance_ref: Some(WeakOrStrongInstanceRef::Strong(instance)), } .into() } diff --git a/lib/vm/src/instance/ref.rs b/lib/vm/src/instance/ref.rs index 2f5b127040e..d3ea6facb98 100644 --- a/lib/vm/src/instance/ref.rs +++ b/lib/vm/src/instance/ref.rs @@ -3,7 +3,7 @@ use loupe::{MemoryUsage, MemoryUsageTracker}; use std::alloc::Layout; use std::mem; use std::ptr::{self, NonNull}; -use std::sync::Arc; +use std::sync::{Arc, Weak}; /// Dynamic instance allocation. /// @@ -154,4 +154,71 @@ impl InstanceRef { let ptr: *mut InstanceInner = Arc::as_ptr(&self.0) as *mut _; (&mut *ptr).as_mut() } + + /// TODO: document this + pub fn downgrade(&self) -> WeakInstanceRef { + WeakInstanceRef(Arc::downgrade(&self.0)) + } +} + +#[derive(Debug, Clone)] +/// TODO: document this +pub struct WeakInstanceRef(Weak); + +impl PartialEq for WeakInstanceRef { + fn eq(&self, other: &Self) -> bool { + self.0.ptr_eq(&other.0) + } +} + +impl WeakInstanceRef { + /// TODO: document this + pub fn upgrade(&self) -> Option { + let inner = self.0.upgrade()?; + Some(InstanceRef(inner)) + } +} + +impl MemoryUsage for WeakInstanceRef { + fn size_of_val(&self, _tracker: &mut dyn MemoryUsageTracker) -> usize { + todo!("Probably missing implementation at crate level for `Weak`. Can be done manually here but I'm focused on other things right now"); + } +} + +#[derive(Debug, Clone, PartialEq, MemoryUsage)] +/// TODO: document this +pub enum WeakOrStrongInstanceRef { + /// A weak instance ref. + Weak(WeakInstanceRef), + /// A strong instance ref. + Strong(InstanceRef), +} + +impl WeakOrStrongInstanceRef { + /// Get a strong `InstanceRef`. A return Value of `None` means that the + /// `InstanceRef` has been freed and cannot be accessed. + pub fn get_strong(&self) -> Option { + match self { + Self::Weak(weak) => weak.upgrade(), + Self::Strong(strong) => Some(strong.clone()), + } + } + /// Get a weak `InstanceRef`. + pub fn get_weak(&self) -> WeakInstanceRef { + match self { + Self::Weak(weak) => weak.clone(), + Self::Strong(strong) => strong.downgrade(), + } + } + + /// TODO: document this + pub fn into_weak(&mut self) { + let new = self.get_weak(); + *self = Self::Weak(new); + } + + /// Check if the reference contained is strong. + pub fn is_strong(&self) -> bool { + matches!(self, WeakOrStrongInstanceRef::Strong(_)) + } } diff --git a/lib/vm/src/lib.rs b/lib/vm/src/lib.rs index dc4a1d8f2fb..8fb3868a555 100644 --- a/lib/vm/src/lib.rs +++ b/lib/vm/src/lib.rs @@ -43,7 +43,7 @@ pub use crate::func_data_registry::{FuncDataRegistry, VMFuncRef}; pub use crate::global::*; pub use crate::imports::Imports; pub use crate::instance::{ - ImportFunctionEnv, ImportInitializerFuncPtr, InstanceAllocator, InstanceHandle, + ImportFunctionEnv, ImportInitializerFuncPtr, InstanceAllocator, InstanceHandle, WeakInstanceRef }; pub use crate::memory::{LinearMemory, Memory, MemoryError, MemoryStyle}; pub use crate::mmap::Mmap; diff --git a/lib/wasi/src/lib.rs b/lib/wasi/src/lib.rs index 944ab34e7f7..9277c55c302 100644 --- a/lib/wasi/src/lib.rs +++ b/lib/wasi/src/lib.rs @@ -30,8 +30,8 @@ pub use crate::utils::{get_wasi_version, get_wasi_versions, is_wasi_module, Wasi use thiserror::Error; use wasmer::{ - imports, ChainableNamedResolver, Function, ImportObject, LazyInit, Memory, Module, - NamedResolver, Store, WasmerEnv, + imports, ChainableNamedResolver, Function, ImportObject, InstanceExport, Memory, Module, + NamedResolver, Store, WasmerEnv, WeakInstanceRef }; use std::sync::{Arc, Mutex, MutexGuard}; @@ -57,7 +57,9 @@ pub struct WasiEnv { /// to lock this mutex, the program will deadlock. pub state: Arc>, #[wasmer(export)] - memory: LazyInit, + memory: LazyInit>, + //#[wasmer(instance)] + //instance: LazyInit, } impl WasiEnv { @@ -65,6 +67,7 @@ impl WasiEnv { Self { state: Arc::new(Mutex::new(state)), memory: LazyInit::new(), + //instance: LazyInit::new(), } }