Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for coercing bevy mutable references to values #643

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/rune/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ similar = { version = "2.2.1", optional = true, features = ["inline", "bytes"] }
sha2 = { version = "0.10.6", optional = true }
base64 = { version = "0.21.0", optional = true }
rand = { version = "0.8.5", optional = true }
bevy = { git = "https://github.com/james-j-obrien/bevy", branch = "dynamic-term-builder", optional = true}

[dev-dependencies]
tokio = { version = "1.28.1", features = ["full"] }
Expand Down
130 changes: 130 additions & 0 deletions crates/rune/src/bevy_related.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
use core::any::TypeId;
use core::fmt;

use bevy::ecs::change_detection::MutUntyped;
use bevy::prelude::Mut;

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<T>(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<Any>` 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<T>(
data: bevy::prelude::Mut<'_, T>,
) -> alloc::Result<(Shared<AnyObj>, 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<T>(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::<T>,
as_ptr: as_bevy_ptr_impl::<T>,
as_ptr_mut: as_bevy_ptr_mut_impl::<T>,
debug: debug_mut_impl::<T>,
type_name: type_name_impl::<T>,
type_hash: type_hash_impl::<T>,
};

AnyObj::new_raw(vtable, data)
}

fn bevy_mut_drop<T>(this: *mut ()) {
unsafe {
drop(Box::from_raw_in(this as *mut MutUntyped<'static>, Global));
}
}

fn as_bevy_ptr_impl<T>(this: *const (), expected: TypeId) -> Option<*const ()>
where
T: ?Sized + 'static,
{
if expected == TypeId::of::<T>() {
unsafe {
let this = this as *const () as *const MutUntyped<'static>;
Some((*this).as_ref().as_ptr() as *const _ as *const ())
}
} else {
None
}
}

fn as_bevy_ptr_mut_impl<T>(this: *mut (), expected: TypeId) -> Option<*mut ()>
where
T: ?Sized + 'static,
{
if expected == TypeId::of::<T>() {
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<T>(f: &mut fmt::Formatter<'_>) -> fmt::Result
where
T: ?Sized + Named,
{
write!(f, "&mut {}", T::BASE_NAME)
}

fn type_name_impl<T>() -> RawStr
where
T: ?Sized + Named,
{
T::BASE_NAME
}

fn type_hash_impl<T>() -> Hash
where
T: ?Sized + Any,
{
T::type_hash()
}
4 changes: 4 additions & 0 deletions crates/rune/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -618,5 +618,9 @@ pub mod __private {
pub use rust_alloc::boxed::Box;
}

/// Bevy support.
#[cfg(feature = "bevy")]
pub mod bevy_related;

#[cfg(test)]
mod tests;
2 changes: 1 addition & 1 deletion crates/rune/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
54 changes: 38 additions & 16 deletions crates/rune/src/runtime/any_obj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ impl AnyObj {
kind: AnyObjKind::Owned,
drop: drop_impl::<T>,
as_ptr: as_ptr_impl::<T>,
as_ptr_mut: as_ptr_mut_impl::<T>,
debug: debug_impl::<T>,
type_name: type_name_impl::<T>,
type_hash: type_hash_impl::<T>,
Expand Down Expand Up @@ -137,6 +138,7 @@ impl AnyObj {
kind: AnyObjKind::RefPtr,
drop: noop_drop_impl::<T>,
as_ptr: as_ptr_impl::<T>,
as_ptr_mut: as_ptr_mut_impl::<T>,
debug: debug_ref_impl::<T>,
type_name: type_name_impl::<T>,
type_hash: type_hash_impl::<T>,
Expand Down Expand Up @@ -175,6 +177,7 @@ impl AnyObj {
pub unsafe fn from_deref<T>(data: T) -> alloc::Result<Self>
where
T: Deref,
T: DerefMut,
T::Target: Any,
{
let data = {
Expand All @@ -187,6 +190,7 @@ impl AnyObj {
kind: AnyObjKind::RefPtr,
drop: drop_impl::<T>,
as_ptr: as_ptr_deref_impl::<T>,
as_ptr_mut: as_ptr_deref_mut_impl::<T>,
debug: debug_ref_impl::<T::Target>,
type_name: type_name_impl::<T::Target>,
type_hash: type_hash_impl::<T::Target>,
Expand Down Expand Up @@ -251,6 +255,7 @@ impl AnyObj {
kind: AnyObjKind::MutPtr,
drop: noop_drop_impl::<T>,
as_ptr: as_ptr_impl::<T>,
as_ptr_mut: as_ptr_mut_impl::<T>,
debug: debug_mut_impl::<T>,
type_name: type_name_impl::<T>,
type_hash: type_hash_impl::<T>,
Expand Down Expand Up @@ -300,7 +305,8 @@ impl AnyObj {
vtable: &AnyObjVtable {
kind: AnyObjKind::MutPtr,
drop: drop_impl::<T>,
as_ptr: as_ptr_deref_mut_impl::<T>,
as_ptr: as_ptr_deref_impl::<T>,
as_ptr_mut: as_ptr_deref_mut_impl::<T>,
debug: debug_mut_impl::<T::Target>,
type_name: type_name_impl::<T::Target>,
type_hash: type_hash_impl::<T::Target>,
Expand Down Expand Up @@ -403,7 +409,8 @@ impl AnyObj {
T: Any,
{
unsafe {
(self.vtable.as_ptr)(self.data.as_ptr(), TypeId::of::<T>()).map(|v| &mut *(v as *mut _))
(self.vtable.as_ptr_mut)(self.data.as_ptr(), TypeId::of::<T>())
.map(|v| &mut *(v as *mut _))
}
}

Expand Down Expand Up @@ -431,8 +438,8 @@ impl AnyObj {
// Safety: invariants are checked at construction time.
// We have mutable access to the inner value because we have mutable
// access to the `Any`.
match unsafe { (self.vtable.as_ptr)(self.data.as_ptr(), expected) } {
Some(ptr) => Ok(ptr as *mut ()),
match unsafe { (self.vtable.as_ptr_mut)(self.data.as_ptr(), expected) } {
Some(ptr) => Ok(ptr),
None => Err(AnyObjError::Cast),
}
}
Expand Down Expand Up @@ -470,8 +477,8 @@ impl AnyObj {
// We have mutable access to the inner value because we have mutable
// access to the `Any`.
unsafe {
match (this.vtable.as_ptr)(this.data.as_ptr(), expected) {
Some(data) => Ok(data as *mut ()),
match (this.vtable.as_ptr_mut)(this.data.as_ptr(), expected) {
Some(data) => Ok(data),
None => {
let this = ManuallyDrop::into_inner(this);
Err((AnyObjError::Cast, this))
Expand Down Expand Up @@ -526,6 +533,9 @@ pub type DropFn = unsafe fn(*mut ());
/// The signature of a pointer coercion function.
pub type AsPtrFn = unsafe fn(this: *const (), expected: TypeId) -> Option<*const ()>;

/// The signature of a pointer coercion function, but mutably.
pub type AsPtrMutFn = unsafe fn(this: *mut (), expected: TypeId) -> Option<*mut ()>;

/// The signature of a descriptive type name function.
pub type DebugFn = fn(&mut fmt::Formatter<'_>) -> fmt::Result;

Expand All @@ -536,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`).
Expand All @@ -553,17 +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,
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<T>(this: *mut ()) {
Expand All @@ -581,6 +593,17 @@ where
}
}

