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