From 5fb3eb5cb975b41262c1355714177ce631ae534c Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Wed, 13 Sep 2023 11:29:19 -0700 Subject: [PATCH] Manual "Reflect Value" AssetPath impl to fix dynamic linking (#9752) # Objective Fix #9747 ## Solution Linkers don't like what we're doing with CowArc (I'm guessing it has something to do with `?Sized`). Weirdly the `Reflect` derive on `AssetPath` doesn't fail, despite `CowArc` not implementing `Reflect`. To resolve this, we manually implement "reflect value" for `AssetPath<'static>`. It sadly cannot use `impl_reflect_value` because that macro doesn't support static lifetimes. --------- Co-authored-by: Martin Dickopp --- crates/bevy_asset/src/path.rs | 143 ++++++++++++++++++++++++++++++- crates/bevy_utils/src/cow_arc.rs | 12 +++ 2 files changed, 151 insertions(+), 4 deletions(-) diff --git a/crates/bevy_asset/src/path.rs b/crates/bevy_asset/src/path.rs index db64a6481d..efd1204114 100644 --- a/crates/bevy_asset/src/path.rs +++ b/crates/bevy_asset/src/path.rs @@ -1,9 +1,14 @@ -use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize}; +use bevy_reflect::{ + std_traits::ReflectDefault, utility::NonGenericTypeInfoCell, FromReflect, FromType, + GetTypeRegistration, Reflect, ReflectDeserialize, ReflectFromPtr, ReflectFromReflect, + ReflectMut, ReflectOwned, ReflectRef, ReflectSerialize, TypeInfo, TypePath, TypeRegistration, + Typed, ValueInfo, +}; use bevy_utils::CowArc; use serde::{de::Visitor, Deserialize, Serialize}; use std::{ fmt::{Debug, Display}, - hash::Hash, + hash::{Hash, Hasher}, ops::Deref, path::{Path, PathBuf}, }; @@ -40,8 +45,7 @@ use std::{ /// This means that the common case of `asset_server.load("my_scene.scn")` when it creates and /// clones internal owned [`AssetPaths`](AssetPath). /// This also means that you should use [`AssetPath::new`] in cases where `&str` is the explicit type. -#[derive(Eq, PartialEq, Hash, Clone, Reflect)] -#[reflect(Debug, PartialEq, Hash, Serialize, Deserialize)] +#[derive(Eq, PartialEq, Hash, Clone, Default)] pub struct AssetPath<'a> { path: CowArc<'a, Path>, label: Option>, @@ -267,3 +271,134 @@ impl<'de> Visitor<'de> for AssetPathVisitor { Ok(AssetPath::from(v)) } } + +// NOTE: We manually implement "reflect value" because deriving Reflect on `AssetPath` breaks dynamic linking +// See https://github.com/bevyengine/bevy/issues/9747 +// NOTE: This could use `impl_reflect_value` if it supported static lifetimes. + +impl GetTypeRegistration for AssetPath<'static> { + fn get_type_registration() -> TypeRegistration { + let mut registration = TypeRegistration::of::(); + registration.insert::(FromType::::from_type()); + registration.insert::(FromType::::from_type()); + registration.insert::(FromType::::from_type()); + registration.insert::(FromType::::from_type()); + registration.insert::(FromType::::from_type()); + registration + } +} + +impl TypePath for AssetPath<'static> { + fn type_path() -> &'static str { + "bevy_asset::path::AssetPath<'static>" + } + fn short_type_path() -> &'static str { + "AssetPath<'static>" + } + fn type_ident() -> Option<&'static str> { + Some("AssetPath<'static>") + } + fn crate_name() -> Option<&'static str> { + Option::None + } + fn module_path() -> Option<&'static str> { + Option::None + } +} +impl Typed for AssetPath<'static> { + fn type_info() -> &'static TypeInfo { + static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new(); + CELL.get_or_set(|| { + let info = ValueInfo::new::(); + TypeInfo::Value(info) + }) + } +} +impl Reflect for AssetPath<'static> { + #[inline] + fn type_name(&self) -> &str { + ::core::any::type_name::() + } + #[inline] + fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { + Some(::type_info()) + } + #[inline] + fn into_any(self: Box) -> Box { + self + } + #[inline] + fn as_any(&self) -> &dyn ::core::any::Any { + self + } + #[inline] + fn as_any_mut(&mut self) -> &mut dyn ::core::any::Any { + self + } + #[inline] + fn into_reflect(self: Box) -> Box { + self + } + #[inline] + fn as_reflect(&self) -> &dyn Reflect { + self + } + #[inline] + fn as_reflect_mut(&mut self) -> &mut dyn Reflect { + self + } + #[inline] + fn clone_value(&self) -> Box { + Box::new(self.clone()) + } + #[inline] + fn apply(&mut self, value: &dyn Reflect) { + let value = Reflect::as_any(value); + if let Some(value) = value.downcast_ref::() { + *self = value.clone(); + } else { + panic!("Value is not {}.", std::any::type_name::()); + } + } + #[inline] + fn set( + &mut self, + value: Box, + ) -> Result<(), Box> { + *self = ::take(value)?; + Ok(()) + } + fn reflect_ref(&self) -> ReflectRef { + ReflectRef::Value(self) + } + fn reflect_mut(&mut self) -> ReflectMut { + ReflectMut::Value(self) + } + fn reflect_owned(self: Box) -> ReflectOwned { + ReflectOwned::Value(self) + } + fn reflect_hash(&self) -> Option { + let mut hasher = bevy_reflect::utility::reflect_hasher(); + Hash::hash(&::core::any::Any::type_id(self), &mut hasher); + Hash::hash(self, &mut hasher); + Some(Hasher::finish(&hasher)) + } + fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option { + let value = ::as_any(value); + if let Some(value) = ::downcast_ref::(value) { + Some(::core::cmp::PartialEq::eq(self, value)) + } else { + Some(false) + } + } + fn debug(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + ::core::fmt::Debug::fmt(self, f) + } +} +impl FromReflect for AssetPath<'static> { + fn from_reflect(reflect: &dyn Reflect) -> Option { + Some(Clone::clone(::downcast_ref::< + AssetPath<'static>, + >(::as_any(reflect))?)) + } +} diff --git a/crates/bevy_utils/src/cow_arc.rs b/crates/bevy_utils/src/cow_arc.rs index 5e1259a82b..31a204863d 100644 --- a/crates/bevy_utils/src/cow_arc.rs +++ b/crates/bevy_utils/src/cow_arc.rs @@ -101,6 +101,18 @@ impl<'a, T: PartialOrd + ?Sized> PartialOrd for CowArc<'a, T> { } } +impl Default for CowArc<'static, str> { + fn default() -> Self { + CowArc::Static(Default::default()) + } +} + +impl Default for CowArc<'static, Path> { + fn default() -> Self { + CowArc::Static(Path::new("")) + } +} + impl<'a, T: Ord + ?Sized> Ord for CowArc<'a, T> { #[inline] fn cmp(&self, other: &Self) -> std::cmp::Ordering {