diff --git a/crates/rspack_cacheable/src/context.rs b/crates/rspack_cacheable/src/context.rs index ec9d235ff64c..8911b256d910 100644 --- a/crates/rspack_cacheable/src/context.rs +++ b/crates/rspack_cacheable/src/context.rs @@ -1,4 +1,4 @@ -use std::{any::Any, ptr::NonNull}; +use std::{any::Any, ops::Deref, path::Path, ptr::NonNull}; use rkyv::{ de::{ErasedPtr, Pooling, PoolingState}, @@ -10,13 +10,31 @@ use crate::error::{Error, Result}; const CONTEXT_ADDR: usize = 0; unsafe fn default_drop(_: ErasedPtr) {} +pub trait CacheableContext: Any { + fn project_root(&self) -> Option<&Path>; +} + +// Implement for unit type for convenience in tests and simple cases +impl CacheableContext for () { + fn project_root(&self) -> Option<&Path> { + None + } +} + /// A context wrapper that provides shared context methods pub struct ContextGuard<'a> { - context: &'a dyn Any, + context: &'a dyn CacheableContext, +} + +impl<'a> Deref for ContextGuard<'a> { + type Target = dyn CacheableContext; + fn deref(&self) -> &Self::Target { + self.context + } } impl<'a> ContextGuard<'a> { - pub fn new(context: &'a dyn Any) -> Self { + pub fn new(context: &'a dyn CacheableContext) -> Self { Self { context } } @@ -25,11 +43,11 @@ impl<'a> ContextGuard<'a> { sharing.finish_sharing(CONTEXT_ADDR, self as *const _ as usize) } - pub fn sharing_context + ?Sized>(sharing: &'a mut S) -> Result<&'a dyn Any> { + pub fn sharing_guard + ?Sized>(sharing: &'a mut S) -> Result<&'a Self> { match sharing.start_sharing(CONTEXT_ADDR) { SharingState::Finished(addr) => { let guard: &Self = unsafe { &*(addr as *const Self) }; - Ok(guard.context) + Ok(guard) } _ => Err(Error::NoContext), } @@ -43,13 +61,19 @@ impl<'a> ContextGuard<'a> { } } - pub fn pooling_context + ?Sized>(pooling: &'a mut P) -> Result<&'a dyn Any> { + pub fn pooling_guard + ?Sized>(pooling: &'a mut P) -> Result<&'a Self> { match pooling.start_pooling(CONTEXT_ADDR) { PoolingState::Finished(ptr) => { let guard: &Self = unsafe { &*(ptr.data_address() as *const Self) }; - Ok(guard.context) + Ok(guard) } _ => Err(Error::NoContext), } } + + pub fn downcast_context(&'a self) -> Result<&'a T> { + (self.context as &dyn Any) + .downcast_ref() + .ok_or(Error::NoContext) + } } diff --git a/crates/rspack_cacheable/src/deserialize.rs b/crates/rspack_cacheable/src/deserialize.rs index 54777f6be193..1815d3a4c65e 100644 --- a/crates/rspack_cacheable/src/deserialize.rs +++ b/crates/rspack_cacheable/src/deserialize.rs @@ -1,5 +1,3 @@ -use std::any::Any; - use rkyv::{ Archive, Deserialize, access, api::{deserialize_using, high::HighValidator}, @@ -10,7 +8,7 @@ use rkyv::{ }; use crate::{ - context::ContextGuard, + context::{CacheableContext, ContextGuard}, error::{Error, Result}, }; @@ -21,7 +19,7 @@ pub type Deserializer = Strategy; /// /// This function implementation refers to rkyv::from_bytes and /// add custom error and context support -pub fn from_bytes(bytes: &[u8], context: &C) -> Result +pub fn from_bytes(bytes: &[u8], context: &C) -> Result where T: Archive, T::Archived: for<'a> CheckBytes> + Deserialize, diff --git a/crates/rspack_cacheable/src/lib.rs b/crates/rspack_cacheable/src/lib.rs index 7ba5c296366e..71357875aab8 100644 --- a/crates/rspack_cacheable/src/lib.rs +++ b/crates/rspack_cacheable/src/lib.rs @@ -29,7 +29,7 @@ pub mod __private { #[cfg(not(feature = "noop"))] pub use deserialize::from_bytes; #[cfg(feature = "noop")] -pub fn from_bytes(_bytes: &[u8], _context: &C) -> Result { +pub fn from_bytes(_bytes: &[u8], _context: &C) -> Result { let _ = deserialize::from_bytes::; panic!("Cannot use from_bytes when noop feature is enabled") } @@ -37,11 +37,12 @@ pub fn from_bytes(_bytes: &[u8], _context: &C) -> Result #[cfg(not(feature = "noop"))] pub use serialize::to_bytes; #[cfg(feature = "noop")] -pub fn to_bytes(_value: &T, _ctx: &C) -> Result> { +pub fn to_bytes(_value: &T, _ctx: &C) -> Result> { let _ = serialize::to_bytes::; panic!("Cannot use to_bytes when noop feature is enabled") } +pub use context::{CacheableContext, ContextGuard}; pub use deserialize::{Deserializer, Validator}; pub use error::{Error, Result}; pub use serialize::Serializer; diff --git a/crates/rspack_cacheable/src/serialize.rs b/crates/rspack_cacheable/src/serialize.rs index 987f223b228f..f5ec772aa7b5 100644 --- a/crates/rspack_cacheable/src/serialize.rs +++ b/crates/rspack_cacheable/src/serialize.rs @@ -1,5 +1,3 @@ -use std::any::Any; - use rkyv::{ Serialize, api::{high::HighSerializer, serialize_using}, @@ -12,7 +10,7 @@ use rkyv::{ }; use crate::{ - context::ContextGuard, + context::{CacheableContext, ContextGuard}, error::{Error, Result}, }; @@ -22,7 +20,7 @@ pub type Serializer<'a> = HighSerializer, Error>; /// /// This function implementation refers to rkyv::to_bytes and /// add custom error and context support -pub fn to_bytes(value: &T, ctx: &C) -> Result> +pub fn to_bytes(value: &T, ctx: &C) -> Result> where T: for<'a> Serialize>, { diff --git a/crates/rspack_cacheable/src/with/as.rs b/crates/rspack_cacheable/src/with/as.rs index 31711cdb15ce..6de22f98ea63 100644 --- a/crates/rspack_cacheable/src/with/as.rs +++ b/crates/rspack_cacheable/src/with/as.rs @@ -1,5 +1,3 @@ -use std::any::Any; - use rkyv::{ Archive, Archived, Deserialize, Place, Resolver, Serialize, de::Pooling, @@ -11,10 +9,10 @@ use rkyv::{ use crate::{Error, Result, context::ContextGuard}; pub trait AsConverter { - fn serialize(data: &T, ctx: &dyn Any) -> Result + fn serialize(data: &T, guard: &ContextGuard) -> Result where Self: Sized; - fn deserialize(self, ctx: &dyn Any) -> Result; + fn deserialize(self, guard: &ContextGuard) -> Result; } pub struct As { @@ -47,8 +45,8 @@ where { #[inline] fn serialize_with(field: &T, serializer: &mut S) -> Result { - let ctx = ContextGuard::sharing_context(serializer)?; - let value = >::serialize(field, ctx)?; + let guard = ContextGuard::sharing_guard(serializer)?; + let value = >::serialize(field, guard)?; Ok(AsResolver { resolver: value.serialize(serializer)?, value, @@ -65,7 +63,7 @@ where #[inline] fn deserialize_with(field: &Archived, de: &mut D) -> Result { let field = A::Archived::deserialize(field, de)?; - let ctx = ContextGuard::pooling_context(de)?; - field.deserialize(ctx) + let guard = ContextGuard::pooling_guard(de)?; + field.deserialize(guard) } } diff --git a/crates/rspack_cacheable/src/with/custom.rs b/crates/rspack_cacheable/src/with/custom.rs index 7c298b351a4d..8d871bc3a1c7 100644 --- a/crates/rspack_cacheable/src/with/custom.rs +++ b/crates/rspack_cacheable/src/with/custom.rs @@ -1,5 +1,3 @@ -use std::any::Any; - use rkyv::{ Archive, Deserialize, Place, Serialize, de::Pooling, @@ -8,15 +6,15 @@ use rkyv::{ with::{ArchiveWith, DeserializeWith, SerializeWith}, }; -use crate::{Error, Result, cacheable, context::ContextGuard}; +use crate::{ContextGuard, Error, Result, cacheable}; /// A trait for writing custom serialization and deserialization. /// /// `#[cacheable(with=Custom)]` will use this trait. pub trait CustomConverter { type Target: Archive; - fn serialize(&self, ctx: &dyn Any) -> Result; - fn deserialize(data: Self::Target, ctx: &dyn Any) -> Result + fn serialize(&self, guard: &ContextGuard) -> Result; + fn deserialize(data: Self::Target, guard: &ContextGuard) -> Result where Self: Sized; } @@ -57,8 +55,8 @@ where { #[inline] fn serialize_with(field: &T, serializer: &mut S) -> Result { - let ctx = ContextGuard::sharing_context(serializer)?; - let value = DataBox(T::serialize(field, ctx)?); + let guard = ContextGuard::sharing_guard(serializer)?; + let value = DataBox(T::serialize(field, guard)?); Ok(CustomResolver { resolver: value.serialize(serializer)?, value, @@ -76,7 +74,7 @@ where #[inline] fn deserialize_with(field: &ArchivedDataBox, de: &mut D) -> Result { let value = field.deserialize(de)?; - let ctx = ContextGuard::pooling_context(de)?; - T::deserialize(value.0, ctx) + let guard = ContextGuard::pooling_guard(de)?; + T::deserialize(value.0, guard) } } diff --git a/crates/rspack_cacheable_test/tests/context.rs b/crates/rspack_cacheable_test/tests/context.rs index caab02399c8a..b145f5ceed06 100644 --- a/crates/rspack_cacheable_test/tests/context.rs +++ b/crates/rspack_cacheable_test/tests/context.rs @@ -1,7 +1,8 @@ -use std::{any::Any, sync::Arc}; +use std::sync::Arc; use rspack_cacheable::{ - Error, enable_cacheable as cacheable, from_bytes, to_bytes, + CacheableContext, ContextGuard, Error, Result, enable_cacheable as cacheable, from_bytes, + to_bytes, with::{As, AsConverter}, }; @@ -15,17 +16,21 @@ struct Context { option: Arc, } +impl CacheableContext for Context { + fn project_root(&self) -> Option<&std::path::Path> { + None + } +} + #[cacheable] struct FromContext; impl AsConverter> for FromContext { - fn serialize(_data: &Arc, _ctx: &dyn Any) -> Result { + fn serialize(_data: &Arc, _guard: &ContextGuard) -> Result { Ok(FromContext) } - fn deserialize(self, ctx: &dyn Any) -> Result, Error> { - let Some(ctx) = ctx.downcast_ref::() else { - return Err(Error::MessageError("context not match")); - }; + fn deserialize(self, guard: &ContextGuard) -> Result> { + let ctx = guard.downcast_context::()?; Ok(ctx.option.clone()) } } @@ -50,10 +55,15 @@ fn test_context() { let bytes = to_bytes(&module, &()).unwrap(); - assert!(matches!( - from_bytes::(&bytes, &()), - Err(Error::MessageError("context not match")) - )); + let result = from_bytes::(&bytes, &()); + assert!(result.is_err(), "should fail when using wrong context"); + if let Err(e) = result { + assert!( + matches!(e, Error::NoContext), + "expected NoContext but got: {:?}", + e + ); + } let new_module: Module = from_bytes(&bytes, &context).unwrap(); assert_eq!(module, new_module); } diff --git a/crates/rspack_cacheable_test/tests/macro/cacheable_dyn.rs b/crates/rspack_cacheable_test/tests/macro/cacheable_dyn.rs index 03fb5af3446c..350f9722ce73 100644 --- a/crates/rspack_cacheable_test/tests/macro/cacheable_dyn.rs +++ b/crates/rspack_cacheable_test/tests/macro/cacheable_dyn.rs @@ -1,11 +1,17 @@ use rspack_cacheable::{ - enable_cacheable as cacheable, enable_cacheable_dyn as cacheable_dyn, from_bytes, to_bytes, + CacheableContext, enable_cacheable as cacheable, enable_cacheable_dyn as cacheable_dyn, + from_bytes, to_bytes, }; #[test] #[cfg_attr(miri, ignore)] fn test_cacheable_dyn_macro() { struct Context; + impl CacheableContext for Context { + fn project_root(&self) -> Option<&std::path::Path> { + None + } + } #[cacheable_dyn] trait Animal { @@ -77,6 +83,11 @@ fn test_cacheable_dyn_macro() { #[cfg_attr(miri, ignore)] fn test_cacheable_dyn_macro_with_generics() { struct Context; + impl CacheableContext for Context { + fn project_root(&self) -> Option<&std::path::Path> { + None + } + } #[cacheable_dyn] trait Animal: Send + Sync diff --git a/crates/rspack_cacheable_test/tests/macro/manual_cacheable_dyn.rs b/crates/rspack_cacheable_test/tests/macro/manual_cacheable_dyn.rs index 9ed7f9e24cb0..4779ddf4eab1 100644 --- a/crates/rspack_cacheable_test/tests/macro/manual_cacheable_dyn.rs +++ b/crates/rspack_cacheable_test/tests/macro/manual_cacheable_dyn.rs @@ -1,9 +1,16 @@ -use rspack_cacheable::{r#dyn::VTablePtr, enable_cacheable as cacheable, from_bytes, to_bytes}; +use rspack_cacheable::{ + CacheableContext, r#dyn::VTablePtr, enable_cacheable as cacheable, from_bytes, to_bytes, +}; #[test] #[cfg_attr(miri, ignore)] fn test_manual_cacheable_dyn_macro() { struct Context; + impl CacheableContext for Context { + fn project_root(&self) -> Option<&std::path::Path> { + None + } + } trait Animal: rspack_cacheable::r#dyn::SerializeDyn { fn color(&self) -> &str; diff --git a/crates/rspack_cacheable_test/tests/macro/manual_cacheable_dyn_with_generics.rs b/crates/rspack_cacheable_test/tests/macro/manual_cacheable_dyn_with_generics.rs index 5aece11115b9..ed5182e53359 100644 --- a/crates/rspack_cacheable_test/tests/macro/manual_cacheable_dyn_with_generics.rs +++ b/crates/rspack_cacheable_test/tests/macro/manual_cacheable_dyn_with_generics.rs @@ -1,9 +1,16 @@ -use rspack_cacheable::{r#dyn::VTablePtr, enable_cacheable as cacheable, from_bytes, to_bytes}; +use rspack_cacheable::{ + CacheableContext, r#dyn::VTablePtr, enable_cacheable as cacheable, from_bytes, to_bytes, +}; #[test] #[cfg_attr(miri, ignore)] fn test_manual_cacheable_dyn_macro_with_generics() { struct Context; + impl CacheableContext for Context { + fn project_root(&self) -> Option<&std::path::Path> { + None + } + } trait Animal: rspack_cacheable::r#dyn::SerializeDyn { fn color(&self) -> &str; diff --git a/crates/rspack_cacheable_test/tests/with/as.rs b/crates/rspack_cacheable_test/tests/with/as.rs index cb97ffd9add6..eff0b83eac73 100644 --- a/crates/rspack_cacheable_test/tests/with/as.rs +++ b/crates/rspack_cacheable_test/tests/with/as.rs @@ -1,7 +1,7 @@ -use std::{any::Any, path::PathBuf}; +use std::path::PathBuf; use rspack_cacheable::{ - Error, enable_cacheable as cacheable, + ContextGuard, Result, enable_cacheable as cacheable, with::{As, AsConverter}, }; @@ -12,10 +12,10 @@ struct UnCacheableData(PathBuf); struct CacheableData(String); impl AsConverter for CacheableData { - fn serialize(data: &UnCacheableData, _ctx: &dyn Any) -> Result { + fn serialize(data: &UnCacheableData, _guard: &ContextGuard) -> Result { Ok(Self(data.0.to_string_lossy().to_string())) } - fn deserialize(self, _ctx: &dyn Any) -> Result { + fn deserialize(self, _guard: &ContextGuard) -> Result { Ok(UnCacheableData(PathBuf::from(&self.0))) } } diff --git a/crates/rspack_cacheable_test/tests/with/custom.rs b/crates/rspack_cacheable_test/tests/with/custom.rs index b47dea864d72..10f35a165000 100644 --- a/crates/rspack_cacheable_test/tests/with/custom.rs +++ b/crates/rspack_cacheable_test/tests/with/custom.rs @@ -1,7 +1,7 @@ -use std::{any::Any, path::PathBuf}; +use std::path::PathBuf; use rspack_cacheable::{ - Error, enable_cacheable as cacheable, + ContextGuard, Result, enable_cacheable as cacheable, with::{Custom, CustomConverter}, }; @@ -11,10 +11,10 @@ struct Data(PathBuf); impl CustomConverter for Data { type Target = String; - fn serialize(&self, _ctx: &dyn Any) -> Result { + fn serialize(&self, _guard: &ContextGuard) -> Result { Ok(self.0.to_string_lossy().to_string()) } - fn deserialize(data: Self::Target, _ctx: &dyn Any) -> Result { + fn deserialize(data: Self::Target, _guard: &ContextGuard) -> Result { Ok(Data(PathBuf::from(&data))) } } diff --git a/crates/rspack_core/src/cache/persistent/cacheable_context.rs b/crates/rspack_core/src/cache/persistent/cacheable_context.rs index 96838b089dc1..b2e919abb23d 100644 --- a/crates/rspack_core/src/cache/persistent/cacheable_context.rs +++ b/crates/rspack_core/src/cache/persistent/cacheable_context.rs @@ -1,2 +1,8 @@ #[derive(Debug)] pub struct CacheableContext; + +impl rspack_cacheable::CacheableContext for CacheableContext { + fn project_root(&self) -> Option<&std::path::Path> { + None + } +} diff --git a/crates/rspack_core/src/module_profile.rs b/crates/rspack_core/src/module_profile.rs index 46ff39ae2714..71da68128ffd 100644 --- a/crates/rspack_core/src/module_profile.rs +++ b/crates/rspack_core/src/module_profile.rs @@ -1,7 +1,7 @@ use std::time::Instant; use rspack_cacheable::{ - cacheable, + ContextGuard, Result, cacheable, with::{Custom, CustomConverter}, }; @@ -17,13 +17,10 @@ enum ProfileState { impl CustomConverter for ProfileState { type Target = Option; - fn serialize(&self, _ctx: &dyn std::any::Any) -> Result { + fn serialize(&self, _guard: &ContextGuard) -> Result { Ok(self.duration()) } - fn deserialize( - data: Self::Target, - _ctx: &dyn std::any::Any, - ) -> Result { + fn deserialize(data: Self::Target, _guard: &ContextGuard) -> Result { if let Some(time) = data { Ok(ProfileState::Finish(time)) } else {