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

[Merged by Bors] - bevy_reflect: ReflectFromPtr to create &dyn Reflect from a *const () #4475

Closed
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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/bevy_reflect/bevy_reflect_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,7 @@ fn impl_get_type_registration(
impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_name #ty_generics #where_clause {
fn get_type_registration() -> #bevy_reflect_path::TypeRegistration {
let mut registration = #bevy_reflect_path::TypeRegistration::of::<#type_name #ty_generics>();
registration.insert::<#bevy_reflect_path::ReflectFromPtr>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());
#(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());)*
registration
}
Expand Down
5 changes: 4 additions & 1 deletion crates/bevy_reflect/src/impls/std.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate as bevy_reflect;
use crate::{self as bevy_reflect, ReflectFromPtr};
use crate::{
map_partial_eq, serde::Serializable, DynamicMap, FromReflect, FromType, GetTypeRegistration,
List, ListIter, Map, MapIter, Reflect, ReflectDeserialize, ReflectMut, ReflectRef,
Expand Down Expand Up @@ -148,6 +148,7 @@ impl<T: FromReflect + for<'de> Deserialize<'de>> GetTypeRegistration for Vec<T>
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<Vec<T>>();
registration.insert::<ReflectDeserialize>(FromType::<Vec<T>>::from_type());
registration.insert::<ReflectFromPtr>(FromType::<Vec<T>>::from_type());
registration
}
}
Expand Down Expand Up @@ -270,6 +271,7 @@ where
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<Self>();
registration.insert::<ReflectDeserialize>(FromType::<Self>::from_type());
registration.insert::<ReflectFromPtr>(FromType::<Self>::from_type());
jakobhellermann marked this conversation as resolved.
Show resolved Hide resolved
registration
}
}
Expand Down Expand Up @@ -355,6 +357,7 @@ impl GetTypeRegistration for Cow<'static, str> {
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<Cow<'static, str>>();
registration.insert::<ReflectDeserialize>(FromType::<Cow<'static, str>>::from_type());
registration.insert::<ReflectFromPtr>(FromType::<Cow<'static, str>>::from_type());
registration
}
}
Expand Down
96 changes: 94 additions & 2 deletions crates/bevy_reflect/src/type_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use bevy_utils::{HashMap, HashSet};
use downcast_rs::{impl_downcast, Downcast};
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use serde::Deserialize;
use std::{any::TypeId, fmt::Debug, sync::Arc};
use std::{any::TypeId, fmt::Debug, marker::PhantomData, sync::Arc};

/// A registry of reflected types.
#[derive(Default)]
Expand Down Expand Up @@ -350,11 +350,103 @@ impl<T: for<'a> Deserialize<'a> + Reflect> FromType<T> for ReflectDeserialize {
}
}

#[derive(Clone)]
pub struct ReflectFromPtr {
jakobhellermann marked this conversation as resolved.
Show resolved Hide resolved
type_id: TypeId,
to_reflect: for<'a> unsafe fn(*const (), lt: PhantomData<&'a ()>) -> &'a dyn Reflect,
to_reflect_mut: for<'a> unsafe fn(*mut (), lt: PhantomData<&'a ()>) -> &'a mut dyn Reflect,
}

impl ReflectFromPtr {
/// # Safety:
/// - `val` must be a pointer to a value of the type that the [`ReflectFromPtr`] was constructed for
/// - the lifetime `'a` of the return type can be arbitrarily chosen by the caller, who must ensure that during
/// that lifetime, `val` is valid
pub unsafe fn to_reflect_ptr<'a>(&self, val: *const ()) -> &'a dyn Reflect {
jakobhellermann marked this conversation as resolved.
Show resolved Hide resolved
(self.to_reflect)(val, PhantomData)
}

/// # Safety:
/// - `val` must be a pointer to a value of the type that the [`ReflectFromPtr`] was constructed for
/// - the lifetime `'a` of the return type can be arbitrarily chosen by the caller, who must ensure that during
/// that lifetime, `val` is valid and not aliased
pub unsafe fn to_reflect_ptr_mut<'a>(&self, val: *mut ()) -> &'a mut dyn Reflect {
(self.to_reflect_mut)(val, PhantomData)
}

pub fn to_reflect<'a, T: 'static>(&self, val: &'a T) -> &'a dyn Reflect {
assert_eq!(self.type_id, std::any::TypeId::of::<T>());
// SAFE: the lifetime of `val` is the same as the lifetime of the return value
// and the type of `val` has been checked to be the same correct one
unsafe { self.to_reflect_ptr(val as *const _ as *const ()) }
}

pub fn to_reflect_mut<'a, T: 'static>(&self, val: &'a mut T) -> &'a mut dyn Reflect {
assert_eq!(self.type_id, std::any::TypeId::of::<T>());
// SAFE: the lifetime of `val` is the same as the lifetime of the return value
// and the type of `val` has been checked to be the same correct one
unsafe { self.to_reflect_ptr_mut(val as *mut _ as *mut ()) }
}
}

impl<T: Reflect + 'static> FromType<T> for ReflectFromPtr {
jakobhellermann marked this conversation as resolved.
Show resolved Hide resolved
fn from_type() -> Self {
ReflectFromPtr {
type_id: std::any::TypeId::of::<T>(),
to_reflect: |ptr, _lt| {
// SAFE: can only be called by `to_reflect_ptr` where the caller promises the safety requirements
// or `to_reflect` which is typed and checks that the correct type is used.
let val: &T = unsafe { &*ptr.cast::<T>() };
val as &dyn Reflect
},
to_reflect_mut: |ptr, _lt| {
// SAFE: can only be called by `to_reflect_ptr_mut` where the caller promises the safety requirements
// or `to_reflect_mut` which is typed and checks that the correct type is used.
let val: &mut T = unsafe { &mut *ptr.cast::<T>() };
val as &mut dyn Reflect
},
}
}
}

#[cfg(test)]
mod test {
use crate::TypeRegistration;
use crate::{GetTypeRegistration, ReflectFromPtr, TypeRegistration};
use bevy_utils::HashMap;

use crate as bevy_reflect;
use crate::Reflect;

#[test]
fn test_reflect_from_ptr() {
#[derive(Reflect)]
struct Foo {
a: f32,
}

let foo_registration = <Foo as GetTypeRegistration>::get_type_registration();
let reflect_from_ptr = foo_registration.data::<ReflectFromPtr>().unwrap();

let mut value = Foo { a: 1.0 };

let dyn_reflect = reflect_from_ptr.to_reflect_mut(&mut value);
match dyn_reflect.reflect_mut() {
bevy_reflect::ReflectMut::Struct(strukt) => {
strukt.field_mut("a").unwrap().apply(&2.0f32)
}
_ => panic!("invalid reflection"),
}

let dyn_reflect = reflect_from_ptr.to_reflect(&value);
match dyn_reflect.reflect_ref() {
bevy_reflect::ReflectRef::Struct(strukt) => {
let a = strukt.field("a").unwrap().downcast_ref::<f32>().unwrap();
assert_eq!(*a, 2.0);
}
_ => panic!("invalid reflection"),
}
}

#[test]
fn test_get_short_name() {
assert_eq!(
Expand Down