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  /// Concatenated modules already encode the generated module bodies into
178  /// `ConcatenatedModule::get_runtime_hash`, so we can reuse that digest here
179  /// and only mix in codegen-specific metadata instead of hashing the large
180  /// concatenated source again.
181  pub fn set_hash_for_concatenated_module(
182    &mut self,
183    runtime_hash: &RspackHashDigest,
184    hash_function: &HashFunction,
185    hash_digest: &HashDigest,
186    hash_salt: &HashSalt,
187  ) {
188    let mut hasher = RspackHash::with_salt(hash_function, hash_salt);
189    runtime_hash.hash(&mut hasher);
190    for source_type in self.inner.as_ref().keys() {
191      source_type.hash(&mut hasher);
192    }
193    self.chunk_init_fragments.hash(&mut hasher);
194    self.runtime_requirements.hash(&mut hasher);
195    self.hash = Some(hasher.digest(hash_digest));
196  }
197}
198
199#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd, Serialize)]
200pub struct CodeGenResultId(u32);
201
202impl Default for CodeGenResultId {
203  fn default() -> Self {
204    Self(CODE_GEN_RESULT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed))
205  }
206}
207
208pub static CODE_GEN_RESULT_ID: AtomicU32 = AtomicU32::new(0);
209
210#[derive(Debug, Default, Clone)]
211pub struct CodeGenerationResults {
212  module_generation_result_map: HashMap<CodeGenResultId, BindingCell<CodeGenerationResult>>,
213  map: IdentifierMap<RuntimeSpecMap<CodeGenResultId>>,
214}
215
216impl ArtifactExt for CodeGenerationResults {
217  const PASS: IncrementalPasses = IncrementalPasses::MODULES_CODEGEN;
218}
219
220impl CodeGenerationResults {
221  pub fn is_empty(&self) -> bool {
222    self.module_generation_result_map.is_empty() && self.map.is_empty()
223  }
224
225  pub fn insert(
226    &mut self,
227    module_identifier: ModuleIdentifier,
228    codegen_res: CodeGenerationResult,
229    runtimes: impl IntoIterator<Item = RuntimeSpec>,
230  ) {
231    let codegen_res_id = codegen_res.id;
232    self
233      .module_generation_result_map
234      .insert(codegen_res_id, BindingCell::from(codegen_res));
235    for runtime in runtimes {
236      self.add(module_identifier, runtime, codegen_res_id);
237    }
238  }
239
240  pub fn remove(&mut self, module_identifier: &ModuleIdentifier) -> Option<()> {
241    let runtime_map = self.map.remove(module_identifier)?;
242    for result in runtime_map.values() {
243      self.module_generation_result_map.remove(result)?;
244    }
245    Some(())
246  }
247
248  pub fn get(
249    &self,
250    module_identifier: &ModuleIdentifier,
251    runtime: Option<&RuntimeSpec>,
252  ) -> &BindingCell<CodeGenerationResult> {
253    if let Some(entry) = self.map.get(module_identifier) {
254      if let Some(runtime) = runtime {
255        entry
256          .get(runtime)
257          .and_then(|m| {
258            self.module_generation_result_map.get(m)
259          })
260          .unwrap_or_else(|| {
261            panic!(
262              "Failed to code generation result for {module_identifier} with runtime {runtime:?} \n {entry:?}"
263            )
264          })
265      } else {
266        if entry.size() > 1 {
267          let mut values = entry.values();
268          let results: FxHashSet<_> = entry.values().collect();
269          if results.len() > 1 {
270            panic!(
271              "No unique code generation entry for unspecified runtime for {module_identifier} ",
272            );
273          }
274
275          return values
276            .next()
277            .and_then(|m| self.module_generation_result_map.get(m))
278            .unwrap_or_else(|| panic!("Expected value exists"));
279        }
280
281        entry
282          .values()
283          .next()
284          .and_then(|m| self.module_generation_result_map.get(m))
285          .unwrap_or_else(|| panic!("Expected value exists"))
286      }
287    } else {
288      panic!(
289        "No code generation entry for {} (existing entries: {:?})",
290        module_identifier,
291        self.map.keys().collect::<Vec<_>>()
292      )
293    }
294  }
295
296  /**
297   * This API should be used carefully, it will return one of the code generation result,
298   * make sure the module has the same code generation result for all runtimes.
299   */
300  pub fn get_one(
301    &self,
302    module_identifier: &ModuleIdentifier,
303  ) -> &BindingCell<CodeGenerationResult> {
304    self
305      .map
306      .get(module_identifier)
307      .and_then(|entry| {
308        entry
309          .values()
310          .next()
311          .and_then(|m| self.module_generation_result_map.get(m))
312      })
313      .unwrap_or_else(|| panic!("No code generation result for {module_identifier}"))
314  }
315
316  pub fn get_mut(
317    &mut self,
318    module_identifier: &ModuleIdentifier,
319    runtime: Option<&RuntimeSpec>,
320  ) -> &mut BindingCell<CodeGenerationResult> {
321    if let Some(entry) = self.map.get(module_identifier) {
322      if let Some(runtime) = runtime {
323        entry
324          .get(runtime)
325          .and_then(|m| {
326            self.module_generation_result_map.get_mut(m)
327          })
328          .unwrap_or_else(|| {
329            panic!(
330              "Failed to code generation result for {module_identifier} with runtime {runtime:?} \n {entry:?}"
331            )
332          })
333      } else {
334        if entry.size() > 1 {
335          let mut values = entry.values();
336          let results: FxHashSet<_> = entry.values().collect();
337          if results.len() > 1 {
338            panic!(
339              "No unique code generation entry for unspecified runtime for {module_identifier} ",
340            );
341          }
342
343          return values
344            .next()
345            .and_then(|m| self.module_generation_result_map.get_mut(m))
346            .unwrap_or_else(|| panic!("Expected value exists"));
347        }
348
349        entry
350          .values()
351          .next()
352          .and_then(|m| self.module_generation_result_map.get_mut(m))
353          .unwrap_or_else(|| panic!("Expected value exists"))
354      }
355    } else {
356      panic!(
357        "No code generation entry for {} (existing entries: {:?})",
358        module_identifier,
359        self.map.keys().collect::<Vec<_>>()
360      )
361    }
362  }
363
364  pub fn add(
365    &mut self,
366    module_identifier: ModuleIdentifier,
367    runtime: RuntimeSpec,
368    result: CodeGenResultId,
369  ) {
370    match self.map.entry(module_identifier) {
371      Entry::Occupied(mut record) => {
372        record.get_mut().set(runtime, result);
373      }
374      Entry::Vacant(record) => {
375        let mut spec_map = RuntimeSpecMap::default();
376        spec_map.set(runtime, result);
377        record.insert(spec_map);
378      }
379    };
380  }
381
382  pub fn get_runtime_requirements(
383    &self,
384    module_identifier: &ModuleIdentifier,
385    runtime: Option<&RuntimeSpec>,
386  ) -> RuntimeGlobals {
387    self.get(module_identifier, runtime).runtime_requirements
388  }
389
390  pub fn get_hash(
391    &self,
392    module_identifier: &ModuleIdentifier,
393    runtime: Option<&RuntimeSpec>,
394  ) -> Option<&RspackHashDigest> {
395    let code_generation_result = self.get(module_identifier, runtime);
396
397    code_generation_result.hash.as_ref()
398  }
399
400  pub fn inner(
401    &self,
402  ) -> (
403    &IdentifierMap<RuntimeSpecMap<CodeGenResultId>>,
404    &HashMap<CodeGenResultId, BindingCell<CodeGenerationResult>>,
405  ) {
406    (&self.map, &self.module_generation_result_map)
407  }
408}
409
410#[derive(Debug)]
411pub struct CodeGenerationJob {
412  pub module: ModuleIdentifier,
413  pub hash: RspackHashDigest,
414  pub runtime: RuntimeSpec,
415  pub runtimes: Vec<RuntimeSpec>,
416  pub scope: Option<ConcatenationScope>,
417}