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#[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 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 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 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}