Skip to content
Draft
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
11 changes: 11 additions & 0 deletions crates/rspack_cacheable/src/with/as_inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@ impl<T> AsInnerConverter for std::sync::Arc<T> {
}
}

// for Box
impl<T> AsInnerConverter for Box<T> {
type Inner = T;
fn to_inner(&self) -> &Self::Inner {
self.as_ref()
}
fn from_inner(data: Self::Inner) -> Self {
Self::new(data)
}
}

// for OnceCell
// rkyv::with::Map
#[repr(u8)]
Expand Down
18 changes: 18 additions & 0 deletions crates/rspack_cacheable/src/with/as_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,24 @@ where
}
}

// for hashlink::LinkedHashSet
impl<T, S> AsVecConverter for hashlink::LinkedHashSet<T, S>
where
T: std::cmp::Eq + std::hash::Hash,
S: core::hash::BuildHasher + Default,
{
type Item = T;
fn len(&self) -> usize {
self.len()
}
fn iter(&self) -> impl Iterator<Item = &Self::Item> {
self.iter()
}
fn from(data: impl Iterator<Item = Result<Self::Item>>) -> Result<Self> {
data.collect::<Result<hashlink::LinkedHashSet<T, S>>>()
}
}

// for indexmap::IndexSet
impl<T, S> AsVecConverter for indexmap::IndexSet<T, S>
where
Expand Down
137 changes: 115 additions & 22 deletions crates/rspack_core/src/artifacts/code_generation_results.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
use std::{
collections::hash_map::Entry,
ops::{Deref, DerefMut},
sync::atomic::AtomicU32,
};
use std::{collections::hash_map::Entry, fmt::Debug};

use anymap::CloneAny;
use dyn_clone::{DynClone, clone_trait_object};
use rspack_cacheable::{
cacheable, cacheable_dyn,
with::{AsCacheable, AsInner, AsMap, AsOption, AsPreset, AsVec, Unsupported},
};
use rspack_collections::IdentifierMap;
use rspack_hash::{HashDigest, HashFunction, HashSalt, RspackHash, RspackHashDigest, RspackHasher};
use rspack_sources::BoxSource;
use rspack_util::atom::Atom;
use rspack_tasks::fetch_new_code_generation_result_id;
use rspack_util::{
atom::Atom,
ext::{AsAny, IntoAny},
};
use rustc_hash::{FxHashMap as HashMap, FxHashSet};
use serde::Serialize;

Expand All @@ -17,6 +21,7 @@ use crate::{
RuntimeGlobals, RuntimeSpec, RuntimeSpecMap, SourceType, incremental::IncrementalPasses,
};

#[cacheable]
#[derive(Clone, Debug)]
pub struct CodeGenerationDataUrl {
inner: String,
Expand All @@ -33,12 +38,15 @@ impl CodeGenerationDataUrl {
}

// For performance, mark the js modules containing AUTO_PUBLIC_PATH_PLACEHOLDER
#[cacheable]
#[derive(Clone, Debug)]
pub struct CodeGenerationPublicPathAutoReplace(pub bool);

#[cacheable]
#[derive(Clone, Debug)]
pub struct URLStaticMode;

#[cacheable]
#[derive(Clone, Debug)]
pub struct CodeGenerationDataFilename {
filename: String,
Expand All @@ -62,6 +70,7 @@ impl CodeGenerationDataFilename {
}
}

#[cacheable]
#[derive(Clone, Debug)]
pub struct CodeGenerationDataAssetInfo {
inner: AssetInfo,
Expand All @@ -77,8 +86,10 @@ impl CodeGenerationDataAssetInfo {
}
}

#[cacheable]
#[derive(Clone, Debug)]
pub struct CodeGenerationDataTopLevelDeclarations {
#[cacheable(with=AsVec<AsPreset>)]
inner: FxHashSet<Atom>,
}

Expand All @@ -92,6 +103,7 @@ impl CodeGenerationDataTopLevelDeclarations {
}
}

#[cacheable]
#[derive(Clone, Debug)]
pub struct CodeGenerationExportsFinalNames {
inner: HashMap<String, String>,
Expand All @@ -107,34 +119,116 @@ impl CodeGenerationExportsFinalNames {
}
}

#[cacheable_dyn]
pub trait CodeGenerationDataItem: Debug + DynClone + AsAny + IntoAny + Send + Sync {}

clone_trait_object!(CodeGenerationDataItem);

