diff --git a/crates/rune/src/bevy_related.rs b/crates/rune/src/bevy_related.rs index 001037300..ebdd45ccc 100644 --- a/crates/rune/src/bevy_related.rs +++ b/crates/rune/src/bevy_related.rs @@ -1,51 +1,130 @@ +use core::any::TypeId; +use core::fmt; + +use bevy::ecs::change_detection::MutUntyped; use bevy::prelude::Mut; -impl crate::compile::Named for Mut<'_, T> { - const BASE_NAME: rune_core::RawStr = T::BASE_NAME; +use crate::alloc::alloc::Global; +use crate::alloc::{self, Box}; +use crate::any::Any; +use crate::compile::Named; +use crate::hash::Hash; +use crate::runtime::{ + AnyObj, AnyObjKind, AnyObjVtable, RawStr, Shared, SharedPointerGuard, Value, VmResult, +}; + +/// Unsafely convert a bevy mutable reference into a value and a guard. +/// +/// # Safety +/// +/// The value returned must not be used after the guard associated with it has +/// been dropped. +pub unsafe fn bevy_mut_to_value(this: Mut<'_, T>) -> VmResult<(Value, SharedPointerGuard)> +where + T: Any, +{ + let (shared, guard) = vm_try!(from_bevy_mut(this)); + VmResult::Ok((Value::from(shared), guard)) +} + +/// Construct a `Shared` from a bevy specific mutable pointer that does change detection, this will be "taken" +/// once the returned guard is dropped. +/// +/// # Safety +/// +/// The reference must be valid for the duration of the guard. +/// +/// # Examples +/// +unsafe fn from_bevy_mut( + data: bevy::prelude::Mut<'_, T>, +) -> alloc::Result<(Shared, SharedPointerGuard)> +where + T: Any, +{ + Shared::unsafe_from_any_pointer(anyobj_from_bevy_mut(data)) +} + +/// Construct an Any that wraps a bevy specific mutable pointer that does change +/// detection. +/// +/// # Safety +/// +/// Caller must ensure that the returned `AnyObj` doesn't outlive the reference +/// it is wrapping. +unsafe fn anyobj_from_bevy_mut(data: Mut<'_, T>) -> AnyObj +where + T: Any, +{ + let untyped = MutUntyped::from(data); + let (ptr, _) = Box::into_raw_with_allocator(Box::try_new(untyped).unwrap()); + let data = ptr as *const _ as *const (); + + let vtable = &AnyObjVtable { + kind: AnyObjKind::MutPtr, + drop: bevy_mut_drop::, + as_ptr: as_bevy_ptr_impl::, + as_ptr_mut: as_bevy_ptr_mut_impl::, + debug: debug_mut_impl::, + type_name: type_name_impl::, + type_hash: type_hash_impl::, + }; + + AnyObj::new_raw(vtable, data) } -impl crate::Any for Mut<'static, T> { - fn type_hash() -> rune_core::Hash { - T::type_hash() +fn bevy_mut_drop(this: *mut ()) { + unsafe { + drop(Box::from_raw_in(this as *mut MutUntyped<'static>, Global)); } } -/// This is for internal use, need to create a second trait so that way -/// this trait can be implemented for *all* T, without causing the compiler -/// to cry about other types that implement the other UnsafeToValue -pub trait UnsafeToValue2: Sized { - /// Convert into a value. - /// - /// # Safety - /// - /// The value returned must not be used after the guard associated with it - /// has been dropped. - unsafe fn unsafe_to_value( - self, - ) -> crate::runtime::VmResult<(crate::Value, crate::runtime::SharedPointerGuard)>; -} - -impl crate::__private::InstallWith for Mut<'static, T> { - fn install_with( - module: &mut crate::__private::Module, - ) -> core::result::Result<(), crate::compile::ContextError> { - T::install_with(module)?; - Ok(()) +fn as_bevy_ptr_impl(this: *const (), expected: TypeId) -> Option<*const ()> +where + T: ?Sized + 'static, +{ + if expected == TypeId::of::() { + unsafe { + let this = this as *const () as *const MutUntyped<'static>; + Some((*this).as_ref().as_ptr() as *const _ as *const ()) + } + } else { + None } } -impl UnsafeToValue2 for Mut<'_, T> { - unsafe fn unsafe_to_value( - self, - ) -> crate::runtime::VmResult<(crate::runtime::Value, crate::runtime::SharedPointerGuard)> { - let this: Mut = unsafe { std::mem::transmute(self) }; - let (shared, guard) = - match crate::runtime::try_result(crate::runtime::Shared::from_bevy_mut(this)) { - crate::runtime::VmResult::Ok(value) => value, - crate::runtime::VmResult::Err(err) => { - return crate::runtime::VmResult::Err(crate::runtime::VmError::from(err)); - } - }; - crate::runtime::VmResult::Ok((crate::runtime::Value::from(shared), guard)) +fn as_bevy_ptr_mut_impl(this: *mut (), expected: TypeId) -> Option<*mut ()> +where + T: ?Sized + 'static, +{ + if expected == TypeId::of::() { + unsafe { + let this = this as *mut () as *mut MutUntyped<'static>; + // NB: `as_mut` calls `set_changed`. + Some((*this).as_mut().as_ptr() as *mut ()) + } + } else { + None } } + +fn debug_mut_impl(f: &mut fmt::Formatter<'_>) -> fmt::Result +where + T: ?Sized + Named, +{ + write!(f, "&mut {}", T::BASE_NAME) +} + +fn type_name_impl() -> RawStr +where + T: ?Sized + Named, +{ + T::BASE_NAME +} + +fn type_hash_impl() -> Hash +where + T: ?Sized + Any, +{ + T::type_hash() +} diff --git a/crates/rune/src/lib.rs b/crates/rune/src/lib.rs index f3c4ec7d1..0ef779b90 100644 --- a/crates/rune/src/lib.rs +++ b/crates/rune/src/lib.rs @@ -618,8 +618,9 @@ pub mod __private { pub use rust_alloc::boxed::Box; } -/// Bevy related specific traits and implementations +/// Bevy support. #[cfg(feature = "bevy")] pub mod bevy_related; + #[cfg(test)] mod tests; diff --git a/crates/rune/src/runtime.rs b/crates/rune/src/runtime.rs index 9850c05fe..fe957620e 100644 --- a/crates/rune/src/runtime.rs +++ b/crates/rune/src/runtime.rs @@ -10,7 +10,7 @@ pub use self::access::{ }; mod any_obj; -pub use self::any_obj::{AnyObj, AnyObjError, AnyObjVtable}; +pub use self::any_obj::{AnyObj, AnyObjError, AnyObjKind, AnyObjVtable}; mod args; pub use self::args::Args; diff --git a/crates/rune/src/runtime/any_obj.rs b/crates/rune/src/runtime/any_obj.rs index d0af8b583..47d992e3e 100644 --- a/crates/rune/src/runtime/any_obj.rs +++ b/crates/rune/src/runtime/any_obj.rs @@ -264,35 +264,6 @@ impl AnyObj { } } - /// Construct an Any that wraps a bevy specific mutable pointer that does change detection. - /// - /// # Safety - /// - /// Caller must ensure that the returned `AnyObj` doesn't outlive the - /// reference it is wrapping. - #[cfg(feature = "bevy")] - pub unsafe fn from_bevy_mut(data: bevy::prelude::Mut) -> Self - where - T: Any, - { - let untyped = bevy::ecs::change_detection::MutUntyped::from(data); - let (ptr, _) = Box::into_raw_with_allocator(Box::try_new(untyped).unwrap()); - let data = ptr::NonNull::new_unchecked(ptr as *mut _ as *mut ()); - - Self { - vtable: &AnyObjVtable { - kind: AnyObjKind::MutPtr, - drop: bevy_mut_drop::, - as_ptr: as_bevy_ptr_impl::, - as_ptr_mut: as_bevy_ptr_mut_impl::, - debug: debug_mut_impl::, - type_name: type_name_impl::, - type_hash: type_hash_impl::, - }, - data, - } - } - /// Construct an Any that wraps a DerefMut type, behaving as the Target of /// the DerefMut implementation /// @@ -575,7 +546,7 @@ pub type TypeNameFn = fn() -> RawStr; pub type TypeHashFn = fn() -> Hash; /// The kind of the stored value in the `AnyObj`. -enum AnyObjKind { +pub enum AnyObjKind { /// A boxed value that is owned. Owned, /// A pointer (`*const T`). @@ -592,19 +563,19 @@ enum AnyObjKind { #[repr(C)] pub struct AnyObjVtable { /// The kind of the object being stored. Determines how it can be accessed. - kind: AnyObjKind, + pub kind: AnyObjKind, /// The underlying drop implementation for the stored type. - drop: DropFn, + pub drop: DropFn, /// Punt the inner pointer to the type corresponding to the type hash. - as_ptr: AsPtrFn, + pub as_ptr: AsPtrFn, /// Punt the inner pointer to the type corresponding to the type hash, but mutably, - as_ptr_mut: AsPtrMutFn, + pub as_ptr_mut: AsPtrMutFn, /// Type information for diagnostics. - debug: DebugFn, + pub debug: DebugFn, /// Type name accessor. - type_name: TypeNameFn, + pub type_name: TypeNameFn, /// Get the type hash of the stored type. - type_hash: TypeHashFn, + pub type_hash: TypeHashFn, } unsafe fn drop_impl(this: *mut ()) { @@ -633,30 +604,6 @@ where } } -#[cfg(feature = "bevy")] -fn as_bevy_ptr_impl(this: *const (), expected: TypeId) -> Option<*const ()> { - if expected == TypeId::of::() { - let this = this as *const () as *const bevy::ecs::change_detection::MutUntyped; - let this = unsafe { &*this }.as_ref(); - Some(unsafe { this.as_ptr() } as *const _ as *const ()) - } else { - None - } -} - -#[cfg(feature = "bevy")] -fn as_bevy_ptr_mut_impl(this: *mut (), expected: TypeId) -> Option<*mut ()> { - use bevy::prelude::DetectChangesMut; - if expected == TypeId::of::() { - let this = this as *mut () as *mut bevy::ecs::change_detection::MutUntyped; - unsafe { DetectChangesMut::set_changed(&mut *this) } - let this = unsafe { DetectChangesMut::bypass_change_detection(&mut *this) }; - Some(this.as_ptr() as *mut _ as *mut ()) - } else { - None - } -} - fn as_ptr_deref_impl(this: *const (), expected: TypeId) -> Option<*const ()> where T::Target: Any, @@ -682,16 +629,6 @@ where } fn noop_drop_impl(_: *mut ()) {} -#[cfg(feature = "bevy")] -fn bevy_mut_drop(this: *mut ()) { - unsafe { - drop(Box::from_raw_in( - this as *mut bevy::ecs::change_detection::MutUntyped, - Global, - )); - } -} - fn debug_impl(f: &mut fmt::Formatter<'_>) -> fmt::Result where T: Any, diff --git a/crates/rune/src/runtime/shared.rs b/crates/rune/src/runtime/shared.rs index 1dec50ddf..7f5229682 100644 --- a/crates/rune/src/runtime/shared.rs +++ b/crates/rune/src/runtime/shared.rs @@ -450,32 +450,15 @@ impl Shared { Self::unsafe_from_any_pointer(AnyObj::from_mut(data)) } - /// Construct a `Shared` from a bevy specific mutable pointer that does change detection, this will be "taken" - /// once the returned guard is dropped. - /// - /// # Safety - /// - /// The reference must be valid for the duration of the guard. - /// - /// # Examples - /// - #[cfg(feature = "bevy")] - pub unsafe fn from_bevy_mut( - data: bevy::prelude::Mut, - ) -> alloc::Result<(Self, SharedPointerGuard)> - where - T: Any, - { - Self::unsafe_from_any_pointer(AnyObj::from_bevy_mut(data)) - } - /// Construct a `Shared` from an Any which is expected to wrap a /// pointer, that will be "taken" once the returned guard is dropped. /// /// # Safety /// /// The reference must be valid for the duration of the guard. - unsafe fn unsafe_from_any_pointer(any: AnyObj) -> alloc::Result<(Self, SharedPointerGuard)> { + pub(crate) unsafe fn unsafe_from_any_pointer( + any: AnyObj, + ) -> alloc::Result<(Self, SharedPointerGuard)> { let shared = SharedBox { access: Access::new(true), count: Cell::new(2),