fn as_ptr_mut_impl<T>(this: *mut (), expected: TypeId) -> Option<*mut ()>
where
T: Any,
{
if expected == TypeId::of::<T>() {
Some(this)
} else {
None
}
}

fn as_ptr_deref_impl<T: Deref>(this: *const (), expected: TypeId) -> Option<*const ()>
where
T::Target: Any,
Expand All @@ -593,18 +616,17 @@ where
}
}

fn as_ptr_deref_mut_impl<T: DerefMut>(this: *const (), expected: TypeId) -> Option<*const ()>
fn as_ptr_deref_mut_impl<T: DerefMut>(this: *mut (), expected: TypeId) -> Option<*mut ()>
where
T::Target: Any,
{
if expected == TypeId::of::<T::Target>() {
let guard = this as *mut T;
unsafe { Some((*guard).deref_mut() as *const _ as *const ()) }
unsafe { Some((*guard).deref_mut() as *mut _ as *mut ()) }
} else {
None
}
}

fn noop_drop_impl<T>(_: *mut ()) {}

fn debug_impl<T>(f: &mut fmt::Formatter<'_>) -> fmt::Result
Expand Down
4 changes: 3 additions & 1 deletion crates/rune/src/runtime/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,9 @@ impl Shared<AnyObj> {
/// # 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),
Expand Down
Loading