#[cacheable]
#[derive(Debug, Default, Clone)]
pub struct CodeGenerationData {
inner: anymap::Map<dyn CloneAny + Send + Sync>,
pub struct CodeGenerationDataChunkInitFragments {
inner: ChunkInitFragments,
}

impl Deref for CodeGenerationData {
type Target = anymap::Map<dyn CloneAny + Send + Sync>;

fn deref(&self) -> &Self::Target {
impl CodeGenerationDataChunkInitFragments {
pub fn inner(&self) -> &ChunkInitFragments {
&self.inner
}
}

impl DerefMut for CodeGenerationData {
fn deref_mut(&mut self) -> &mut Self::Target {
pub fn inner_mut(&mut self) -> &mut ChunkInitFragments {
&mut self.inner
}
}

impl From<ChunkInitFragments> for CodeGenerationDataChunkInitFragments {
fn from(inner: ChunkInitFragments) -> Self {
Self { inner }
}
}

#[cacheable_dyn]
impl CodeGenerationDataItem for CodeGenerationDataUrl {}

#[cacheable_dyn]
impl CodeGenerationDataItem for CodeGenerationPublicPathAutoReplace {}

#[cacheable_dyn]
impl CodeGenerationDataItem for URLStaticMode {}

#[cacheable_dyn]
impl CodeGenerationDataItem for CodeGenerationDataFilename {}

#[cacheable_dyn]
impl CodeGenerationDataItem for CodeGenerationDataAssetInfo {}

#[cacheable_dyn]
impl CodeGenerationDataItem for CodeGenerationDataTopLevelDeclarations {}

#[cacheable_dyn]
impl CodeGenerationDataItem for CodeGenerationExportsFinalNames {}

#[cacheable_dyn]
impl CodeGenerationDataItem for CodeGenerationDataChunkInitFragments {}

#[cacheable]
#[derive(Debug, Default, Clone)]
pub struct CodeGenerationData {
inner: Vec<Box<dyn CodeGenerationDataItem>>,
}

impl CodeGenerationData {
pub fn insert<T: CodeGenerationDataItem + 'static>(&mut self, item: T) -> Option<T> {
if let Some(index) = self
.inner
.iter()
.position(|item| item.as_ref().as_any().is::<T>())
{
let old = std::mem::replace(&mut self.inner[index], Box::new(item));
old.into_any().downcast::<T>().ok().map(|item| *item)
} else {
self.inner.push(Box::new(item));
None
}
}

pub fn get<T: CodeGenerationDataItem + 'static>(&self) -> Option<&T> {
self
.inner
.iter()
.find_map(|item| item.as_ref().as_any().downcast_ref::<T>())
}

pub fn get_mut<T: CodeGenerationDataItem + 'static>(&mut self) -> Option<&mut T> {
self
.inner
.iter_mut()
.find_map(|item| item.as_mut().as_any_mut().downcast_mut::<T>())
}

pub fn contains<T: CodeGenerationDataItem + 'static>(&self) -> bool {
self.get::<T>().is_some()
}

pub fn len(&self) -> usize {
self.inner.len()
}

pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
}

#[cacheable]
#[derive(Debug, Default, Clone)]
pub struct CodeGenerationResult {
#[cacheable(with=AsInner<AsMap<AsCacheable, AsPreset>>)]
pub inner: BindingCell<HashMap<SourceType, BoxSource>>,
/// [definition in webpack](https://github.com/webpack/webpack/blob/4b4ca3bb53f36a5b8fc6bc1bd976ed7af161bd80/lib/Module.js#L75)
pub data: CodeGenerationData,
pub chunk_init_fragments: ChunkInitFragments,
pub runtime_requirements: RuntimeGlobals,
pub hash: Option<RspackHashDigest>,
pub id: CodeGenResultId,
#[cacheable(with=AsOption<Unsupported>)]
pub concatenation_scope: Option<ConcatenationScope>,
Comment on lines +231 to 232

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid poisoning codegen cache for concatenation scopes

Marking concatenation_scope as AsOption<Unsupported> makes serialization fail whenever it is Some. The ESM library concatenation-scope hook returns Some(ConcatenationScope) for scope-hoisted modules and parser/generator stores it on CodeGenerationResult, so ModulesCodegenOccasion::save will fail encoding and reset the entire modules-codegen occasion for those builds. This silently disables the new persistent codegen cache in that configuration; serialize the scope or skip only those entries instead of failing the whole artifact.

Useful? React with 👍 / 👎.

}

