Skip to main content

rspack_core/artifacts/
code_generation_results.rs

1use std::{
2  collections::hash_map::Entry,
3  hash::Hash,
4  ops::{Deref, DerefMut},
5  sync::atomic::AtomicU32,
6};
7
8use anymap::CloneAny;
9use rspack_collections::IdentifierMap;
10use rspack_hash::{HashDigest, HashFunction, HashSalt, RspackHash, RspackHashDigest};
11use rspack_sources::BoxSource;
12use rspack_util::atom::Atom;
13use rustc_hash::{FxHashMap as HashMap, FxHashSet};
14use serde::Serialize;
15
16use crate::{
17  ArtifactExt, AssetInfo, BindingCell, ChunkInitFragments, ConcatenationScope, ModuleIdentifier,
18  RuntimeGlobals, RuntimeSpec, RuntimeSpecMap, SourceType, incremental::IncrementalPasses,
19};
20
21#[derive(Clone, Debug)]
22pub struct CodeGenerationDataUrl {
23  inner: String,
24}
25
26impl CodeGenerationDataUrl {
27  pub fn new(inner: String) -> Self {
28    Self { inner }
29  }
30
31  pub fn inner(&self) -> &str {
32    &self.inner
33  }
34}
35
36// For performance, mark the js modules containing AUTO_PUBLIC_PATH_PLACEHOLDER
37#[derive(Clone, Debug)]
38pub struct CodeGenerationPublicPathAutoReplace(pub bool);
39
40#[derive(Clone, Debug)]
41pub struct URLStaticMode;
42
43#[derive(Clone, Debug)]
44pub struct CodeGenerationDataFilename {
45  filename: String,
46  public_path: String,
47}
48
49impl CodeGenerationDataFilename {
50  pub fn new(filename: String, public_path: String) -> Self {
51    Self {
52      filename,
53      public_path,
54    }
55  }
56
57  pub fn filename(&self) -> &str {
58    &self.filename
59  }
60
61  pub fn public_path(&self) -> &str {
62    &self.public_path
63  }
64}
65
66#[derive(Clone, Debug)]
67pub struct CodeGenerationDataAssetInfo {
68  inner: AssetInfo,
69}
70
71impl CodeGenerationDataAssetInfo {
72  pub fn new(inner: AssetInfo) -> Self {
73    Self { inner }
74  }
75
76  pub fn inner(&self) -> &AssetInfo {
77    &self.inner
78  }
79}
80
81#[derive(Clone, Debug)]
82pub struct CodeGenerationDataTopLevelDeclarations {
83  inner: FxHashSet<Atom>,
84}
85
86impl CodeGenerationDataTopLevelDeclarations {
87  pub fn new(inner: FxHashSet<Atom>) -> Self {
88    Self { inner }
89  }
90
91  pub fn inner(&self) -> &FxHashSet<Atom> {
92    &self.inner
93  }
94}
95
96#[derive(Clone, Debug)]
97pub struct CodeGenerationExportsFinalNames {
98  inner: HashMap<String, String>,
99}
100
101impl CodeGenerationExportsFinalNames {
102  pub fn new(inner: HashMap<String, String>) -> Self {
103    Self { inner }
104  }
105
106  pub fn inner(&self) -> &HashMap<String, String> {
107    &self.inner
108  }
109}
110
111#[derive(Debug, Default, Clone)]
112pub struct CodeGenerationData {
113  inner: anymap::Map<dyn CloneAny + Send + Sync>,
114}
115
116impl Deref for CodeGenerationData {
117  type Target = anymap::Map<dyn CloneAny + Send + Sync>;
118
119  fn deref(&self) -> &Self::Target {
120    &self.inner
121  }
122}
123
124impl DerefMut for CodeGenerationData {
125  fn deref_mut(&mut self) -> &mut Self::Target {
126    &mut self.inner
127  }
128}
129
130#[derive(Debug, Default, Clone)]
131pub struct CodeGenerationResult {
132  pub inner: BindingCell<HashMap<SourceType, BoxSource>>,
133  /// [definition in webpack](https://github.com/webpack/webpack/blob/4b4ca3bb53f36a5b8fc6bc1bd976ed7af161bd80/lib/Module.js#L75)
134  pub data: CodeGenerationData,
135  pub chunk_init_fragments: ChunkInitFragments,
136  pub runtime_requirements: RuntimeGlobals,
137  pub hash: Option<RspackHashDigest>,
138  pub id: CodeGenResultId,
139  pub concatenation_scope: Option<ConcatenationScope>,
140}
141
142impl CodeGenerationResult {
143  pub fn with_javascript(mut self, generation_result: BoxSource) -> Self {
144    self.inner.insert(SourceType::JavaScript, generation_result);
145    self
146  }
147
148  pub fn inner(&self) -> &HashMap<SourceType, BoxSource> {
149    &self.inner
150  }
151
152  pub fn get(&self, source_type: &SourceType) -> Option<&BoxSource> {
153    self.inner.get(source_type)
154  }
155
156  pub fn add(&mut self, source_type: SourceType, generation_result: BoxSource) {
157    let result = self.inner.insert(source_type, generation_result);
158    debug_assert!(result.is_none());
159  }
160
161  pub fn set_hash(
162    &mut self,
163    hash_function: &HashFunction,
164    hash_digest: &HashDigest,
165    hash_salt: &HashSalt,
166  ) {
167    let mut hasher = RspackHash::with_salt(hash_function, hash_salt);
168    for (source_type, source) in self.inner.as_ref() {
169      source_type.hash(&mut hasher);
170      source.hash(&mut hasher);
171    }
172    self.chunk_init_fragments.hash(&mut hasher);
173    self.runtime_requirements.hash(&mut hasher);
174    self.hash = Some(hasher.digest(hash_digest));
175  }
176}
177
178#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd, Serialize)]
179pub struct CodeGenResultId(u32);
180
181impl Default for CodeGenResultId {
182  fn default() -> Self {
183    Self(CODE_GEN_RESULT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed))
184  }
185}
186
187pub static CODE_GEN_RESULT_ID: AtomicU32 = AtomicU32::new(0);
188
189#[derive(Debug, Default, Clone)]
190pub struct CodeGenerationResults {
191  module_generation_result_map: HashMap<CodeGenResultId, BindingCell<CodeGenerationResult>>,
192  map: IdentifierMap<RuntimeSpecMap<CodeGenResultId>>,
193}
194
195impl ArtifactExt for CodeGenerationResults {
196  const PASS: IncrementalPasses = IncrementalPasses::MODULES_CODEGEN;
197}
198
199impl CodeGenerationResults {
200  pub fn is_empty(&self) -> bool {
201    self.module_generation_result_map.is_empty() && self.map.is_empty()
202  }
203
204  pub fn insert(
205    &mut self,
206    module_identifier: ModuleIdentifier,
207    codegen_res: CodeGenerationResult,
208    runtimes: impl IntoIterator<Item = RuntimeSpec>,
209  ) {
210    let codegen_res_id = codegen_res.id;
211    self
212      .module_generation_result_map
213      .insert(codegen_res_id, BindingCell::from(codegen_res));
214    for runtime in runtimes {
215      self.add(module_identifier, runtime, codegen_res_id);
216    }
217  }
218
219  pub fn remove(&mut self, module_identifier: &ModuleIdentifier) -> Option<()> {
220    let runtime_map = self.map.remove(module_identifier)?;
221    for result in runtime_map.values() {
222      self.module_generation_result_map.remove(result)?;
223    }
224    Some(())
225  }
226
227  pub fn get(
228    &self,
229    module_identifier: &ModuleIdentifier,
230    runtime: Option<&RuntimeSpec>,
231  ) -> &BindingCell<CodeGenerationResult> {
232    if let Some(entry) = self.map.get(module_identifier) {
233      if let Some(runtime) = runtime {
234        entry
235          .get(runtime)
236          .and_then(|m| {
237            self.module_generation_result_map.get(m)
238          })
239          .unwrap_or_else(|| {
240            panic!(
241              "Failed to code generation result for {module_identifier} with runtime {runtime:?} \n {entry:?}"
242            )
243          })
244      } else {
245        if entry.size() > 1 {
246          let mut values = entry.values();
247          let results: FxHashSet<_> = entry.values().collect();
248          if results.len() > 1 {
249            panic!(
250              "No unique code generation entry for unspecified runtime for {module_identifier} ",
251            );
252          }
253
254          return values
255            .next()
256            .and_then(|m| self.module_generation_result_map.get(m))
257            .unwrap_or_else(|| panic!("Expected value exists"));
258        }
259
260        entry
261          .values()
262          .next()
263          .and_then(|m| self.module_generation_result_map.get(m))
264          .unwrap_or_else(|| panic!("Expected value exists"))
265      }
266    } else {
267      panic!(
268        "No code generation entry for {} (existing entries: {:?})",
269        module_identifier,
270        self.map.keys().collect::<Vec<_>>()
271      )
272    }
273  }
274
275  /**
276   * This API should be used carefully, it will return one of the code generation result,
277   * make sure the module has the same code generation result for all runtimes.
278   */
279  pub fn get_one(
280    &self,
281    module_identifier: &ModuleIdentifier,
282  ) -> &BindingCell<CodeGenerationResult> {
283    self
284      .map
285      .get(module_identifier)
286      .and_then(|entry| {
287        entry
288          .values()
289          .next()
290          .and_then(|m| self.module_generation_result_map.get(m))
291      })
292      .unwrap_or_else(|| panic!("No code generation result for {module_identifier}"))
293  }
294
295  pub fn get_mut(
296    &mut self,
297    module_identifier: &ModuleIdentifier,
298    runtime: Option<&RuntimeSpec>,
299  ) -> &mut BindingCell<CodeGenerationResult> {
300    if let Some(entry) = self.map.get(module_identifier) {
301      if let Some(runtime) = runtime {
302        entry
303          .get(runtime)
304          .and_then(|m| {
305            self.module_generation_result_map.get_mut(m)
306          })
307          .unwrap_or_else(|| {
308            panic!(
309              "Failed to code generation result for {module_identifier} with runtime {runtime:?} \n {entry:?}"
310            )
311          })
312      } else {
313        if entry.size() > 1 {
314          let mut values = entry.values();
315          let results: FxHashSet<_> = entry.values().collect();
316          if results.len() > 1 {
317            panic!(
318              "No unique code generation entry for unspecified runtime for {module_identifier} ",
319            );
320          }
321
322          return values
323            .next()
324            .and_then(|m| self.module_generation_result_map.get_mut(m))
325            .unwrap_or_else(|| panic!("Expected value exists"));
326        }
327
328        entry
329          .values()
330          .next()
331          .and_then(|m| self.module_generation_result_map.get_mut(m))
332          .unwrap_or_else(|| panic!("Expected value exists"))
333      }
334    } else {
335      panic!(
336        "No code generation entry for {} (existing entries: {:?})",
337        module_identifier,
338        self.map.keys().collect::<Vec<_>>()
339      )
340    }
341  }
342
343  pub fn add(
344    &mut self,
345    module_identifier: ModuleIdentifier,
346    runtime: RuntimeSpec,
347    result: CodeGenResultId,
348  ) {
349    match self.map.entry(module_identifier) {
350      Entry::Occupied(mut record) => {
351        record.get_mut().set(runtime, result);
352      }
353      Entry::Vacant(record) => {
354        let mut spec_map = RuntimeSpecMap::default();
355        spec_map.set(runtime, result);
356        record.insert(spec_map);
357      }
358    };
359  }
360
361  pub fn get_runtime_requirements(
362    &self,
363    module_identifier: &ModuleIdentifier,
364    runtime: Option<&RuntimeSpec>,
365  ) -> RuntimeGlobals {
366    self.get(module_identifier, runtime).runtime_requirements
367  }
368
369  pub fn get_hash(
370    &self,
371    module_identifier: &ModuleIdentifier,
372    runtime: Option<&RuntimeSpec>,
373  ) -> Option<&RspackHashDigest> {
374    let code_generation_result = self.get(module_identifier, runtime);
375
376    code_generation_result.hash.as_ref()
377  }
378
379  pub fn inner(
380    &self,
381  ) -> (
382    &IdentifierMap<RuntimeSpecMap<CodeGenResultId>>,
383    &HashMap<CodeGenResultId, BindingCell<CodeGenerationResult>>,
384  ) {
385    (&self.map, &self.module_generation_result_map)
386  }
387}
388
389#[derive(Debug)]
390pub struct CodeGenerationJob {
391  pub module: ModuleIdentifier,
392  pub hash: RspackHashDigest,
393  pub runtime: RuntimeSpec,
394  pub runtimes: Vec<RuntimeSpec>,
395  pub scope: Option<ConcatenationScope>,
396}