Skip to content

Commit 852afa5

Browse files
jakobhellermanncart
authored andcommitted
bevy_reflect: ReflectFromPtr to create &dyn Reflect from a *const () (bevyengine#4475)
# Objective bevyengine#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 <[email protected]> Co-authored-by: Carter Anderson <[email protected]>
1 parent 3b2b959 commit 852afa5

File tree

4 files changed

+139
-6
lines changed

4 files changed

+139
-6
lines changed

crates/bevy_reflect/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ bevy = ["glam", "smallvec"]
1616
# bevy
1717
bevy_reflect_derive = { path = "bevy_reflect_derive", version = "0.8.0-dev" }
1818
bevy_utils = { path = "../bevy_utils", version = "0.8.0-dev" }
19+
bevy_ptr = { path = "../bevy_ptr", version = "0.8.0-dev" }
1920

2021
# other
2122
erased-serde = "0.3"

crates/bevy_reflect/bevy_reflect_derive/src/registration.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub(crate) fn impl_get_type_registration(
1717
impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_name #ty_generics #where_clause {
1818
fn get_type_registration() -> #bevy_reflect_path::TypeRegistration {
1919
let mut registration = #bevy_reflect_path::TypeRegistration::of::<#type_name #ty_generics>();
20+
registration.insert::<#bevy_reflect_path::ReflectFromPtr>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());
2021
#(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());)*
2122
registration
2223
}

crates/bevy_reflect/src/impls/std.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate as bevy_reflect;
1+
use crate::{self as bevy_reflect, ReflectFromPtr};
22
use crate::{
33
map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicMap, FromReflect, FromType,
44
GetTypeRegistration, List, ListInfo, Map, MapInfo, MapIter, Reflect, ReflectDeserialize,
@@ -175,6 +175,7 @@ impl<T: FromReflect + for<'de> Deserialize<'de>> GetTypeRegistration for Vec<T>
175175
fn get_type_registration() -> TypeRegistration {
176176
let mut registration = TypeRegistration::of::<Vec<T>>();
177177
registration.insert::<ReflectDeserialize>(FromType::<Vec<T>>::from_type());
178+
registration.insert::<ReflectFromPtr>(FromType::<Vec<T>>::from_type());
178179
registration
179180
}
180181
}
@@ -326,8 +327,9 @@ where
326327
V: FromReflect + Clone + for<'de> Deserialize<'de>,
327328
{
328329
fn get_type_registration() -> TypeRegistration {
329-
let mut registration = TypeRegistration::of::<Self>();
330-
registration.insert::<ReflectDeserialize>(FromType::<Self>::from_type());
330+
let mut registration = TypeRegistration::of::<HashMap<K, V>>();
331+
registration.insert::<ReflectDeserialize>(FromType::<HashMap<K, V>>::from_type());
332+
registration.insert::<ReflectFromPtr>(FromType::<HashMap<K, V>>::from_type());
331333
registration
332334
}
333335
}
@@ -575,6 +577,7 @@ impl GetTypeRegistration for Cow<'static, str> {
575577
fn get_type_registration() -> TypeRegistration {
576578
let mut registration = TypeRegistration::of::<Cow<'static, str>>();
577579
registration.insert::<ReflectDeserialize>(FromType::<Cow<'static, str>>::from_type());
580+
registration.insert::<ReflectFromPtr>(FromType::<Cow<'static, str>>::from_type());
578581
registration.insert::<ReflectSerialize>(FromType::<Cow<'static, str>>::from_type());
579582
registration
580583
}

crates/bevy_reflect/src/type_registry.rs

Lines changed: 131 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use crate::serde::Serializable;
2-
use crate::{Reflect, TypeInfo, Typed};
1+
use crate::{serde::Serializable, Reflect, TypeInfo, Typed};
2+
use bevy_ptr::{Ptr, PtrMut};
33
use bevy_utils::{HashMap, HashSet};
44
use downcast_rs::{impl_downcast, Downcast};
55
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
@@ -411,11 +411,139 @@ impl<T: for<'a> Deserialize<'a> + Reflect> FromType<T> for ReflectDeserialize {
411411
}
412412
}
413413

414+
/// [`Reflect`] values are commonly used in situations where the actual types of values
415+
/// are not known at runtime. In such situations you might have access to a `*const ()` pointer
416+
/// that you know implements [`Reflect`], but have no way of turning it into a `&dyn Reflect`.
417+
///
418+
/// This is where [`ReflectFromPtr`] comes in, when creating a [`ReflectFromPtr`] for a given type `T: Reflect`.
419+
/// Internally, this saves a concrete function `*const T -> const dyn Reflect` which lets you create a trait object of [`Reflect`]
420+
/// from a pointer.
421+
///
422+
/// # Example
423+
/// ```rust
424+
/// use bevy_reflect::{TypeRegistry, Reflect, ReflectFromPtr};
425+
/// use bevy_ptr::Ptr;
426+
/// use std::ptr::NonNull;
427+
///
428+
/// #[derive(Reflect)]
429+
/// struct Reflected(String);
430+
///
431+
/// let mut type_registry = TypeRegistry::default();
432+
/// type_registry.register::<Reflected>();
433+
///
434+
/// let mut value = Reflected("Hello world!".to_string());
435+
/// let value = unsafe { Ptr::new(NonNull::from(&mut value).cast()) };
436+
///
437+
/// let reflect_data = type_registry.get(std::any::TypeId::of::<Reflected>()).unwrap();
438+
/// let reflect_from_ptr = reflect_data.data::<ReflectFromPtr>().unwrap();
439+
/// // SAFE: `value` is of type `Reflected`, which the `ReflectFromPtr` was created for
440+
/// let value = unsafe { reflect_from_ptr.as_reflect_ptr(value) };
441+
///
442+
/// assert_eq!(value.downcast_ref::<Reflected>().unwrap().0, "Hello world!");
443+
/// ```
444+
#[derive(Clone)]
445+
pub struct ReflectFromPtr {
446+
type_id: TypeId,
447+
to_reflect: for<'a> unsafe fn(Ptr<'a>) -> &'a dyn Reflect,
448+
to_reflect_mut: for<'a> unsafe fn(PtrMut<'a>) -> &'a mut dyn Reflect,
449+
}
450+
451+
impl ReflectFromPtr {
452+
/// Returns the [`TypeId`] that the [`ReflectFromPtr`] was constructed for
453+
pub fn type_id(&self) -> TypeId {
454+
self.type_id
455+
}
456+
457+
/// # Safety
458+
///
459+
/// `val` must be a pointer to value of the type that the [`ReflectFromPtr`] was constructed for.
460+
/// This can be verified by checking that the type id returned by [`ReflectFromPtr::type_id`] is the expected one.
461+
pub unsafe fn as_reflect_ptr<'a>(&self, val: Ptr<'a>) -> &'a dyn Reflect {
462+
(self.to_reflect)(val)
463+
}
464+
465+
/// # Safety
466+
///
467+
/// `val` must be a pointer to a value of the type that the [`ReflectFromPtr`] was constructed for
468+
/// This can be verified by checking that the type id returned by [`ReflectFromPtr::type_id`] is the expected one.
469+
pub unsafe fn as_reflect_ptr_mut<'a>(&self, val: PtrMut<'a>) -> &'a mut dyn Reflect {
470+
(self.to_reflect_mut)(val)
471+
}
472+
}
473+
474+
impl<T: Reflect> FromType<T> for ReflectFromPtr {
475+
fn from_type() -> Self {
476+
ReflectFromPtr {
477+
type_id: std::any::TypeId::of::<T>(),
478+
to_reflect: |ptr| {
479+
// SAFE: only called from `as_reflect`, where the `ptr` is guaranteed to be of type `T`,
480+
// and `as_reflect_ptr`, where the caller promises to call it with type `T`
481+
unsafe { ptr.deref::<T>() as &dyn Reflect }
482+
},
483+
to_reflect_mut: |ptr| {
484+
// SAFE: only called from `as_reflect_mut`, where the `ptr` is guaranteed to be of type `T`,
485+
// and `as_reflect_ptr_mut`, where the caller promises to call it with type `T`
486+
unsafe { ptr.deref_mut::<T>() as &mut dyn Reflect }
487+
},
488+
}
489+
}
490+
}
491+
414492
#[cfg(test)]
415493
mod test {
416-
use crate::TypeRegistration;
494+
use std::ptr::NonNull;
495+
496+
use crate::{GetTypeRegistration, ReflectFromPtr, TypeRegistration};
497+
use bevy_ptr::{Ptr, PtrMut};
417498
use bevy_utils::HashMap;
418499

500+
use crate as bevy_reflect;
501+
use crate::Reflect;
502+
503+
#[test]
504+
fn test_reflect_from_ptr() {
505+
#[derive(Reflect)]
506+
struct Foo {
507+
a: f32,
508+
}
509+
510+
let foo_registration = <Foo as GetTypeRegistration>::get_type_registration();
511+
let reflect_from_ptr = foo_registration.data::<ReflectFromPtr>().unwrap();
512+
513+
// not required in this situation because we no nobody messed with the TypeRegistry,
514+
// but in the general case somebody could have replaced the ReflectFromPtr with an
515+
// instance for another type, so then we'd need to check that the type is the expected one
516+
assert_eq!(reflect_from_ptr.type_id(), std::any::TypeId::of::<Foo>());
517+
518+
let mut value = Foo { a: 1.0 };
519+
{
520+
// SAFETY: lifetime doesn't outlive original value, access is unique
521+
let value = unsafe { PtrMut::new(NonNull::from(&mut value).cast()) };
522+
// SAFETY: reflect_from_ptr was constructed for the correct type
523+
let dyn_reflect = unsafe { reflect_from_ptr.as_reflect_ptr_mut(value) };
524+
match dyn_reflect.reflect_mut() {
525+
bevy_reflect::ReflectMut::Struct(strukt) => {
526+
strukt.field_mut("a").unwrap().apply(&2.0f32);
527+
}
528+
_ => panic!("invalid reflection"),
529+
}
530+
}
531+
532+
{
533+
// SAFETY: lifetime doesn't outlive original value
534+
let value = unsafe { Ptr::new(NonNull::from(&mut value).cast()) };
535+
// SAFETY: reflect_from_ptr was constructed for the correct type
536+
let dyn_reflect = unsafe { reflect_from_ptr.as_reflect_ptr(value) };
537+
match dyn_reflect.reflect_ref() {
538+
bevy_reflect::ReflectRef::Struct(strukt) => {
539+
let a = strukt.field("a").unwrap().downcast_ref::<f32>().unwrap();
540+
assert_eq!(*a, 2.0);
541+
}
542+
_ => panic!("invalid reflection"),
543+
}
544+
}
545+
}
546+
419547
#[test]
420548
fn test_property_type_registration() {
421549
assert_eq!(

0 commit comments

Comments
 (0)