Expand Down Expand Up @@ -168,7 +262,6 @@ impl CodeGenerationResult {
source_type.hash(&mut hasher);
std::hash::Hash::hash(source, &mut hasher);
}
self.chunk_init_fragments.hash(&mut hasher);
self.runtime_requirements.hash(&mut hasher);
Comment thread
ahabhgk marked this conversation as resolved.
self.hash = Some(hasher.digest(hash_digest));
}
Expand All @@ -189,25 +282,25 @@ impl CodeGenerationResult {
for source_type in self.inner.as_ref().keys() {
source_type.hash(&mut hasher);
}
self.chunk_init_fragments.hash(&mut hasher);
self.runtime_requirements.hash(&mut hasher);
self.hash = Some(hasher.digest(hash_digest));
}
}

#[cacheable]
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd, Serialize)]
pub struct CodeGenResultId(u32);

impl Default for CodeGenResultId {
fn default() -> Self {
Self(CODE_GEN_RESULT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed))
Self(fetch_new_code_generation_result_id())
}
}

pub static CODE_GEN_RESULT_ID: AtomicU32 = AtomicU32::new(0);

#[cacheable]
#[derive(Debug, Default, Clone)]
pub struct CodeGenerationResults {
#[cacheable(with=AsMap<AsCacheable, AsInner<AsCacheable>>)]
module_generation_result_map: HashMap<CodeGenResultId, BindingCell<CodeGenerationResult>>,
map: IdentifierMap<RuntimeSpecMap<CodeGenResultId>>,
}
Expand Down
13 changes: 13 additions & 0 deletions crates/rspack_core/src/binding/cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ mod napi_binding {
bindgen_prelude::{Object, ToNapiValue},
};
use once_cell::sync::OnceCell;
use rspack_cacheable::with::AsInnerConverter;
use rspack_napi::{ThreadsafeOneShotRef, object_assign};
use rspack_sources::BoxSource;
use rustc_hash::FxHashMap;
Expand Down Expand Up @@ -360,6 +361,18 @@ mod napi_binding {
}
}

impl<T: Into<BindingCell<T>>> AsInnerConverter for BindingCell<T> {
type Inner = T;

fn to_inner(&self) -> &Self::Inner {
self.as_ref()
}

fn from_inner(data: Self::Inner) -> Self {
data.into()
}
}

// Implement rkyv traits for BindingCell<T>

impl<T: rkyv::ArchiveUnsized + ?Sized> rkyv::Archive for BindingCell<T> {
Expand Down
2 changes: 1 addition & 1 deletion crates/rspack_core/src/cache/mixed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ impl Cache for MixedCache {
self.persistent.before_modules_codegen(compilation).await;
}

async fn after_modules_codegen(&self, compilation: &Compilation) {
async fn after_modules_codegen(&mut self, compilation: &Compilation) {
self.persistent.after_modules_codegen(compilation).await;
}

Expand Down
2 changes: 1 addition & 1 deletion crates/rspack_core/src/cache/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ pub trait Cache: Debug + Send + Sync {

// MODULES_CODEGEN hooks
async fn before_modules_codegen(&mut self, _compilation: &mut Compilation) {}
async fn after_modules_codegen(&self, _compilation: &Compilation) {}
async fn after_modules_codegen(&mut self, _compilation: &Compilation) {}

// MODULES_RUNTIME_REQUIREMENTS hooks
async fn before_modules_runtime_requirements(&mut self, _compilation: &mut Compilation) {}
Expand Down
2 changes: 2 additions & 0 deletions crates/rspack_core/src/cache/persistent/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ fn read_occasion_timing_label(name: &'static str) -> &'static str {
"make" => "read make from persistent cache",
"meta" => "read meta from persistent cache",
"module hashes" => "read module hashes from persistent cache",
"modules codegen" => "read modules codegen from persistent cache",
"minimize" => "read minimize from persistent cache",
"source map" => "read source map from persistent cache",
_ => "read occasion from persistent cache",
Expand All @@ -331,6 +332,7 @@ fn write_occasion_timing_label(name: &'static str) -> &'static str {
"make" => "write make to persistent cache",
"meta" => "write meta to persistent cache",
"module hashes" => "write module hashes to persistent cache",
"modules codegen" => "write modules codegen to persistent cache",
"minimize" => "write minimize to persistent cache",
"source map" => "write source map to persistent cache",
_ => "write occasion to persistent cache",
Expand Down
Loading
Loading