mirror of
https://github.com/bevyengine/bevy
synced 2025-02-16 22:18:33 +00:00
bevy_reflect: ReflectFromPtr
to create &dyn Reflect
from a *const ()
(#4475)
# Objective https://github.com/bevyengine/bevy/pull/4447 adds functions that can fetch resources/components as `*const ()` ptr by providing the `ComponentId`. This alone is not enough for them to be usable safely with reflection, because there is no general way to go from the raw pointer to a `&dyn Reflect` which is the pointer + a pointer to the VTable of the `Reflect` impl. By adding a `ReflectFromPtr` type that is included in the type type registration when deriving `Reflect`, safe functions can be implemented in scripting languages that don't assume a type layout and can access the component data via reflection: ```rust #[derive(Reflect)] struct StringResource { value: String } ``` ```lua local res_id = world:resource_id_by_name("example::StringResource") local res = world:resource(res_id) print(res.value) ``` ## Solution 1. add a `ReflectFromPtr` type with a `FromType<T: Reflect>` implementation and the following methods: - ` pub unsafe fn as_reflect_ptr<'a>(&self, val: Ptr<'a>) -> &'a dyn Reflect` - ` pub unsafe fn as_reflect_ptr_mut<'a>(&self, val: PtrMut<'a>) -> &'a mud dyn Reflect` Safety requirements of the methods are that you need to check that the `ReflectFromPtr` was constructed for the correct type. 2. add that type to the `TypeRegistration` in the `GetTypeRegistration` impl generated by `#[derive(Reflect)]`. This is different to other reflected traits because it doesn't need `#[reflect(ReflectReflectFromPtr)]` which IMO should be there by default. Co-authored-by: Jakob Hellermann <hellermann@sipgate.de> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
parent
0fa499a7d0
commit
7dcfaaef67
4 changed files with 139 additions and 6 deletions
|
@ -16,6 +16,7 @@ bevy = ["glam", "smallvec"]
|
||||||
# bevy
|
# bevy
|
||||||
bevy_reflect_derive = { path = "bevy_reflect_derive", version = "0.8.0-dev" }
|
bevy_reflect_derive = { path = "bevy_reflect_derive", version = "0.8.0-dev" }
|
||||||
bevy_utils = { path = "../bevy_utils", version = "0.8.0-dev" }
|
bevy_utils = { path = "../bevy_utils", version = "0.8.0-dev" }
|
||||||
|
bevy_ptr = { path = "../bevy_ptr", version = "0.8.0-dev" }
|
||||||
|
|
||||||
# other
|
# other
|
||||||
erased-serde = "0.3"
|
erased-serde = "0.3"
|
||||||
|
|
|
@ -17,6 +17,7 @@ pub(crate) fn impl_get_type_registration(
|
||||||
impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_name #ty_generics #where_clause {
|
impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_name #ty_generics #where_clause {
|
||||||
fn get_type_registration() -> #bevy_reflect_path::TypeRegistration {
|
fn get_type_registration() -> #bevy_reflect_path::TypeRegistration {
|
||||||
let mut registration = #bevy_reflect_path::TypeRegistration::of::<#type_name #ty_generics>();
|
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.insert::<#registration_data>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());)*
|
||||||
registration
|
registration
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate as bevy_reflect;
|
use crate::{self as bevy_reflect, ReflectFromPtr};
|
||||||
use crate::{
|
use crate::{
|
||||||
map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicMap, FromReflect, FromType,
|
map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicMap, FromReflect, FromType,
|
||||||
GetTypeRegistration, List, ListInfo, Map, MapInfo, MapIter, Reflect, ReflectDeserialize,
|
GetTypeRegistration, List, ListInfo, Map, MapInfo, MapIter, Reflect, ReflectDeserialize,
|
||||||
|
@ -175,6 +175,7 @@ impl<T: FromReflect + for<'de> Deserialize<'de>> GetTypeRegistration for Vec<T>
|
||||||
fn get_type_registration() -> TypeRegistration {
|
fn get_type_registration() -> TypeRegistration {
|
||||||
let mut registration = TypeRegistration::of::<Vec<T>>();
|
let mut registration = TypeRegistration::of::<Vec<T>>();
|
||||||
registration.insert::<ReflectDeserialize>(FromType::<Vec<T>>::from_type());
|
registration.insert::<ReflectDeserialize>(FromType::<Vec<T>>::from_type());
|
||||||
|
registration.insert::<ReflectFromPtr>(FromType::<Vec<T>>::from_type());
|
||||||
registration
|
registration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -326,8 +327,9 @@ where
|
||||||
V: FromReflect + Clone + for<'de> Deserialize<'de>,
|
V: FromReflect + Clone + for<'de> Deserialize<'de>,
|
||||||
{
|
{
|
||||||
fn get_type_registration() -> TypeRegistration {
|
fn get_type_registration() -> TypeRegistration {
|
||||||
let mut registration = TypeRegistration::of::<Self>();
|
let mut registration = TypeRegistration::of::<HashMap<K, V>>();
|
||||||
registration.insert::<ReflectDeserialize>(FromType::<Self>::from_type());
|
registration.insert::<ReflectDeserialize>(FromType::<HashMap<K, V>>::from_type());
|
||||||
|
registration.insert::<ReflectFromPtr>(FromType::<HashMap<K, V>>::from_type());
|
||||||
registration
|
registration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -575,6 +577,7 @@ impl GetTypeRegistration for Cow<'static, str> {
|
||||||
fn get_type_registration() -> TypeRegistration {
|
fn get_type_registration() -> TypeRegistration {
|
||||||
let mut registration = TypeRegistration::of::<Cow<'static, str>>();
|
let mut registration = TypeRegistration::of::<Cow<'static, str>>();
|
||||||
registration.insert::<ReflectDeserialize>(FromType::<Cow<'static, str>>::from_type());
|
registration.insert::<ReflectDeserialize>(FromType::<Cow<'static, str>>::from_type());
|
||||||
|
registration.insert::<ReflectFromPtr>(FromType::<Cow<'static, str>>::from_type());
|
||||||
registration.insert::<ReflectSerialize>(FromType::<Cow<'static, str>>::from_type());
|
registration.insert::<ReflectSerialize>(FromType::<Cow<'static, str>>::from_type());
|
||||||
registration
|
registration
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::serde::Serializable;
|
use crate::{serde::Serializable, Reflect, TypeInfo, Typed};
|
||||||
use crate::{Reflect, TypeInfo, Typed};
|
use bevy_ptr::{Ptr, PtrMut};
|
||||||
use bevy_utils::{HashMap, HashSet};
|
use bevy_utils::{HashMap, HashSet};
|
||||||
use downcast_rs::{impl_downcast, Downcast};
|
use downcast_rs::{impl_downcast, Downcast};
|
||||||
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||||
|
@ -411,11 +411,139 @@ impl<T: for<'a> Deserialize<'a> + Reflect> FromType<T> for ReflectDeserialize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [`Reflect`] values are commonly used in situations where the actual types of values
|
||||||
|
/// are not known at runtime. In such situations you might have access to a `*const ()` pointer
|
||||||
|
/// that you know implements [`Reflect`], but have no way of turning it into a `&dyn Reflect`.
|
||||||
|
///
|
||||||
|
/// This is where [`ReflectFromPtr`] comes in, when creating a [`ReflectFromPtr`] for a given type `T: Reflect`.
|
||||||
|
/// Internally, this saves a concrete function `*const T -> const dyn Reflect` which lets you create a trait object of [`Reflect`]
|
||||||
|
/// from a pointer.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```rust
|
||||||
|
/// use bevy_reflect::{TypeRegistry, Reflect, ReflectFromPtr};
|
||||||
|
/// use bevy_ptr::Ptr;
|
||||||
|
/// use std::ptr::NonNull;
|
||||||
|
///
|
||||||
|
/// #[derive(Reflect)]
|
||||||
|
/// struct Reflected(String);
|
||||||
|
///
|
||||||
|
/// let mut type_registry = TypeRegistry::default();
|
||||||
|
/// type_registry.register::<Reflected>();
|
||||||
|
///
|
||||||
|
/// let mut value = Reflected("Hello world!".to_string());
|
||||||
|
/// let value = unsafe { Ptr::new(NonNull::from(&mut value).cast()) };
|
||||||
|
///
|
||||||
|
/// let reflect_data = type_registry.get(std::any::TypeId::of::<Reflected>()).unwrap();
|
||||||
|
/// let reflect_from_ptr = reflect_data.data::<ReflectFromPtr>().unwrap();
|
||||||
|
/// // SAFE: `value` is of type `Reflected`, which the `ReflectFromPtr` was created for
|
||||||
|
/// let value = unsafe { reflect_from_ptr.as_reflect_ptr(value) };
|
||||||
|
///
|
||||||
|
/// assert_eq!(value.downcast_ref::<Reflected>().unwrap().0, "Hello world!");
|
||||||
|
/// ```
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ReflectFromPtr {
|
||||||
|
type_id: TypeId,
|
||||||
|
to_reflect: for<'a> unsafe fn(Ptr<'a>) -> &'a dyn Reflect,
|
||||||
|
to_reflect_mut: for<'a> unsafe fn(PtrMut<'a>) -> &'a mut dyn Reflect,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReflectFromPtr {
|
||||||
|
/// Returns the [`TypeId`] that the [`ReflectFromPtr`] was constructed for
|
||||||
|
pub fn type_id(&self) -> TypeId {
|
||||||
|
self.type_id
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `val` must be a pointer to value of the type that the [`ReflectFromPtr`] was constructed for.
|
||||||
|
/// This can be verified by checking that the type id returned by [`ReflectFromPtr::type_id`] is the expected one.
|
||||||
|
pub unsafe fn as_reflect_ptr<'a>(&self, val: Ptr<'a>) -> &'a dyn Reflect {
|
||||||
|
(self.to_reflect)(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `val` must be a pointer to a value of the type that the [`ReflectFromPtr`] was constructed for
|
||||||
|
/// This can be verified by checking that the type id returned by [`ReflectFromPtr::type_id`] is the expected one.
|
||||||
|
pub unsafe fn as_reflect_ptr_mut<'a>(&self, val: PtrMut<'a>) -> &'a mut dyn Reflect {
|
||||||
|
(self.to_reflect_mut)(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Reflect> FromType<T> for ReflectFromPtr {
|
||||||
|
fn from_type() -> Self {
|
||||||
|
ReflectFromPtr {
|
||||||
|
type_id: std::any::TypeId::of::<T>(),
|
||||||
|
to_reflect: |ptr| {
|
||||||
|
// SAFE: only called from `as_reflect`, where the `ptr` is guaranteed to be of type `T`,
|
||||||
|
// and `as_reflect_ptr`, where the caller promises to call it with type `T`
|
||||||
|
unsafe { ptr.deref::<T>() as &dyn Reflect }
|
||||||
|
},
|
||||||
|
to_reflect_mut: |ptr| {
|
||||||
|
// SAFE: only called from `as_reflect_mut`, where the `ptr` is guaranteed to be of type `T`,
|
||||||
|
// and `as_reflect_ptr_mut`, where the caller promises to call it with type `T`
|
||||||
|
unsafe { ptr.deref_mut::<T>() as &mut dyn Reflect }
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::TypeRegistration;
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
|
use crate::{GetTypeRegistration, ReflectFromPtr, TypeRegistration};
|
||||||
|
use bevy_ptr::{Ptr, PtrMut};
|
||||||
use bevy_utils::HashMap;
|
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();
|
||||||
|
|
||||||
|
// not required in this situation because we no nobody messed with the TypeRegistry,
|
||||||
|
// but in the general case somebody could have replaced the ReflectFromPtr with an
|
||||||
|
// instance for another type, so then we'd need to check that the type is the expected one
|
||||||
|
assert_eq!(reflect_from_ptr.type_id(), std::any::TypeId::of::<Foo>());
|
||||||
|
|
||||||
|
let mut value = Foo { a: 1.0 };
|
||||||
|
{
|
||||||
|
// SAFETY: lifetime doesn't outlive original value, access is unique
|
||||||
|
let value = unsafe { PtrMut::new(NonNull::from(&mut value).cast()) };
|
||||||
|
// SAFETY: reflect_from_ptr was constructed for the correct type
|
||||||
|
let dyn_reflect = unsafe { reflect_from_ptr.as_reflect_ptr_mut(value) };
|
||||||
|
match dyn_reflect.reflect_mut() {
|
||||||
|
bevy_reflect::ReflectMut::Struct(strukt) => {
|
||||||
|
strukt.field_mut("a").unwrap().apply(&2.0f32);
|
||||||
|
}
|
||||||
|
_ => panic!("invalid reflection"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// SAFETY: lifetime doesn't outlive original value
|
||||||
|
let value = unsafe { Ptr::new(NonNull::from(&mut value).cast()) };
|
||||||
|
// SAFETY: reflect_from_ptr was constructed for the correct type
|
||||||
|
let dyn_reflect = unsafe { reflect_from_ptr.as_reflect_ptr(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]
|
#[test]
|
||||||
fn test_property_type_registration() {
|
fn test_property_type_registration() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
Loading…
Add table
Reference in a new issue