Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
38 changes: 31 additions & 7 deletions crates/rspack_cacheable/src/context.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand All @@ -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 }
}

Expand All @@ -25,11 +43,11 @@ impl<'a> ContextGuard<'a> {
sharing.finish_sharing(CONTEXT_ADDR, self as *const _ as usize)
}

pub fn sharing_context<S: Sharing<Error> + ?Sized>(sharing: &'a mut S) -> Result<&'a dyn Any> {
pub fn sharing_guard<S: Sharing<Error> + ?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),
}
Expand All @@ -43,13 +61,19 @@ impl<'a> ContextGuard<'a> {
}
}

pub fn pooling_context<P: Pooling<Error> + ?Sized>(pooling: &'a mut P) -> Result<&'a dyn Any> {
pub fn pooling_guard<P: Pooling<Error> + ?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<T: 'static>(&'a self) -> Result<&'a T> {
(self.context as &dyn Any)
.downcast_ref()
.ok_or(Error::NoContext)
}
}
6 changes: 2 additions & 4 deletions crates/rspack_cacheable/src/deserialize.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::any::Any;

use rkyv::{
Archive, Deserialize, access,
api::{deserialize_using, high::HighValidator},
Expand All @@ -10,7 +8,7 @@ use rkyv::{
};

use crate::{
context::ContextGuard,
context::{CacheableContext, ContextGuard},
error::{Error, Result},
};

Expand All @@ -21,7 +19,7 @@ pub type Deserializer = Strategy<Pool, Error>;
///
/// This function implementation refers to rkyv::from_bytes and
/// add custom error and context support
pub fn from_bytes<T, C: Any>(bytes: &[u8], context: &C) -> Result<T>
pub fn from_bytes<T, C: CacheableContext>(bytes: &[u8], context: &C) -> Result<T>
where
T: Archive,
T::Archived: for<'a> CheckBytes<Validator<'a>> + Deserialize<T, Deserializer>,
Expand Down
5 changes: 3 additions & 2 deletions crates/rspack_cacheable/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,20 @@ pub mod __private {
#[cfg(not(feature = "noop"))]
pub use deserialize::from_bytes;
#[cfg(feature = "noop")]
pub fn from_bytes<T, C: std::any::Any>(_bytes: &[u8], _context: &C) -> Result<T> {
pub fn from_bytes<T, C: CacheableContext>(_bytes: &[u8], _context: &C) -> Result<T> {
let _ = deserialize::from_bytes::<u8, u8>;
panic!("Cannot use from_bytes when noop feature is enabled")
}

#[cfg(not(feature = "noop"))]
pub use serialize::to_bytes;
#[cfg(feature = "noop")]
pub fn to_bytes<T, C: std::any::Any>(_value: &T, _ctx: &C) -> Result<Vec<u8>> {
pub fn to_bytes<T, C: CacheableContext>(_value: &T, _ctx: &C) -> Result<Vec<u8>> {
let _ = serialize::to_bytes::<u8, u8>;
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;
Expand Down
6 changes: 2 additions & 4 deletions crates/rspack_cacheable/src/serialize.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::any::Any;

use rkyv::{
Serialize,
api::{high::HighSerializer, serialize_using},
Expand All @@ -12,7 +10,7 @@ use rkyv::{
};

use crate::{
context::ContextGuard,
context::{CacheableContext, ContextGuard},
error::{Error, Result},
};

Expand All @@ -22,7 +20,7 @@ pub type Serializer<'a> = HighSerializer<AlignedVec, ArenaHandle<'a>, Error>;
///
/// This function implementation refers to rkyv::to_bytes and
/// add custom error and context support
pub fn to_bytes<T, C: Any>(value: &T, ctx: &C) -> Result<Vec<u8>>
pub fn to_bytes<T, C: CacheableContext>(value: &T, ctx: &C) -> Result<Vec<u8>>
where
T: for<'a> Serialize<Serializer<'a>>,
{
Expand Down
14 changes: 6 additions & 8 deletions crates/rspack_cacheable/src/with/as.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::any::Any;

use rkyv::{
Archive, Archived, Deserialize, Place, Resolver, Serialize,
de::Pooling,
Expand All @@ -11,10 +9,10 @@ use rkyv::{
use crate::{Error, Result, context::ContextGuard};

pub trait AsConverter<T> {
fn serialize(data: &T, ctx: &dyn Any) -> Result<Self>
fn serialize(data: &T, guard: &ContextGuard) -> Result<Self>
where
Self: Sized;
fn deserialize(self, ctx: &dyn Any) -> Result<T>;
fn deserialize(self, guard: &ContextGuard) -> Result<T>;
}

pub struct As<A> {
Expand Down Expand Up @@ -47,8 +45,8 @@ where
{
#[inline]
fn serialize_with(field: &T, serializer: &mut S) -> Result<Self::Resolver> {
let ctx = ContextGuard::sharing_context(serializer)?;
let value = <A as AsConverter<T>>::serialize(field, ctx)?;
let guard = ContextGuard::sharing_guard(serializer)?;
let value = <A as AsConverter<T>>::serialize(field, guard)?;
Ok(AsResolver {
resolver: value.serialize(serializer)?,
value,
Expand All @@ -65,7 +63,7 @@ where
#[inline]
fn deserialize_with(field: &Archived<A>, de: &mut D) -> Result<T> {
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)
}
}
16 changes: 7 additions & 9 deletions crates/rspack_cacheable/src/with/custom.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::any::Any;

use rkyv::{
Archive, Deserialize, Place, Serialize,
de::Pooling,
Expand All @@ -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<Self::Target>;
fn deserialize(data: Self::Target, ctx: &dyn Any) -> Result<Self>
fn serialize(&self, guard: &ContextGuard) -> Result<Self::Target>;
fn deserialize(data: Self::Target, guard: &ContextGuard) -> Result<Self>
where
Self: Sized;
}
Expand Down Expand Up @@ -57,8 +55,8 @@ where
{
#[inline]
fn serialize_with(field: &T, serializer: &mut S) -> Result<Self::Resolver> {
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,
Expand All @@ -76,7 +74,7 @@ where
#[inline]
fn deserialize_with(field: &ArchivedDataBox<T::Target>, de: &mut D) -> Result<T> {
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)
}
}
32 changes: 21 additions & 11 deletions crates/rspack_cacheable_test/tests/context.rs
Original file line number Diff line number Diff line change
@@ -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},
};

Expand All @@ -15,17 +16,21 @@ struct Context {
option: Arc<CompilerOptions>,
}

impl CacheableContext for Context {
fn project_root(&self) -> Option<&std::path::Path> {
None
}
}

#[cacheable]
struct FromContext;

impl AsConverter<Arc<CompilerOptions>> for FromContext {
fn serialize(_data: &Arc<CompilerOptions>, _ctx: &dyn Any) -> Result<Self, Error> {
fn serialize(_data: &Arc<CompilerOptions>, _guard: &ContextGuard) -> Result<Self> {
Ok(FromContext)
}
fn deserialize(self, ctx: &dyn Any) -> Result<Arc<CompilerOptions>, Error> {
let Some(ctx) = ctx.downcast_ref::<Context>() else {
return Err(Error::MessageError("context not match"));
};
fn deserialize(self, guard: &ContextGuard) -> Result<Arc<CompilerOptions>> {
let ctx = guard.downcast_context::<Context>()?;
Ok(ctx.option.clone())
}
}
Expand All @@ -50,10 +55,15 @@ fn test_context() {

let bytes = to_bytes(&module, &()).unwrap();

assert!(matches!(
from_bytes::<Module, ()>(&bytes, &()),
Err(Error::MessageError("context not match"))
));
let result = from_bytes::<Module, ()>(&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);
}
13 changes: 12 additions & 1 deletion crates/rspack_cacheable_test/tests/macro/cacheable_dyn.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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<T = ()>: Send + Sync
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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<T = ()>: rspack_cacheable::r#dyn::SerializeDyn {
fn color(&self) -> &str;
Expand Down
8 changes: 4 additions & 4 deletions crates/rspack_cacheable_test/tests/with/as.rs
Original file line number Diff line number Diff line change
@@ -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},
};

Expand All @@ -12,10 +12,10 @@ struct UnCacheableData(PathBuf);
struct CacheableData(String);

impl AsConverter<UnCacheableData> for CacheableData {
fn serialize(data: &UnCacheableData, _ctx: &dyn Any) -> Result<Self, Error> {
fn serialize(data: &UnCacheableData, _guard: &ContextGuard) -> Result<Self> {
Ok(Self(data.0.to_string_lossy().to_string()))
}
fn deserialize(self, _ctx: &dyn Any) -> Result<UnCacheableData, Error> {
fn deserialize(self, _guard: &ContextGuard) -> Result<UnCacheableData> {
Ok(UnCacheableData(PathBuf::from(&self.0)))
}
}
Expand Down
Loading
Loading