1#![allow(non_local_definitions)]
3#![feature(box_patterns)]
4#![feature(min_specialization)]
5#![feature(iter_intersperse)]
6#![feature(int_roundings)]
7#![feature(arbitrary_self_types)]
8#![feature(arbitrary_self_types_pointers)]
9#![recursion_limit = "256"]
10
11pub mod analyzer;
12pub mod annotations;
13pub mod async_chunk;
14pub mod chunk;
15pub mod code_gen;
16mod errors;
17pub mod inlined_bytes_module;
18pub mod magic_identifier;
19pub mod manifest;
20mod merged_module;
21pub mod minify;
22pub mod parse;
23mod path_visitor;
24pub mod references;
25pub mod runtime_functions;
26pub mod side_effect_optimization;
27pub mod single_file_ecmascript_output;
28pub mod source_map;
29pub(crate) mod static_code;
30mod swc_comments;
31pub mod text;
32pub(crate) mod transform;
33pub mod tree_shake;
34pub mod typescript;
35pub mod utils;
36pub mod webpack;
37pub mod worker_chunk;
38
39use std::{
40 borrow::Cow,
41 collections::hash_map::Entry,
42 fmt::{Debug, Display, Formatter},
43 mem::take,
44 sync::{Arc, Mutex},
45};
46
47use anyhow::{Context, Result, anyhow, bail};
48use bincode::{Decode, Encode};
49use either::Either;
50use itertools::Itertools;
51use rustc_hash::{FxHashMap, FxHashSet};
52use serde::Deserialize;
53use smallvec::SmallVec;
54use swc_core::{
55 atoms::Atom,
56 base::SwcComments,
57 common::{
58 BytePos, DUMMY_SP, FileName, GLOBALS, Globals, Loc, Mark, SourceFile, SourceMap,
59 SourceMapper, Span, SpanSnippetError, SyntaxContext,
60 comments::{Comment, CommentKind, Comments},
61 source_map::{FileLinesResult, Files, SourceMapLookupError},
62 util::take::Take,
63 },
64 ecma::{
65 ast::{
66 self, CallExpr, Callee, Decl, EmptyStmt, Expr, ExprStmt, Id, Ident, ModuleItem,
67 Program, Script, SourceMapperExt, Stmt,
68 },
69 codegen::{Emitter, text_writer::JsWriter},
70 utils::StmtLikeInjector,
71 visit::{VisitMut, VisitMutWith, VisitMutWithAstPath, VisitWith},
72 },
73 quote,
74};
75use tracing::{Instrument, Level, instrument};
76use turbo_rcstr::{RcStr, rcstr};
77use turbo_tasks::{
78 FxDashMap, FxIndexMap, IntoTraitRef, NonLocalValue, ReadRef, ResolvedVc, TaskInput,
79 TryJoinIterExt, Upcast, ValueToString, Vc, trace::TraceRawVcs,
80};
81use turbo_tasks_fs::{FileJsonContent, FileSystemPath, glob::Glob, rope::Rope};
82use turbopack_core::{
83 chunk::{
84 AsyncModuleInfo, ChunkItem, ChunkType, ChunkableModule, ChunkingContext, EvaluatableAsset,
85 MergeableModule, MergeableModuleExposure, MergeableModules, MergeableModulesExposed,
86 MinifyType, ModuleChunkItemIdExt, ModuleId,
87 },
88 compile_time_info::CompileTimeInfo,
89 context::AssetContext,
90 ident::AssetIdent,
91 module::{Module, ModuleSideEffects, OptionModule},
92 module_graph::ModuleGraph,
93 output::OutputAssetsReference,
94 reference::ModuleReferences,
95 reference_type::InnerAssets,
96 resolve::{
97 FindContextFileResult, find_context_file, origin::ResolveOrigin, package_json,
98 parse::Request,
99 },
100 source::Source,
101 source_map::GenerateSourceMap,
102};
103
104use crate::{
105 analyzer::graph::EvalContext,
106 chunk::{
107 EcmascriptChunkItem, EcmascriptChunkItemContent, EcmascriptChunkPlaceable,
108 EcmascriptChunkType, EcmascriptExports,
109 placeable::{SideEffectsDeclaration, get_side_effect_free_declaration},
110 },
111 code_gen::{CodeGeneration, CodeGenerationHoistedStmt, CodeGens, ModifiableAst},
112 merged_module::MergedEcmascriptModule,
113 parse::{IdentCollector, ParseResult, generate_js_source_map, parse},
114 path_visitor::ApplyVisitors,
115 references::{
116 analyze_ecmascript_module,
117 async_module::OptionAsyncModule,
118 esm::{UrlRewriteBehavior, base::EsmAssetReferences, export},
119 },
120 side_effect_optimization::reference::EcmascriptModulePartReference,
121 swc_comments::{CowComments, ImmutableComments},
122 transform::{remove_directives, remove_shebang},
123};
124pub use crate::{
125 references::{AnalyzeEcmascriptModuleResult, TURBOPACK_HELPER},
126 static_code::StaticEcmascriptCode,
127 transform::{
128 CustomTransformer, EcmascriptInputTransform, EcmascriptInputTransforms, TransformContext,
129 TransformPlugin,
130 },
131};
132
133#[derive(
134 Eq,
135 PartialEq,
136 Hash,
137 Debug,
138 Clone,
139 Copy,
140 Default,
141 TaskInput,
142 TraceRawVcs,
143 NonLocalValue,
144 Deserialize,
145 Encode,
146 Decode,
147)]
148pub enum SpecifiedModuleType {
149 #[default]
150 Automatic,
151 CommonJs,
152 EcmaScript,
153}
154
155#[derive(
156 PartialOrd,
157 Ord,
158 PartialEq,
159 Eq,
160 Hash,
161 Debug,
162 Clone,
163 Copy,
164 Default,
165 Deserialize,
166 TaskInput,
167 TraceRawVcs,
168 NonLocalValue,
169 Encode,
170 Decode,
171)]
172#[serde(rename_all = "kebab-case")]
173pub enum TreeShakingMode {
174 ModuleFragments,
175 #[default]
176 ReexportsOnly,
177}
178
179#[derive(
180 PartialOrd,
181 Ord,
182 PartialEq,
183 Eq,
184 Hash,
185 Debug,
186 Clone,
187 Copy,
188 Default,
189 Deserialize,
190 TaskInput,
191 TraceRawVcs,
192 NonLocalValue,
193 Encode,
194 Decode,
195)]
196pub enum AnalyzeMode {
197 #[default]
199 CodeGeneration,
200 CodeGenerationAndTracing,
202 Tracing,
204}
205
206impl AnalyzeMode {
207 pub fn is_tracing_assets(self) -> bool {
209 match self {
210 AnalyzeMode::Tracing | AnalyzeMode::CodeGenerationAndTracing => true,
211 AnalyzeMode::CodeGeneration => false,
212 }
213 }
214
215 pub fn is_code_gen(self) -> bool {
216 match self {
217 AnalyzeMode::CodeGeneration | AnalyzeMode::CodeGenerationAndTracing => true,
218 AnalyzeMode::Tracing => false,
219 }
220 }
221}
222
223#[turbo_tasks::value(transparent)]
224pub struct OptionTreeShaking(pub Option<TreeShakingMode>);
225
226#[derive(
228 Copy, Clone, PartialEq, Eq, Debug, Hash, TraceRawVcs, NonLocalValue, TaskInput, Encode, Decode,
229)]
230pub enum TypeofWindow {
231 Object,
232 Undefined,
233}
234
235#[turbo_tasks::value(shared)]
236#[derive(Debug, Default, Copy, Clone)]
237pub struct EcmascriptOptions {
238 pub tree_shaking_mode: Option<TreeShakingMode>,
240 pub specified_module_type: SpecifiedModuleType,
242 pub url_rewrite_behavior: Option<UrlRewriteBehavior>,
246 pub import_externals: bool,
249 pub ignore_dynamic_requests: bool,
253 pub extract_source_map: bool,
256 pub keep_last_successful_parse: bool,
260 pub analyze_mode: AnalyzeMode,
263 pub enable_typeof_window_inlining: Option<TypeofWindow>,
268 pub enable_exports_info_inlining: bool,
270
271 pub inline_helpers: bool,
272 pub infer_module_side_effects: bool,
274}
275
276#[turbo_tasks::value]
277#[derive(Hash, Debug, Copy, Clone, TaskInput)]
278pub enum EcmascriptModuleAssetType {
279 Ecmascript,
281 EcmascriptExtensionless,
283 Typescript {
285 tsx: bool,
287 analyze_types: bool,
289 },
290 TypescriptDeclaration,
292}
293
294impl Display for EcmascriptModuleAssetType {
295 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
296 match self {
297 EcmascriptModuleAssetType::Ecmascript => write!(f, "ecmascript"),
298 EcmascriptModuleAssetType::EcmascriptExtensionless => {
299 write!(f, "ecmascript extensionless")
300 }
301 EcmascriptModuleAssetType::Typescript { tsx, analyze_types } => {
302 write!(f, "typescript")?;
303 if *tsx {
304 write!(f, " with JSX")?;
305 }
306 if *analyze_types {
307 write!(f, " with types")?;
308 }
309 Ok(())
310 }
311 EcmascriptModuleAssetType::TypescriptDeclaration => write!(f, "typescript declaration"),
312 }
313 }
314}
315
316#[derive(Clone)]
317pub struct EcmascriptModuleAssetBuilder {
318 source: ResolvedVc<Box<dyn Source>>,
319 asset_context: ResolvedVc<Box<dyn AssetContext>>,
320 ty: EcmascriptModuleAssetType,
321 transforms: ResolvedVc<EcmascriptInputTransforms>,
322 options: ResolvedVc<EcmascriptOptions>,
323 compile_time_info: ResolvedVc<CompileTimeInfo>,
324 side_effect_free_packages: Option<ResolvedVc<Glob>>,
325 inner_assets: Option<ResolvedVc<InnerAssets>>,
326}
327
328impl EcmascriptModuleAssetBuilder {
329 pub fn with_inner_assets(mut self, inner_assets: ResolvedVc<InnerAssets>) -> Self {
330 self.inner_assets = Some(inner_assets);
331 self
332 }
333
334 pub fn with_type(mut self, ty: EcmascriptModuleAssetType) -> Self {
335 self.ty = ty;
336 self
337 }
338
339 pub fn build(self) -> Vc<EcmascriptModuleAsset> {
340 if let Some(inner_assets) = self.inner_assets {
341 EcmascriptModuleAsset::new_with_inner_assets(
342 *self.source,
343 *self.asset_context,
344 self.ty,
345 *self.transforms,
346 *self.options,
347 *self.compile_time_info,
348 self.side_effect_free_packages.map(|g| *g),
349 *inner_assets,
350 )
351 } else {
352 EcmascriptModuleAsset::new(
353 *self.source,
354 *self.asset_context,
355 self.ty,
356 *self.transforms,
357 *self.options,
358 *self.compile_time_info,
359 self.side_effect_free_packages.map(|g| *g),
360 )
361 }
362 }
363}
364
365#[turbo_tasks::value]
366pub struct EcmascriptModuleAsset {
367 pub source: ResolvedVc<Box<dyn Source>>,
368 pub asset_context: ResolvedVc<Box<dyn AssetContext>>,
369 pub ty: EcmascriptModuleAssetType,
370 pub transforms: ResolvedVc<EcmascriptInputTransforms>,
371 pub options: ResolvedVc<EcmascriptOptions>,
372 pub compile_time_info: ResolvedVc<CompileTimeInfo>,
373 pub side_effect_free_packages: Option<ResolvedVc<Glob>>,
374 pub inner_assets: Option<ResolvedVc<InnerAssets>>,
375 #[turbo_tasks(debug_ignore)]
376 last_successful_parse: turbo_tasks::TransientState<ReadRef<ParseResult>>,
377}
378impl core::fmt::Debug for EcmascriptModuleAsset {
379 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
380 f.debug_struct("EcmascriptModuleAsset")
381 .field("source", &self.source)
382 .field("asset_context", &self.asset_context)
383 .field("ty", &self.ty)
384 .field("transforms", &self.transforms)
385 .field("options", &self.options)
386 .field("compile_time_info", &self.compile_time_info)
387 .field("side_effect_free_packages", &self.side_effect_free_packages)
388 .field("inner_assets", &self.inner_assets)
389 .finish()
390 }
391}
392
393#[turbo_tasks::value_trait]
394pub trait EcmascriptParsable {
395 #[turbo_tasks::function]
396 fn failsafe_parse(self: Vc<Self>) -> Result<Vc<ParseResult>>;
397
398 #[turbo_tasks::function]
399 fn parse_original(self: Vc<Self>) -> Result<Vc<ParseResult>>;
400
401 #[turbo_tasks::function]
402 fn ty(self: Vc<Self>) -> Result<Vc<EcmascriptModuleAssetType>>;
403}
404
405#[turbo_tasks::value_trait]
406pub trait EcmascriptAnalyzable: Module {
407 #[turbo_tasks::function]
408 fn analyze(self: Vc<Self>) -> Vc<AnalyzeEcmascriptModuleResult>;
409
410 #[turbo_tasks::function]
413 async fn module_content_without_analysis(
414 self: Vc<Self>,
415 generate_source_map: bool,
416 ) -> Result<Vc<EcmascriptModuleContent>>;
417
418 #[turbo_tasks::function]
419 async fn module_content_options(
420 self: Vc<Self>,
421 chunking_context: Vc<Box<dyn ChunkingContext>>,
422 async_module_info: Option<Vc<AsyncModuleInfo>>,
423 ) -> Result<Vc<EcmascriptModuleContentOptions>>;
424}
425
426pub trait EcmascriptAnalyzableExt {
427 fn module_content(
428 self: Vc<Self>,
429 chunking_context: Vc<Box<dyn ChunkingContext>>,
430 async_module_info: Option<Vc<AsyncModuleInfo>>,
431 ) -> Vc<EcmascriptModuleContent>;
432}
433
434impl<T> EcmascriptAnalyzableExt for T
435where
436 T: EcmascriptAnalyzable + Upcast<Box<dyn EcmascriptAnalyzable>>,
437{
438 fn module_content(
439 self: Vc<Self>,
440 chunking_context: Vc<Box<dyn ChunkingContext>>,
441 async_module_info: Option<Vc<AsyncModuleInfo>>,
442 ) -> Vc<EcmascriptModuleContent> {
443 let analyzable = Vc::upcast_non_strict::<Box<dyn EcmascriptAnalyzable>>(self);
444 let own_options = analyzable.module_content_options(chunking_context, async_module_info);
445 EcmascriptModuleContent::new(own_options)
446 }
447}
448
449impl EcmascriptModuleAsset {
450 pub fn builder(
451 source: ResolvedVc<Box<dyn Source>>,
452 asset_context: ResolvedVc<Box<dyn AssetContext>>,
453 transforms: ResolvedVc<EcmascriptInputTransforms>,
454 options: ResolvedVc<EcmascriptOptions>,
455 compile_time_info: ResolvedVc<CompileTimeInfo>,
456 side_effect_free_packages: Option<ResolvedVc<Glob>>,
457 ) -> EcmascriptModuleAssetBuilder {
458 EcmascriptModuleAssetBuilder {
459 source,
460 asset_context,
461 ty: EcmascriptModuleAssetType::Ecmascript,
462 transforms,
463 options,
464 compile_time_info,
465 side_effect_free_packages,
466 inner_assets: None,
467 }
468 }
469}
470
471#[turbo_tasks::value]
472#[derive(Clone)]
473pub(crate) struct ModuleTypeResult {
474 pub module_type: SpecifiedModuleType,
475 pub referenced_package_json: Option<FileSystemPath>,
476}
477
478#[turbo_tasks::value_impl]
479impl ModuleTypeResult {
480 #[turbo_tasks::function]
481 fn new(module_type: SpecifiedModuleType) -> Vc<Self> {
482 Self::cell(ModuleTypeResult {
483 module_type,
484 referenced_package_json: None,
485 })
486 }
487
488 #[turbo_tasks::function]
489 fn new_with_package_json(
490 module_type: SpecifiedModuleType,
491 package_json: FileSystemPath,
492 ) -> Vc<Self> {
493 Self::cell(ModuleTypeResult {
494 module_type,
495 referenced_package_json: Some(package_json),
496 })
497 }
498}
499
500#[turbo_tasks::value_impl]
501impl EcmascriptParsable for EcmascriptModuleAsset {
502 #[turbo_tasks::function]
503 async fn failsafe_parse(&self) -> Result<Vc<ParseResult>> {
504 let real_result = self.parse().await?;
505 if self.options.await?.keep_last_successful_parse {
506 let real_result_value = real_result.await?;
507 let result_value = if matches!(*real_result_value, ParseResult::Ok { .. }) {
508 self.last_successful_parse
509 .set_unconditionally(real_result_value.clone());
510 real_result_value
511 } else {
512 let state_ref = self.last_successful_parse.get();
513 state_ref.as_ref().unwrap_or(&real_result_value).clone()
514 };
515 Ok(ReadRef::cell(result_value))
516 } else {
517 Ok(real_result)
518 }
519 }
520
521 #[turbo_tasks::function]
522 fn parse_original(self: Vc<Self>) -> Vc<ParseResult> {
523 self.failsafe_parse()
524 }
525
526 #[turbo_tasks::function]
527 fn ty(&self) -> Vc<EcmascriptModuleAssetType> {
528 self.ty.cell()
529 }
530}
531
532#[turbo_tasks::value_impl]
533impl EcmascriptAnalyzable for EcmascriptModuleAsset {
534 #[turbo_tasks::function]
535 fn analyze(self: Vc<Self>) -> Vc<AnalyzeEcmascriptModuleResult> {
536 analyze_ecmascript_module(self, None)
537 }
538
539 #[turbo_tasks::function]
542 async fn module_content_without_analysis(
543 self: Vc<Self>,
544 generate_source_map: bool,
545 ) -> Result<Vc<EcmascriptModuleContent>> {
546 let this = self.await?;
547
548 let parsed = this.parse().await?;
549
550 Ok(EcmascriptModuleContent::new_without_analysis(
551 parsed,
552 self.ident(),
553 this.options.await?.specified_module_type,
554 generate_source_map,
555 ))
556 }
557
558 #[turbo_tasks::function]
559 async fn module_content_options(
560 self: ResolvedVc<Self>,
561 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
562 async_module_info: Option<ResolvedVc<AsyncModuleInfo>>,
563 ) -> Result<Vc<EcmascriptModuleContentOptions>> {
564 let parsed = self.await?.parse().await?.to_resolved().await?;
565
566 let analyze = self.analyze();
567 let analyze_ref = analyze.await?;
568
569 let module_type_result = self.determine_module_type().await?;
570 let generate_source_map = *chunking_context
571 .reference_module_source_maps(Vc::upcast(*self))
572 .await?;
573
574 Ok(EcmascriptModuleContentOptions {
575 parsed: Some(parsed),
576 module: ResolvedVc::upcast(self),
577 specified_module_type: module_type_result.module_type,
578 chunking_context,
579 references: analyze.references().to_resolved().await?,
580 esm_references: analyze_ref.esm_references,
581 part_references: vec![],
582 code_generation: analyze_ref.code_generation,
583 async_module: analyze_ref.async_module,
584 generate_source_map,
585 original_source_map: analyze_ref.source_map,
586 exports: analyze_ref.exports,
587 async_module_info,
588 }
589 .cell())
590 }
591}
592
593#[turbo_tasks::function]
594async fn determine_module_type_for_directory(
595 context_path: FileSystemPath,
596) -> Result<Vc<ModuleTypeResult>> {
597 let find_package_json =
598 find_context_file(context_path, package_json().resolve().await?, false).await?;
599 let FindContextFileResult::Found(package_json, _) = &*find_package_json else {
600 return Ok(ModuleTypeResult::new(SpecifiedModuleType::Automatic));
601 };
602
603 if let FileJsonContent::Content(content) = &*package_json.read_json().await?
605 && let Some(r#type) = content.get("type")
606 {
607 return Ok(ModuleTypeResult::new_with_package_json(
608 match r#type.as_str() {
609 Some("module") => SpecifiedModuleType::EcmaScript,
610 Some("commonjs") => SpecifiedModuleType::CommonJs,
611 _ => SpecifiedModuleType::Automatic,
612 },
613 package_json.clone(),
614 ));
615 }
616
617 Ok(ModuleTypeResult::new_with_package_json(
618 SpecifiedModuleType::Automatic,
619 package_json.clone(),
620 ))
621}
622
623#[turbo_tasks::value_impl]
624impl EcmascriptModuleAsset {
625 #[turbo_tasks::function]
626 fn new(
627 source: ResolvedVc<Box<dyn Source>>,
628 asset_context: ResolvedVc<Box<dyn AssetContext>>,
629 ty: EcmascriptModuleAssetType,
630 transforms: ResolvedVc<EcmascriptInputTransforms>,
631 options: ResolvedVc<EcmascriptOptions>,
632 compile_time_info: ResolvedVc<CompileTimeInfo>,
633 side_effect_free_packages: Option<ResolvedVc<Glob>>,
634 ) -> Vc<Self> {
635 Self::cell(EcmascriptModuleAsset {
636 source,
637 asset_context,
638 ty,
639 transforms,
640 options,
641 compile_time_info,
642 side_effect_free_packages,
643 inner_assets: None,
644 last_successful_parse: Default::default(),
645 })
646 }
647
648 #[turbo_tasks::function]
649 async fn new_with_inner_assets(
650 source: ResolvedVc<Box<dyn Source>>,
651 asset_context: ResolvedVc<Box<dyn AssetContext>>,
652 ty: EcmascriptModuleAssetType,
653 transforms: ResolvedVc<EcmascriptInputTransforms>,
654 options: ResolvedVc<EcmascriptOptions>,
655 compile_time_info: ResolvedVc<CompileTimeInfo>,
656 side_effect_free_packages: Option<ResolvedVc<Glob>>,
657 inner_assets: ResolvedVc<InnerAssets>,
658 ) -> Result<Vc<Self>> {
659 if inner_assets.await?.is_empty() {
660 Ok(Self::new(
661 *source,
662 *asset_context,
663 ty,
664 *transforms,
665 *options,
666 *compile_time_info,
667 side_effect_free_packages.map(|g| *g),
668 ))
669 } else {
670 Ok(Self::cell(EcmascriptModuleAsset {
671 source,
672 asset_context,
673 ty,
674 transforms,
675 options,
676 compile_time_info,
677 side_effect_free_packages,
678 inner_assets: Some(inner_assets),
679 last_successful_parse: Default::default(),
680 }))
681 }
682 }
683
684 #[turbo_tasks::function]
685 pub fn source(&self) -> Vc<Box<dyn Source>> {
686 *self.source
687 }
688
689 #[turbo_tasks::function]
690 pub fn analyze(self: Vc<Self>) -> Vc<AnalyzeEcmascriptModuleResult> {
691 analyze_ecmascript_module(self, None)
692 }
693
694 #[turbo_tasks::function]
695 pub fn options(&self) -> Vc<EcmascriptOptions> {
696 *self.options
697 }
698}
699
700impl EcmascriptModuleAsset {
701 pub async fn parse(&self) -> Result<Vc<ParseResult>> {
702 let options = self.options.await?;
703 Ok(parse(
704 *self.source,
705 self.ty,
706 *self.transforms,
707 options.analyze_mode == AnalyzeMode::Tracing,
708 options.inline_helpers,
709 ))
710 }
711
712 #[tracing::instrument(level = "trace", skip_all)]
713 pub(crate) async fn determine_module_type(self: Vc<Self>) -> Result<ReadRef<ModuleTypeResult>> {
714 let this = self.await?;
715
716 match this.options.await?.specified_module_type {
717 SpecifiedModuleType::EcmaScript => {
718 return ModuleTypeResult::new(SpecifiedModuleType::EcmaScript).await;
719 }
720 SpecifiedModuleType::CommonJs => {
721 return ModuleTypeResult::new(SpecifiedModuleType::CommonJs).await;
722 }
723 SpecifiedModuleType::Automatic => {}
724 }
725
726 determine_module_type_for_directory(self.origin_path().await?.parent()).await
727 }
728}
729
730#[turbo_tasks::value_impl]
731impl Module for EcmascriptModuleAsset {
732 #[turbo_tasks::function]
733 async fn ident(&self) -> Result<Vc<AssetIdent>> {
734 let mut ident = self.source.ident().owned().await?;
735 if let Some(inner_assets) = self.inner_assets {
736 for (name, asset) in inner_assets.await?.iter() {
737 ident.add_asset(name.clone(), asset.ident().to_resolved().await?);
738 }
739 }
740 ident.add_modifier(rcstr!("ecmascript"));
741 ident.layer = Some(self.asset_context.into_trait_ref().await?.layer());
742 Ok(AssetIdent::new(ident))
743 }
744
745 #[turbo_tasks::function]
746 fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
747 Vc::cell(Some(self.source))
748 }
749
750 #[turbo_tasks::function]
751 fn references(self: Vc<Self>) -> Result<Vc<ModuleReferences>> {
752 Ok(self.analyze().references())
753 }
754
755 #[turbo_tasks::function]
756 async fn is_self_async(self: Vc<Self>) -> Result<Vc<bool>> {
757 if let Some(async_module) = *self.get_async_module().await? {
758 Ok(async_module.is_self_async(self.references()))
759 } else {
760 Ok(Vc::cell(false))
761 }
762 }
763
764 #[turbo_tasks::function]
765 async fn side_effects(self: Vc<Self>) -> Result<Vc<ModuleSideEffects>> {
766 let this = self.await?;
767 Ok((match *get_side_effect_free_declaration(
770 self.ident().path().owned().await?,
771 this.side_effect_free_packages.map(|g| *g),
772 )
773 .await?
774 {
775 SideEffectsDeclaration::SideEffectful => ModuleSideEffects::SideEffectful,
776 SideEffectsDeclaration::SideEffectFree => ModuleSideEffects::SideEffectFree,
777 SideEffectsDeclaration::None => self.analyze().await?.side_effects,
778 })
779 .cell())
780 }
781}
782
783#[turbo_tasks::value_impl]
784impl ChunkableModule for EcmascriptModuleAsset {
785 #[turbo_tasks::function]
786 async fn as_chunk_item(
787 self: ResolvedVc<Self>,
788 _module_graph: ResolvedVc<ModuleGraph>,
789 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
790 ) -> Vc<Box<dyn ChunkItem>> {
791 Vc::upcast(ModuleChunkItem::cell(ModuleChunkItem {
792 module: self,
793 chunking_context,
794 }))
795 }
796}
797
798#[turbo_tasks::value_impl]
799impl EcmascriptChunkPlaceable for EcmascriptModuleAsset {
800 #[turbo_tasks::function]
801 async fn get_exports(self: Vc<Self>) -> Result<Vc<EcmascriptExports>> {
802 Ok(*self.analyze().await?.exports)
803 }
804
805 #[turbo_tasks::function]
806 async fn get_async_module(self: Vc<Self>) -> Result<Vc<OptionAsyncModule>> {
807 Ok(*self.analyze().await?.async_module)
808 }
809}
810
811#[turbo_tasks::value_impl]
812impl MergeableModule for EcmascriptModuleAsset {
813 #[turbo_tasks::function]
814 async fn is_mergeable(self: ResolvedVc<Self>) -> Result<Vc<bool>> {
815 if matches!(
816 &*self.get_exports().await?,
817 EcmascriptExports::EsmExports(_)
818 ) {
819 return Ok(Vc::cell(true));
820 }
821
822 Ok(Vc::cell(false))
823 }
824
825 #[turbo_tasks::function]
826 async fn merge(
827 self: Vc<Self>,
828 modules: Vc<MergeableModulesExposed>,
829 entry_points: Vc<MergeableModules>,
830 ) -> Result<Vc<Box<dyn ChunkableModule>>> {
831 Ok(Vc::upcast(
832 *MergedEcmascriptModule::new(
833 modules,
834 entry_points,
835 self.options().to_resolved().await?,
836 )
837 .await?,
838 ))
839 }
840}
841
842#[turbo_tasks::value_impl]
843impl EvaluatableAsset for EcmascriptModuleAsset {}
844
845#[turbo_tasks::value_impl]
846impl ResolveOrigin for EcmascriptModuleAsset {
847 #[turbo_tasks::function]
848 fn origin_path(&self) -> Vc<FileSystemPath> {
849 self.source.ident().path()
850 }
851
852 #[turbo_tasks::function]
853 fn asset_context(&self) -> Vc<Box<dyn AssetContext>> {
854 *self.asset_context
855 }
856
857 #[turbo_tasks::function]
858 async fn get_inner_asset(&self, request: Vc<Request>) -> Result<Vc<OptionModule>> {
859 Ok(Vc::cell(if let Some(inner_assets) = &self.inner_assets {
860 if let Some(request) = request.await?.request() {
861 inner_assets.await?.get(&request).copied()
862 } else {
863 None
864 }
865 } else {
866 None
867 }))
868 }
869}
870
871#[turbo_tasks::value]
872struct ModuleChunkItem {
873 module: ResolvedVc<EcmascriptModuleAsset>,
874 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
875}
876
877#[turbo_tasks::value_impl]
878impl OutputAssetsReference for ModuleChunkItem {}
879
880#[turbo_tasks::value_impl]
881impl ChunkItem for ModuleChunkItem {
882 #[turbo_tasks::function]
883 fn asset_ident(&self) -> Vc<AssetIdent> {
884 self.module.ident()
885 }
886
887 #[turbo_tasks::function]
888 fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
889 *self.chunking_context
890 }
891
892 #[turbo_tasks::function]
893 async fn ty(&self) -> Result<Vc<Box<dyn ChunkType>>> {
894 Ok(Vc::upcast(
895 Vc::<EcmascriptChunkType>::default().resolve().await?,
896 ))
897 }
898
899 #[turbo_tasks::function]
900 fn module(&self) -> Vc<Box<dyn Module>> {
901 *ResolvedVc::upcast(self.module)
902 }
903}
904
905#[turbo_tasks::value_impl]
906impl EcmascriptChunkItem for ModuleChunkItem {
907 #[turbo_tasks::function]
908 fn content(self: Vc<Self>) -> Vc<EcmascriptChunkItemContent> {
909 panic!("content() should not be called");
910 }
911
912 #[turbo_tasks::function]
913 async fn content_with_async_module_info(
914 self: Vc<Self>,
915 async_module_info: Option<Vc<AsyncModuleInfo>>,
916 _estimated: bool,
917 ) -> Result<Vc<EcmascriptChunkItemContent>> {
918 let span = tracing::info_span!(
919 "code generation",
920 name = display(self.asset_ident().to_string().await?)
921 );
922 async {
923 let this = self.await?;
924 let async_module_options = this
925 .module
926 .get_async_module()
927 .module_options(async_module_info);
928
929 let content = this
931 .module
932 .module_content(*this.chunking_context, async_module_info);
933
934 EcmascriptChunkItemContent::new(content, *this.chunking_context, async_module_options)
935 .resolve()
936 .await
937 }
938 .instrument(span)
939 .await
940 }
941}
942
943#[turbo_tasks::value(shared)]
945pub struct EcmascriptModuleContent {
946 pub inner_code: Rope,
947 pub source_map: Option<Rope>,
948 pub is_esm: bool,
949 pub strict: bool,
950 pub additional_ids: SmallVec<[ModuleId; 1]>,
951}
952
953#[turbo_tasks::value(shared)]
954#[derive(Clone, Debug, Hash, TaskInput)]
955pub struct EcmascriptModuleContentOptions {
956 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
957 parsed: Option<ResolvedVc<ParseResult>>,
958 specified_module_type: SpecifiedModuleType,
959 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
960 references: ResolvedVc<ModuleReferences>,
961 part_references: Vec<ResolvedVc<EcmascriptModulePartReference>>,
962 esm_references: ResolvedVc<EsmAssetReferences>,
963 code_generation: ResolvedVc<CodeGens>,
964 async_module: ResolvedVc<OptionAsyncModule>,
965 generate_source_map: bool,
966 original_source_map: Option<ResolvedVc<Box<dyn GenerateSourceMap>>>,
967 exports: ResolvedVc<EcmascriptExports>,
968 async_module_info: Option<ResolvedVc<AsyncModuleInfo>>,
969}
970
971impl EcmascriptModuleContentOptions {
972 async fn merged_code_gens(
973 &self,
974 scope_hoisting_context: ScopeHoistingContext<'_>,
975 eval_context: &EvalContext,
976 ) -> Result<Vec<CodeGeneration>> {
977 let EcmascriptModuleContentOptions {
980 module,
981 chunking_context,
982 references,
983 part_references,
984 esm_references,
985 code_generation,
986 async_module,
987 exports,
988 async_module_info,
989 ..
990 } = self;
991
992 async {
993 let additional_code_gens = [
994 if let Some(async_module) = &*async_module.await? {
995 Some(
996 async_module
997 .code_generation(
998 async_module_info.map(|info| *info),
999 **references,
1000 **chunking_context,
1001 )
1002 .await?,
1003 )
1004 } else {
1005 None
1006 },
1007 if let EcmascriptExports::EsmExports(exports) = *exports.await? {
1008 Some(
1009 exports
1010 .code_generation(
1011 **chunking_context,
1012 scope_hoisting_context,
1013 eval_context,
1014 *module,
1015 )
1016 .await?,
1017 )
1018 } else {
1019 None
1020 },
1021 ];
1022
1023 let part_code_gens = part_references
1024 .iter()
1025 .map(|r| r.code_generation(**chunking_context, scope_hoisting_context))
1026 .try_join()
1027 .await?;
1028
1029 let esm_code_gens = esm_references
1030 .await?
1031 .iter()
1032 .map(|r| r.code_generation(**chunking_context, scope_hoisting_context))
1033 .try_join()
1034 .await?;
1035
1036 let code_gens = code_generation
1037 .await?
1038 .iter()
1039 .map(|c| {
1040 c.code_generation(
1041 **chunking_context,
1042 scope_hoisting_context,
1043 *module,
1044 *exports,
1045 )
1046 })
1047 .try_join()
1048 .await?;
1049
1050 anyhow::Ok(
1051 part_code_gens
1052 .into_iter()
1053 .chain(esm_code_gens.into_iter())
1054 .chain(additional_code_gens.into_iter().flatten())
1055 .chain(code_gens.into_iter())
1056 .collect(),
1057 )
1058 }
1059 .instrument(tracing::info_span!("precompute code generation"))
1060 .await
1061 }
1062}
1063
1064#[turbo_tasks::value_impl]
1065impl EcmascriptModuleContent {
1066 #[turbo_tasks::function]
1068 pub async fn new(input: Vc<EcmascriptModuleContentOptions>) -> Result<Vc<Self>> {
1069 let input = input.await?;
1070 let EcmascriptModuleContentOptions {
1071 parsed,
1072 module,
1073 specified_module_type,
1074 generate_source_map,
1075 original_source_map,
1076 chunking_context,
1077 ..
1078 } = &*input;
1079
1080 let minify = chunking_context.minify_type().await?;
1081
1082 let content = process_parse_result(
1083 *parsed,
1084 module.ident(),
1085 *specified_module_type,
1086 *generate_source_map,
1087 *original_source_map,
1088 *minify,
1089 Some(&*input),
1090 None,
1091 )
1092 .await?;
1093 emit_content(content, Default::default()).await
1094 }
1095
1096 #[turbo_tasks::function]
1098 pub async fn new_without_analysis(
1099 parsed: Vc<ParseResult>,
1100 ident: Vc<AssetIdent>,
1101 specified_module_type: SpecifiedModuleType,
1102 generate_source_map: bool,
1103 ) -> Result<Vc<Self>> {
1104 let content = process_parse_result(
1105 Some(parsed.to_resolved().await?),
1106 ident,
1107 specified_module_type,
1108 generate_source_map,
1109 None,
1110 MinifyType::NoMinify,
1111 None,
1112 None,
1113 )
1114 .await?;
1115 emit_content(content, Default::default()).await
1116 }
1117
1118 #[turbo_tasks::function]
1125 pub async fn new_merged(
1126 modules: Vec<(
1127 ResolvedVc<Box<dyn EcmascriptAnalyzable>>,
1128 MergeableModuleExposure,
1129 )>,
1130 module_options: Vec<Vc<EcmascriptModuleContentOptions>>,
1131 entry_points: Vec<ResolvedVc<Box<dyn EcmascriptAnalyzable>>>,
1132 ) -> Result<Vc<Self>> {
1133 async {
1134 let modules = modules
1135 .into_iter()
1136 .map(|(m, exposed)| {
1137 (
1138 ResolvedVc::try_sidecast::<Box<dyn EcmascriptChunkPlaceable>>(m).unwrap(),
1139 exposed,
1140 )
1141 })
1142 .collect::<FxIndexMap<_, _>>();
1143 let entry_points = entry_points
1144 .into_iter()
1145 .map(|m| {
1146 let m =
1147 ResolvedVc::try_sidecast::<Box<dyn EcmascriptChunkPlaceable>>(m).unwrap();
1148 (m, modules.get_index_of(&m).unwrap())
1149 })
1150 .collect::<Vec<_>>();
1151
1152 let globals_merged = Globals::default();
1153
1154 let contents = module_options
1155 .iter()
1156 .map(async |options| {
1157 let options = options.await?;
1158 let EcmascriptModuleContentOptions {
1159 chunking_context,
1160 parsed,
1161 module,
1162 specified_module_type,
1163 generate_source_map,
1164 original_source_map,
1165 ..
1166 } = &*options;
1167
1168 let result = process_parse_result(
1169 *parsed,
1170 module.ident(),
1171 *specified_module_type,
1172 *generate_source_map,
1173 *original_source_map,
1174 *chunking_context.minify_type().await?,
1175 Some(&*options),
1176 Some(ScopeHoistingOptions {
1177 module: *module,
1178 modules: &modules,
1179 }),
1180 )
1181 .await?;
1182
1183 Ok((*module, result))
1184 })
1185 .try_join()
1186 .await?;
1187
1188 let (merged_ast, comments, source_maps, original_source_maps, lookup_table) =
1189 merge_modules(contents, &entry_points, &globals_merged).await?;
1190
1191 let options = module_options.last().unwrap().await?;
1194
1195 let modules_header_width = modules.len().next_power_of_two().trailing_zeros();
1196 let content = CodeGenResult {
1197 program: merged_ast,
1198 source_map: CodeGenResultSourceMap::ScopeHoisting {
1199 modules_header_width,
1200 lookup_table: lookup_table.clone(),
1201 source_maps,
1202 },
1203 comments: CodeGenResultComments::ScopeHoisting {
1204 modules_header_width,
1205 lookup_table,
1206 comments,
1207 },
1208 is_esm: true,
1209 strict: true,
1210 original_source_map: CodeGenResultOriginalSourceMap::ScopeHoisting(
1211 original_source_maps,
1212 ),
1213 minify: *options.chunking_context.minify_type().await?,
1214 scope_hoisting_syntax_contexts: None,
1215 };
1216
1217 let first_entry = entry_points.first().unwrap().0;
1218 let additional_ids = modules
1219 .keys()
1220 .filter(|m| {
1227 **m != first_entry
1228 && *modules.get(*m).unwrap() == MergeableModuleExposure::External
1229 })
1230 .map(|m| m.chunk_item_id(*options.chunking_context))
1231 .try_join()
1232 .await?
1233 .into();
1234
1235 emit_content(content, additional_ids)
1236 .instrument(tracing::info_span!("emit code"))
1237 .await
1238 }
1239 .instrument(tracing::info_span!(
1240 "generate merged code",
1241 modules = module_options.len()
1242 ))
1243 .await
1244 }
1245}
1246
1247#[instrument(level = Level::TRACE, skip_all, name = "merge")]
1256#[allow(clippy::type_complexity)]
1257async fn merge_modules(
1258 mut contents: Vec<(ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, CodeGenResult)>,
1259 entry_points: &Vec<(ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, usize)>,
1260 globals_merged: &'_ Globals,
1261) -> Result<(
1262 Program,
1263 Vec<CodeGenResultComments>,
1264 Vec<CodeGenResultSourceMap>,
1265 SmallVec<[ResolvedVc<Box<dyn GenerateSourceMap>>; 1]>,
1266 Arc<Mutex<Vec<ModulePosition>>>,
1267)> {
1268 struct SetSyntaxContextVisitor<'a> {
1269 modules_header_width: u32,
1270 current_module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1271 current_module_idx: u32,
1272 lookup_table: &'a mut Vec<ModulePosition>,
1273 reverse_module_contexts:
1275 FxHashMap<SyntaxContext, ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
1276 export_contexts:
1279 &'a FxHashMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, &'a FxHashMap<RcStr, Id>>,
1280 unique_contexts_cache: &'a mut FxHashMap<
1283 (ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, SyntaxContext),
1284 SyntaxContext,
1285 >,
1286
1287 error: anyhow::Result<()>,
1288 }
1289
1290 impl<'a> SetSyntaxContextVisitor<'a> {
1291 fn get_context_for(
1292 &mut self,
1293 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1294 local_ctxt: SyntaxContext,
1295 ) -> SyntaxContext {
1296 if let Some(&global_ctxt) = self.unique_contexts_cache.get(&(module, local_ctxt)) {
1297 global_ctxt
1298 } else {
1299 let global_ctxt = SyntaxContext::empty().apply_mark(Mark::new());
1300 self.unique_contexts_cache
1301 .insert((module, local_ctxt), global_ctxt);
1302 global_ctxt
1303 }
1304 }
1305 }
1306
1307 impl VisitMut for SetSyntaxContextVisitor<'_> {
1308 fn visit_mut_ident(&mut self, ident: &mut Ident) {
1309 let Ident {
1310 sym, ctxt, span, ..
1311 } = ident;
1312
1313 if let Some(&module) = self.reverse_module_contexts.get(ctxt) {
1316 let eval_context_exports = self.export_contexts.get(&module).unwrap();
1317 let sym_rc_str: RcStr = sym.as_str().into();
1320 let (local, local_ctxt) = if let Some((local, local_ctxt)) =
1321 eval_context_exports.get(&sym_rc_str)
1322 {
1323 (Some(local), *local_ctxt)
1324 } else if sym.starts_with("__TURBOPACK__imported__module__") {
1325 (None, SyntaxContext::empty())
1330 } else {
1331 self.error = Err(anyhow::anyhow!(
1332 "Expected to find a local export for {sym} with ctxt {ctxt:#?} in \
1333 {eval_context_exports:?}",
1334 ));
1335 return;
1336 };
1337
1338 let global_ctxt = self.get_context_for(module, local_ctxt);
1339
1340 if let Some(local) = local {
1341 *sym = local.clone();
1342 }
1343 *ctxt = global_ctxt;
1344 span.visit_mut_with(self);
1345 } else {
1346 ident.visit_mut_children_with(self);
1347 }
1348 }
1349
1350 fn visit_mut_syntax_context(&mut self, local_ctxt: &mut SyntaxContext) {
1351 let module = self
1354 .reverse_module_contexts
1355 .get(local_ctxt)
1356 .copied()
1357 .unwrap_or(self.current_module);
1358
1359 let global_ctxt = self.get_context_for(module, *local_ctxt);
1360 *local_ctxt = global_ctxt;
1361 }
1362 fn visit_mut_span(&mut self, span: &mut Span) {
1363 span.lo = CodeGenResultComments::encode_bytepos_with_vec(
1366 self.modules_header_width,
1367 self.current_module_idx,
1368 span.lo,
1369 self.lookup_table,
1370 )
1371 .unwrap_or_else(|err| {
1372 self.error = Err(err);
1373 span.lo
1374 });
1375 span.hi = CodeGenResultComments::encode_bytepos_with_vec(
1376 self.modules_header_width,
1377 self.current_module_idx,
1378 span.hi,
1379 self.lookup_table,
1380 )
1381 .unwrap_or_else(|err| {
1382 self.error = Err(err);
1383 span.hi
1384 });
1385 }
1386 }
1387
1388 let mut programs = contents
1391 .iter_mut()
1392 .map(|(_, content)| content.program.take())
1393 .collect::<Vec<_>>();
1394
1395 let export_contexts = contents
1396 .iter()
1397 .map(|(module, content)| {
1398 Ok((
1399 *module,
1400 content
1401 .scope_hoisting_syntax_contexts
1402 .as_ref()
1403 .map(|(_, export_contexts)| export_contexts)
1404 .context("expected exports contexts")?,
1405 ))
1406 })
1407 .collect::<Result<FxHashMap<_, _>>>()?;
1408
1409 let mut lookup_table = Vec::new();
1410 let result = GLOBALS.set(globals_merged, || {
1411 let _ = tracing::trace_span!("merge inner").entered();
1412 let mut unique_contexts_cache =
1414 FxHashMap::with_capacity_and_hasher(contents.len() * 5, Default::default());
1415
1416 let mut prepare_module =
1417 |module_count: usize,
1418 current_module_idx: usize,
1419 (module, content): &(ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, CodeGenResult),
1420 program: &mut Program,
1421 lookup_table: &mut Vec<ModulePosition>| {
1422 let _ = tracing::trace_span!("prepare module").entered();
1423 if let CodeGenResult {
1424 scope_hoisting_syntax_contexts: Some((module_contexts, _)),
1425 ..
1426 } = content
1427 {
1428 let modules_header_width = module_count.next_power_of_two().trailing_zeros();
1429 GLOBALS.set(globals_merged, || {
1430 let mut visitor = SetSyntaxContextVisitor {
1431 modules_header_width,
1432 current_module: *module,
1433 current_module_idx: current_module_idx as u32,
1434 lookup_table,
1435 reverse_module_contexts: module_contexts
1436 .iter()
1437 .map(|e| (*e.value(), *e.key()))
1438 .collect(),
1439 export_contexts: &export_contexts,
1440 unique_contexts_cache: &mut unique_contexts_cache,
1441 error: Ok(()),
1442 };
1443 program.visit_mut_with(&mut visitor);
1444 visitor.error
1445 })?;
1446
1447 Ok(match program.take() {
1448 Program::Module(module) => Either::Left(module.body.into_iter()),
1449 Program::Script(script) => {
1452 Either::Right(script.body.into_iter().map(ModuleItem::Stmt))
1453 }
1454 })
1455 } else {
1456 bail!("Expected scope_hosting_syntax_contexts");
1457 }
1458 };
1459
1460 let mut inserted = FxHashSet::with_capacity_and_hasher(contents.len(), Default::default());
1461 inserted.extend(entry_points.iter().map(|(_, i)| *i));
1463
1464 let mut inserted_imports = FxHashMap::default();
1465
1466 let span = tracing::trace_span!("merge ASTs");
1467 let mut queue = entry_points
1470 .iter()
1471 .map(|&(_, i)| {
1472 prepare_module(
1473 contents.len(),
1474 i,
1475 &contents[i],
1476 &mut programs[i],
1477 &mut lookup_table,
1478 )
1479 .map_err(|err| (i, err))
1480 })
1481 .flatten_ok()
1482 .rev()
1483 .collect::<Result<Vec<_>, _>>()?;
1484 let mut result = vec![];
1485 while let Some(item) = queue.pop() {
1486 if let ModuleItem::Stmt(stmt) = &item {
1487 match stmt {
1488 Stmt::Expr(ExprStmt { expr, .. }) => {
1489 if let Expr::Call(CallExpr {
1490 callee: Callee::Expr(callee),
1491 args,
1492 ..
1493 }) = &**expr
1494 && callee.is_ident_ref_to("__turbopack_merged_esm__")
1495 {
1496 let index =
1497 args[0].expr.as_lit().unwrap().as_num().unwrap().value as usize;
1498
1499 if inserted.insert(index) {
1501 queue.extend(
1502 prepare_module(
1503 contents.len(),
1504 index,
1505 &contents[index],
1506 &mut programs[index],
1507 &mut lookup_table,
1508 )
1509 .map_err(|err| (index, err))?
1510 .into_iter()
1511 .rev(),
1512 );
1513 }
1514 continue;
1515 }
1516 }
1517 Stmt::Decl(Decl::Var(var)) => {
1518 if let [decl] = &*var.decls
1519 && let Some(name) = decl.name.as_ident()
1520 && name.sym.starts_with("__TURBOPACK__imported__module__")
1521 {
1522 match inserted_imports.entry(name.sym.clone()) {
1527 Entry::Occupied(entry) => {
1528 let entry_ctxt = *entry.get();
1533 let new = Ident::new(name.sym.clone(), DUMMY_SP, name.ctxt);
1534 let old = Ident::new(name.sym.clone(), DUMMY_SP, entry_ctxt);
1535 result.push(ModuleItem::Stmt(
1536 quote!("var $new = $old;" as Stmt,
1537 new: Ident = new,
1538 old: Ident = old
1539 ),
1540 ));
1541 continue;
1542 }
1543 Entry::Vacant(entry) => {
1544 entry.insert(name.ctxt);
1545 }
1546 }
1547 }
1548 }
1549 _ => (),
1550 }
1551 }
1552
1553 result.push(item);
1554 }
1555 drop(span);
1556
1557 let span = tracing::trace_span!("hygiene").entered();
1558 let mut merged_ast = Program::Module(swc_core::ecma::ast::Module {
1559 body: result,
1560 span: DUMMY_SP,
1561 shebang: None,
1562 });
1563 merged_ast.visit_mut_with(&mut swc_core::ecma::transforms::base::hygiene::hygiene());
1564 drop(span);
1565
1566 Ok((merged_ast, inserted))
1567 });
1568
1569 let (merged_ast, inserted) = match result {
1570 Ok(v) => v,
1571 Err((content_idx, err)) => {
1572 return Err(err.context(format!(
1573 "Processing {}",
1574 contents[content_idx].0.ident().to_string().await?
1575 )));
1576 }
1577 };
1578
1579 debug_assert!(
1580 inserted.len() == contents.len(),
1581 "Not all merged modules were inserted: {:?}",
1582 contents
1583 .iter()
1584 .enumerate()
1585 .map(async |(i, m)| Ok((inserted.contains(&i), m.0.ident().to_string().await?)))
1586 .try_join()
1587 .await?,
1588 );
1589
1590 let comments = contents
1591 .iter_mut()
1592 .map(|(_, content)| content.comments.take())
1593 .collect::<Vec<_>>();
1594
1595 let source_maps = contents
1596 .iter_mut()
1597 .map(|(_, content)| std::mem::take(&mut content.source_map))
1598 .collect::<Vec<_>>();
1599
1600 let original_source_maps = contents
1601 .iter_mut()
1602 .flat_map(|(_, content)| match content.original_source_map {
1603 CodeGenResultOriginalSourceMap::ScopeHoisting(_) => unreachable!(
1604 "Didn't expect nested CodeGenResultOriginalSourceMap::ScopeHoisting: {:?}",
1605 content.original_source_map
1606 ),
1607 CodeGenResultOriginalSourceMap::Single(map) => map,
1608 })
1609 .collect();
1610
1611 Ok((
1612 merged_ast,
1613 comments,
1614 source_maps,
1615 original_source_maps,
1616 Arc::new(Mutex::new(lookup_table)),
1617 ))
1618}
1619
1620#[derive(Clone, Copy)]
1625pub enum ScopeHoistingContext<'a> {
1626 Some {
1627 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1629 modules:
1631 &'a FxIndexMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, MergeableModuleExposure>,
1632
1633 is_import_mark: Mark,
1634 globals: &'a Arc<Globals>,
1635 module_syntax_contexts_cache:
1637 &'a FxDashMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, SyntaxContext>,
1638 },
1639 None,
1640}
1641
1642impl<'a> ScopeHoistingContext<'a> {
1643 pub fn module(&self) -> Option<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>> {
1645 match self {
1646 ScopeHoistingContext::Some { module, .. } => Some(*module),
1647 ScopeHoistingContext::None => None,
1648 }
1649 }
1650
1651 pub fn skip_module_exports(&self) -> bool {
1653 match self {
1654 ScopeHoistingContext::Some {
1655 module, modules, ..
1656 } => match modules.get(module).unwrap() {
1657 MergeableModuleExposure::None => true,
1658 MergeableModuleExposure::Internal | MergeableModuleExposure::External => false,
1659 },
1660 ScopeHoistingContext::None => false,
1661 }
1662 }
1663
1664 pub fn get_module_syntax_context(
1666 &self,
1667 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1668 ) -> Option<SyntaxContext> {
1669 match self {
1670 ScopeHoistingContext::Some {
1671 modules,
1672 module_syntax_contexts_cache,
1673 globals,
1674 is_import_mark,
1675 ..
1676 } => {
1677 if !modules.contains_key(&module) {
1678 return None;
1679 }
1680
1681 Some(match module_syntax_contexts_cache.entry(module) {
1682 dashmap::Entry::Occupied(e) => *e.get(),
1683 dashmap::Entry::Vacant(e) => {
1684 let ctxt = GLOBALS.set(globals, || {
1685 let mark = Mark::fresh(*is_import_mark);
1686 SyntaxContext::empty()
1687 .apply_mark(*is_import_mark)
1688 .apply_mark(mark)
1689 });
1690
1691 e.insert(ctxt);
1692 ctxt
1693 }
1694 })
1695 }
1696 ScopeHoistingContext::None => None,
1697 }
1698 }
1699
1700 pub fn get_module_index(
1701 &self,
1702 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1703 ) -> Option<usize> {
1704 match self {
1705 ScopeHoistingContext::Some { modules, .. } => modules.get_index_of(&module),
1706 ScopeHoistingContext::None => None,
1707 }
1708 }
1709}
1710
1711struct CodeGenResult {
1712 program: Program,
1713 source_map: CodeGenResultSourceMap,
1714 comments: CodeGenResultComments,
1715 is_esm: bool,
1716 strict: bool,
1717 original_source_map: CodeGenResultOriginalSourceMap,
1718 minify: MinifyType,
1719 #[allow(clippy::type_complexity)]
1720 scope_hoisting_syntax_contexts: Option<(
1722 FxDashMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable + 'static>>, SyntaxContext>,
1723 FxHashMap<RcStr, Id>,
1724 )>,
1725}
1726
1727struct ScopeHoistingOptions<'a> {
1728 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1729 modules: &'a FxIndexMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, MergeableModuleExposure>,
1730}
1731
1732async fn process_parse_result(
1733 parsed: Option<ResolvedVc<ParseResult>>,
1734 ident: Vc<AssetIdent>,
1735 specified_module_type: SpecifiedModuleType,
1736 generate_source_map: bool,
1737 original_source_map: Option<ResolvedVc<Box<dyn GenerateSourceMap>>>,
1738 minify: MinifyType,
1739 options: Option<&EcmascriptModuleContentOptions>,
1740 scope_hoisting_options: Option<ScopeHoistingOptions<'_>>,
1741) -> Result<CodeGenResult> {
1742 with_consumed_parse_result(
1743 parsed,
1744 async |mut program, source_map, globals, eval_context, comments| -> Result<CodeGenResult> {
1745 let (top_level_mark, is_esm, strict) = eval_context
1746 .as_ref()
1747 .map_either(
1748 |e| {
1749 (
1750 e.top_level_mark,
1751 e.is_esm(specified_module_type),
1752 e.imports.strict,
1753 )
1754 },
1755 |e| {
1756 (
1757 e.top_level_mark,
1758 e.is_esm(specified_module_type),
1759 e.imports.strict,
1760 )
1761 },
1762 )
1763 .into_inner();
1764
1765 let (mut code_gens, retain_syntax_context, prepend_ident_comment) =
1766 if let Some(scope_hoisting_options) = scope_hoisting_options {
1767 let is_import_mark = GLOBALS.set(globals, || Mark::new());
1768
1769 let module_syntax_contexts_cache = FxDashMap::default();
1770 let ctx = ScopeHoistingContext::Some {
1771 module: scope_hoisting_options.module,
1772 modules: scope_hoisting_options.modules,
1773 module_syntax_contexts_cache: &module_syntax_contexts_cache,
1774 is_import_mark,
1775 globals,
1776 };
1777 let code_gens = options
1778 .unwrap()
1779 .merged_code_gens(
1780 ctx,
1781 match &eval_context {
1782 Either::Left(e) => e,
1783 Either::Right(e) => e,
1784 },
1785 )
1786 .await?;
1787
1788 let export_contexts = eval_context
1789 .map_either(
1790 |e| Cow::Owned(e.imports.exports),
1791 |e| Cow::Borrowed(&e.imports.exports),
1792 )
1793 .into_inner();
1794 let preserved_exports =
1795 match &*scope_hoisting_options.module.get_exports().await? {
1796 EcmascriptExports::EsmExports(exports) => exports
1797 .await?
1798 .exports
1799 .iter()
1800 .filter(|(_, e)| matches!(e, export::EsmExport::LocalBinding(_, _)))
1801 .map(|(name, e)| {
1802 if let Some((sym, ctxt)) = export_contexts.get(name) {
1803 Ok((sym.clone(), *ctxt))
1804 } else {
1805 bail!("Couldn't find export {} for binding {:?}", name, e);
1806 }
1807 })
1808 .collect::<Result<FxHashSet<_>>>()?,
1809 _ => Default::default(),
1810 };
1811
1812 let prepend_ident_comment = if matches!(minify, MinifyType::NoMinify) {
1813 Some(Comment {
1814 kind: CommentKind::Line,
1815 span: DUMMY_SP,
1816 text: format!(" MERGED MODULE: {}", ident.to_string().await?).into(),
1817 })
1818 } else {
1819 None
1820 };
1821
1822 (
1823 code_gens,
1824 Some((
1825 is_import_mark,
1826 module_syntax_contexts_cache,
1827 preserved_exports,
1828 export_contexts,
1829 )),
1830 prepend_ident_comment,
1831 )
1832 } else if let Some(options) = options {
1833 (
1834 options
1835 .merged_code_gens(
1836 ScopeHoistingContext::None,
1837 match &eval_context {
1838 Either::Left(e) => e,
1839 Either::Right(e) => e,
1840 },
1841 )
1842 .await?,
1843 None,
1844 None,
1845 )
1846 } else {
1847 (vec![], None, None)
1848 };
1849
1850 let extra_comments = SwcComments {
1851 leading: Default::default(),
1852 trailing: Default::default(),
1853 };
1854
1855 process_content_with_code_gens(&mut program, globals, &mut code_gens);
1856
1857 for comments in code_gens.iter_mut().flat_map(|cg| cg.comments.as_mut()) {
1858 let leading = Arc::unwrap_or_clone(take(&mut comments.leading));
1859 let trailing = Arc::unwrap_or_clone(take(&mut comments.trailing));
1860
1861 for (pos, v) in leading {
1862 extra_comments.leading.entry(pos).or_default().extend(v);
1863 }
1864
1865 for (pos, v) in trailing {
1866 extra_comments.trailing.entry(pos).or_default().extend(v);
1867 }
1868 }
1869
1870 GLOBALS.set(globals, || {
1871 if let Some(prepend_ident_comment) = prepend_ident_comment {
1872 let span = Span::dummy_with_cmt();
1873 extra_comments.add_leading(span.lo, prepend_ident_comment);
1874 let stmt = Stmt::Empty(EmptyStmt { span });
1875 match &mut program {
1876 Program::Module(module) => module.body.prepend_stmt(ModuleItem::Stmt(stmt)),
1877 Program::Script(script) => script.body.prepend_stmt(stmt),
1878 }
1879 }
1880
1881 if let Some((is_import_mark, _, preserved_exports, _)) = &retain_syntax_context {
1882 program.visit_mut_with(&mut hygiene_rename_only(
1883 Some(top_level_mark),
1884 *is_import_mark,
1885 preserved_exports,
1886 ));
1887 } else {
1888 program.visit_mut_with(
1889 &mut swc_core::ecma::transforms::base::hygiene::hygiene_with_config(
1890 swc_core::ecma::transforms::base::hygiene::Config {
1891 top_level_mark,
1892 ..Default::default()
1893 },
1894 ),
1895 );
1896 }
1897 program.visit_mut_with(&mut swc_core::ecma::transforms::base::fixer::fixer(None));
1898
1899 remove_shebang(&mut program);
1902 remove_directives(&mut program);
1903 });
1904
1905 Ok(CodeGenResult {
1906 program,
1907 source_map: if generate_source_map {
1908 CodeGenResultSourceMap::Single {
1909 source_map: source_map.clone(),
1910 }
1911 } else {
1912 CodeGenResultSourceMap::None
1913 },
1914 comments: CodeGenResultComments::Single {
1915 comments,
1916 extra_comments,
1917 },
1918 is_esm,
1919 strict,
1920 original_source_map: CodeGenResultOriginalSourceMap::Single(original_source_map),
1921 minify,
1922 scope_hoisting_syntax_contexts: retain_syntax_context
1923 .map(|(_, ctxts, _, export_contexts)| (ctxts, export_contexts.into_owned())),
1925 })
1926 },
1927 async |parse_result| -> Result<CodeGenResult> {
1928 Ok(match parse_result {
1929 ParseResult::Ok { .. } => unreachable!(),
1930 ParseResult::Unparsable { messages } => {
1931 let path = ident.path().to_string().await?;
1932 let error_messages = messages
1933 .as_ref()
1934 .and_then(|m| m.first().map(|f| format!("\n{f}")))
1935 .unwrap_or("".into());
1936 let msg = format!("Could not parse module '{path}'\n{error_messages}");
1937 let body = vec![
1938 quote!(
1939 "const e = new Error($msg);" as Stmt,
1940 msg: Expr = Expr::Lit(msg.into()),
1941 ),
1942 quote!("e.code = 'MODULE_UNPARSABLE';" as Stmt),
1943 quote!("throw e;" as Stmt),
1944 ];
1945
1946 CodeGenResult {
1947 program: Program::Script(Script {
1948 span: DUMMY_SP,
1949 body,
1950 shebang: None,
1951 }),
1952 source_map: CodeGenResultSourceMap::None,
1953 comments: CodeGenResultComments::Empty,
1954 is_esm: false,
1955 strict: false,
1956 original_source_map: CodeGenResultOriginalSourceMap::Single(None),
1957 minify: MinifyType::NoMinify,
1958 scope_hoisting_syntax_contexts: None,
1959 }
1960 }
1961 ParseResult::NotFound => {
1962 let path = ident.path().to_string().await?;
1963 let msg = format!("Could not parse module '{path}', file not found");
1964 let body = vec![
1965 quote!(
1966 "const e = new Error($msg);" as Stmt,
1967 msg: Expr = Expr::Lit(msg.into()),
1968 ),
1969 quote!("e.code = 'MODULE_UNPARSABLE';" as Stmt),
1970 quote!("throw e;" as Stmt),
1971 ];
1972 CodeGenResult {
1973 program: Program::Script(Script {
1974 span: DUMMY_SP,
1975 body,
1976 shebang: None,
1977 }),
1978 source_map: CodeGenResultSourceMap::None,
1979 comments: CodeGenResultComments::Empty,
1980 is_esm: false,
1981 strict: false,
1982 original_source_map: CodeGenResultOriginalSourceMap::Single(None),
1983 minify: MinifyType::NoMinify,
1984 scope_hoisting_syntax_contexts: None,
1985 }
1986 }
1987 })
1988 },
1989 )
1990 .instrument(tracing::trace_span!(
1991 "process parse result",
1992 ident = display(ident.to_string().await?),
1993 ))
1994 .await
1995}
1996
1997async fn with_consumed_parse_result<T>(
1999 parsed: Option<ResolvedVc<ParseResult>>,
2000 success: impl AsyncFnOnce(
2001 Program,
2002 &Arc<SourceMap>,
2003 &Arc<Globals>,
2004 Either<EvalContext, &'_ EvalContext>,
2005 Either<ImmutableComments, Arc<ImmutableComments>>,
2006 ) -> Result<T>,
2007 error: impl AsyncFnOnce(&ParseResult) -> Result<T>,
2008) -> Result<T> {
2009 let Some(parsed) = parsed else {
2010 let globals = Globals::new();
2011 let eval_context = GLOBALS.set(&globals, || EvalContext {
2012 unresolved_mark: Mark::new(),
2013 top_level_mark: Mark::new(),
2014 imports: Default::default(),
2015 force_free_values: Default::default(),
2016 });
2017 return success(
2018 Program::Module(swc_core::ecma::ast::Module::dummy()),
2019 &Default::default(),
2020 &Default::default(),
2021 Either::Left(eval_context),
2022 Either::Left(Default::default()),
2023 )
2024 .await;
2025 };
2026
2027 let parsed = parsed.final_read_hint().await?;
2028 match &*parsed {
2029 ParseResult::Ok { .. } => {
2030 let mut parsed = ReadRef::try_unwrap(parsed);
2031 let (program, source_map, globals, eval_context, comments) = match &mut parsed {
2032 Ok(ParseResult::Ok {
2033 program,
2034 source_map,
2035 globals,
2036 eval_context,
2037 comments,
2038 ..
2039 }) => (
2040 program.take(),
2041 &*source_map,
2042 &*globals,
2043 Either::Left(std::mem::replace(
2044 eval_context,
2045 EvalContext {
2046 unresolved_mark: eval_context.unresolved_mark,
2047 top_level_mark: eval_context.top_level_mark,
2048 imports: Default::default(),
2049 force_free_values: Default::default(),
2050 },
2051 )),
2052 match Arc::try_unwrap(take(comments)) {
2053 Ok(comments) => Either::Left(comments),
2054 Err(comments) => Either::Right(comments),
2055 },
2056 ),
2057 Err(parsed) => {
2058 let ParseResult::Ok {
2059 program,
2060 source_map,
2061 globals,
2062 eval_context,
2063 comments,
2064 ..
2065 } = &**parsed
2066 else {
2067 unreachable!();
2068 };
2069 (
2070 program.clone(),
2071 source_map,
2072 globals,
2073 Either::Right(eval_context),
2074 Either::Right(comments.clone()),
2075 )
2076 }
2077 _ => unreachable!(),
2078 };
2079
2080 success(program, source_map, globals, eval_context, comments).await
2081 }
2082 _ => error(&parsed).await,
2083 }
2084}
2085
2086async fn emit_content(
2087 content: CodeGenResult,
2088 additional_ids: SmallVec<[ModuleId; 1]>,
2089) -> Result<Vc<EcmascriptModuleContent>> {
2090 let CodeGenResult {
2091 program,
2092 source_map,
2093 comments,
2094 is_esm,
2095 strict,
2096 original_source_map,
2097 minify,
2098 scope_hoisting_syntax_contexts: _,
2099 } = content;
2100
2101 let generate_source_map = source_map.is_some();
2102
2103 let source_map_names = if generate_source_map {
2105 let mut collector = IdentCollector::default();
2106 program.visit_with(&mut collector);
2107 collector.into_map()
2108 } else {
2109 Default::default()
2110 };
2111
2112 let mut bytes: Vec<u8> = vec![];
2113 let mut mappings = vec![];
2117
2118 let source_map = Arc::new(source_map);
2119
2120 {
2121 let mut wr = JsWriter::new(
2122 Default::default(),
2124 "\n",
2125 &mut bytes,
2126 generate_source_map.then_some(&mut mappings),
2127 );
2128 if matches!(minify, MinifyType::Minify { .. }) {
2129 wr.set_indent_str("");
2130 }
2131
2132 let comments = comments.consumable();
2133
2134 let mut emitter = Emitter {
2135 cfg: swc_core::ecma::codegen::Config::default(),
2136 cm: source_map.clone(),
2137 comments: Some(&comments as &dyn Comments),
2138 wr,
2139 };
2140
2141 emitter.emit_program(&program)?;
2142 drop(program);
2144 }
2145
2146 let source_map = if generate_source_map {
2147 let original_source_maps = original_source_map
2148 .iter()
2149 .map(|map| map.generate_source_map())
2150 .try_join()
2151 .await?;
2152 let original_source_maps = original_source_maps
2153 .iter()
2154 .filter_map(|map| map.as_content())
2155 .map(|map| map.content())
2156 .collect::<Vec<_>>();
2157
2158 Some(generate_js_source_map(
2159 &*source_map,
2160 mappings,
2161 original_source_maps,
2162 matches!(
2163 original_source_map,
2164 CodeGenResultOriginalSourceMap::Single(_)
2165 ),
2166 true,
2167 source_map_names,
2168 )?)
2169 } else {
2170 None
2171 };
2172
2173 Ok(EcmascriptModuleContent {
2174 inner_code: bytes.into(),
2175 source_map,
2176 is_esm,
2177 strict,
2178 additional_ids,
2179 }
2180 .cell())
2181}
2182
2183#[instrument(level = Level::TRACE, skip_all, name = "apply code generation")]
2184fn process_content_with_code_gens(
2185 program: &mut Program,
2186 globals: &Globals,
2187 code_gens: &mut Vec<CodeGeneration>,
2188) {
2189 let mut visitors = Vec::new();
2190 let mut root_visitors = Vec::new();
2191 let mut early_hoisted_stmts = FxIndexMap::default();
2192 let mut hoisted_stmts = FxIndexMap::default();
2193 let mut early_late_stmts = FxIndexMap::default();
2194 let mut late_stmts = FxIndexMap::default();
2195 for code_gen in code_gens {
2196 for CodeGenerationHoistedStmt { key, stmt } in code_gen.hoisted_stmts.drain(..) {
2197 hoisted_stmts.entry(key).or_insert(stmt);
2198 }
2199 for CodeGenerationHoistedStmt { key, stmt } in code_gen.early_hoisted_stmts.drain(..) {
2200 early_hoisted_stmts.insert(key.clone(), stmt);
2201 }
2202 for CodeGenerationHoistedStmt { key, stmt } in code_gen.late_stmts.drain(..) {
2203 late_stmts.insert(key.clone(), stmt);
2204 }
2205 for CodeGenerationHoistedStmt { key, stmt } in code_gen.early_late_stmts.drain(..) {
2206 early_late_stmts.insert(key.clone(), stmt);
2207 }
2208 for (path, visitor) in &code_gen.visitors {
2209 if path.is_empty() {
2210 root_visitors.push(&**visitor);
2211 } else {
2212 visitors.push((path, &**visitor));
2213 }
2214 }
2215 }
2216
2217 GLOBALS.set(globals, || {
2218 if !visitors.is_empty() {
2219 program.visit_mut_with_ast_path(
2220 &mut ApplyVisitors::new(visitors),
2221 &mut Default::default(),
2222 );
2223 }
2224 for pass in root_visitors {
2225 program.modify(pass);
2226 }
2227 });
2228
2229 match program {
2230 Program::Module(ast::Module { body, .. }) => {
2231 body.splice(
2232 0..0,
2233 early_hoisted_stmts
2234 .into_values()
2235 .chain(hoisted_stmts.into_values())
2236 .map(ModuleItem::Stmt),
2237 );
2238 body.extend(
2239 early_late_stmts
2240 .into_values()
2241 .chain(late_stmts.into_values())
2242 .map(ModuleItem::Stmt),
2243 );
2244 }
2245 Program::Script(Script { body, .. }) => {
2246 body.splice(
2247 0..0,
2248 early_hoisted_stmts
2249 .into_values()
2250 .chain(hoisted_stmts.into_values()),
2251 );
2252 body.extend(
2253 early_late_stmts
2254 .into_values()
2255 .chain(late_stmts.into_values()),
2256 );
2257 }
2258 };
2259}
2260
2261fn hygiene_rename_only(
2268 top_level_mark: Option<Mark>,
2269 is_import_mark: Mark,
2270 preserved_exports: &FxHashSet<Id>,
2271) -> impl VisitMut {
2272 struct HygieneRenamer<'a> {
2273 preserved_exports: &'a FxHashSet<Id>,
2274 is_import_mark: Mark,
2275 }
2276 impl swc_core::ecma::transforms::base::rename::Renamer for HygieneRenamer<'_> {
2278 type Target = Id;
2279
2280 const MANGLE: bool = false;
2281 const RESET_N: bool = true;
2282
2283 fn new_name_for(&self, orig: &Id, n: &mut usize) -> Atom {
2284 let res = if *n == 0 {
2285 orig.0.clone()
2286 } else {
2287 format!("{}{}", orig.0, n).into()
2288 };
2289 *n += 1;
2290 res
2291 }
2292
2293 fn preserve_name(&self, orig: &Id) -> bool {
2294 self.preserved_exports.contains(orig) || orig.1.has_mark(self.is_import_mark)
2295 }
2296 }
2297 swc_core::ecma::transforms::base::rename::renamer_keep_contexts(
2298 swc_core::ecma::transforms::base::hygiene::Config {
2299 top_level_mark: top_level_mark.unwrap_or_default(),
2300 ..Default::default()
2301 },
2302 HygieneRenamer {
2303 preserved_exports,
2304 is_import_mark,
2305 },
2306 )
2307}
2308
2309#[derive(Default)]
2310enum CodeGenResultSourceMap {
2311 #[default]
2312 None,
2314 Single {
2315 source_map: Arc<SourceMap>,
2316 },
2317 ScopeHoisting {
2318 modules_header_width: u32,
2321 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2322 source_maps: Vec<CodeGenResultSourceMap>,
2323 },
2324}
2325
2326impl CodeGenResultSourceMap {
2327 fn is_some(&self) -> bool {
2328 match self {
2329 CodeGenResultSourceMap::None => false,
2330 CodeGenResultSourceMap::Single { .. }
2331 | CodeGenResultSourceMap::ScopeHoisting { .. } => true,
2332 }
2333 }
2334}
2335
2336impl Debug for CodeGenResultSourceMap {
2337 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2338 match self {
2339 CodeGenResultSourceMap::None => write!(f, "CodeGenResultSourceMap::None"),
2340 CodeGenResultSourceMap::Single { source_map } => {
2341 write!(
2342 f,
2343 "CodeGenResultSourceMap::Single {{ source_map: {:?} }}",
2344 source_map.files().clone()
2345 )
2346 }
2347 CodeGenResultSourceMap::ScopeHoisting {
2348 modules_header_width,
2349 source_maps,
2350 ..
2351 } => write!(
2352 f,
2353 "CodeGenResultSourceMap::ScopeHoisting {{ modules_header_width: \
2354 {modules_header_width}, source_maps: {source_maps:?} }}",
2355 ),
2356 }
2357 }
2358}
2359
2360impl Files for CodeGenResultSourceMap {
2361 fn try_lookup_source_file(
2362 &self,
2363 pos: BytePos,
2364 ) -> Result<Option<Arc<SourceFile>>, SourceMapLookupError> {
2365 match self {
2366 CodeGenResultSourceMap::None => Ok(None),
2367 CodeGenResultSourceMap::Single { source_map } => source_map.try_lookup_source_file(pos),
2368 CodeGenResultSourceMap::ScopeHoisting {
2369 modules_header_width,
2370 lookup_table,
2371 source_maps,
2372 } => {
2373 let (module, pos) =
2374 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2375 source_maps[module].try_lookup_source_file(pos)
2376 }
2377 }
2378 }
2379
2380 fn is_in_file(&self, f: &Arc<SourceFile>, raw_pos: BytePos) -> bool {
2381 match self {
2382 CodeGenResultSourceMap::None => false,
2383 CodeGenResultSourceMap::Single { .. } => f.start_pos <= raw_pos && raw_pos < f.end_pos,
2384 CodeGenResultSourceMap::ScopeHoisting { .. } => {
2385 false
2391 }
2392 }
2393 }
2394
2395 fn map_raw_pos(&self, pos: BytePos) -> BytePos {
2396 match self {
2397 CodeGenResultSourceMap::None => BytePos::DUMMY,
2398 CodeGenResultSourceMap::Single { .. } => pos,
2399 CodeGenResultSourceMap::ScopeHoisting {
2400 modules_header_width,
2401 lookup_table,
2402 ..
2403 } => CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table).1,
2404 }
2405 }
2406}
2407
2408impl SourceMapper for CodeGenResultSourceMap {
2409 fn lookup_char_pos(&self, pos: BytePos) -> Loc {
2410 match self {
2411 CodeGenResultSourceMap::None => {
2412 panic!("CodeGenResultSourceMap::None cannot lookup_char_pos")
2413 }
2414 CodeGenResultSourceMap::Single { source_map } => source_map.lookup_char_pos(pos),
2415 CodeGenResultSourceMap::ScopeHoisting {
2416 modules_header_width,
2417 lookup_table,
2418 source_maps,
2419 } => {
2420 let (module, pos) =
2421 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2422 source_maps[module].lookup_char_pos(pos)
2423 }
2424 }
2425 }
2426 fn span_to_lines(&self, sp: Span) -> FileLinesResult {
2427 match self {
2428 CodeGenResultSourceMap::None => {
2429 panic!("CodeGenResultSourceMap::None cannot span_to_lines")
2430 }
2431 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_lines(sp),
2432 CodeGenResultSourceMap::ScopeHoisting {
2433 modules_header_width,
2434 lookup_table,
2435 source_maps,
2436 } => {
2437 let (module, lo) = CodeGenResultComments::decode_bytepos(
2438 *modules_header_width,
2439 sp.lo,
2440 lookup_table,
2441 );
2442 source_maps[module].span_to_lines(Span {
2443 lo,
2444 hi: CodeGenResultComments::decode_bytepos(
2445 *modules_header_width,
2446 sp.hi,
2447 lookup_table,
2448 )
2449 .1,
2450 })
2451 }
2452 }
2453 }
2454 fn span_to_string(&self, sp: Span) -> String {
2455 match self {
2456 CodeGenResultSourceMap::None => {
2457 panic!("CodeGenResultSourceMap::None cannot span_to_string")
2458 }
2459 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_string(sp),
2460 CodeGenResultSourceMap::ScopeHoisting {
2461 modules_header_width,
2462 lookup_table,
2463 source_maps,
2464 } => {
2465 let (module, lo) = CodeGenResultComments::decode_bytepos(
2466 *modules_header_width,
2467 sp.lo,
2468 lookup_table,
2469 );
2470 source_maps[module].span_to_string(Span {
2471 lo,
2472 hi: CodeGenResultComments::decode_bytepos(
2473 *modules_header_width,
2474 sp.hi,
2475 lookup_table,
2476 )
2477 .1,
2478 })
2479 }
2480 }
2481 }
2482 fn span_to_filename(&self, sp: Span) -> Arc<FileName> {
2483 match self {
2484 CodeGenResultSourceMap::None => {
2485 panic!("CodeGenResultSourceMap::None cannot span_to_filename")
2486 }
2487 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_filename(sp),
2488 CodeGenResultSourceMap::ScopeHoisting {
2489 modules_header_width,
2490 lookup_table,
2491 source_maps,
2492 } => {
2493 let (module, lo) = CodeGenResultComments::decode_bytepos(
2494 *modules_header_width,
2495 sp.lo,
2496 lookup_table,
2497 );
2498 source_maps[module].span_to_filename(Span {
2499 lo,
2500 hi: CodeGenResultComments::decode_bytepos(
2501 *modules_header_width,
2502 sp.hi,
2503 lookup_table,
2504 )
2505 .1,
2506 })
2507 }
2508 }
2509 }
2510 fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span> {
2511 match self {
2512 CodeGenResultSourceMap::None => {
2513 panic!("CodeGenResultSourceMap::None cannot merge_spans")
2514 }
2515 CodeGenResultSourceMap::Single { source_map } => source_map.merge_spans(sp_lhs, sp_rhs),
2516 CodeGenResultSourceMap::ScopeHoisting {
2517 modules_header_width,
2518 lookup_table,
2519 source_maps,
2520 } => {
2521 let (module_lhs, lo_lhs) = CodeGenResultComments::decode_bytepos(
2522 *modules_header_width,
2523 sp_lhs.lo,
2524 lookup_table,
2525 );
2526 let (module_rhs, lo_rhs) = CodeGenResultComments::decode_bytepos(
2527 *modules_header_width,
2528 sp_rhs.lo,
2529 lookup_table,
2530 );
2531 if module_lhs != module_rhs {
2532 return None;
2533 }
2534 source_maps[module_lhs].merge_spans(
2535 Span {
2536 lo: lo_lhs,
2537 hi: CodeGenResultComments::decode_bytepos(
2538 *modules_header_width,
2539 sp_lhs.hi,
2540 lookup_table,
2541 )
2542 .1,
2543 },
2544 Span {
2545 lo: lo_rhs,
2546 hi: CodeGenResultComments::decode_bytepos(
2547 *modules_header_width,
2548 sp_rhs.hi,
2549 lookup_table,
2550 )
2551 .1,
2552 },
2553 )
2554 }
2555 }
2556 }
2557 fn call_span_if_macro(&self, sp: Span) -> Span {
2558 match self {
2559 CodeGenResultSourceMap::None => {
2560 panic!("CodeGenResultSourceMap::None cannot call_span_if_macro")
2561 }
2562 CodeGenResultSourceMap::Single { source_map } => source_map.call_span_if_macro(sp),
2563 CodeGenResultSourceMap::ScopeHoisting {
2564 modules_header_width,
2565 lookup_table,
2566 source_maps,
2567 } => {
2568 let (module, lo) = CodeGenResultComments::decode_bytepos(
2569 *modules_header_width,
2570 sp.lo,
2571 lookup_table,
2572 );
2573 source_maps[module].call_span_if_macro(Span {
2574 lo,
2575 hi: CodeGenResultComments::decode_bytepos(
2576 *modules_header_width,
2577 sp.hi,
2578 lookup_table,
2579 )
2580 .1,
2581 })
2582 }
2583 }
2584 }
2585 fn doctest_offset_line(&self, _line: usize) -> usize {
2586 panic!("doctest_offset_line is not implemented for CodeGenResultSourceMap");
2587 }
2588 fn span_to_snippet(&self, sp: Span) -> Result<String, Box<SpanSnippetError>> {
2589 match self {
2590 CodeGenResultSourceMap::None => Err(Box::new(SpanSnippetError::SourceNotAvailable {
2591 filename: FileName::Anon,
2592 })),
2593 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_snippet(sp),
2594 CodeGenResultSourceMap::ScopeHoisting {
2595 modules_header_width,
2596 lookup_table,
2597 source_maps,
2598 } => {
2599 let (module, lo) = CodeGenResultComments::decode_bytepos(
2600 *modules_header_width,
2601 sp.lo,
2602 lookup_table,
2603 );
2604 source_maps[module].span_to_snippet(Span {
2605 lo,
2606 hi: CodeGenResultComments::decode_bytepos(
2607 *modules_header_width,
2608 sp.hi,
2609 lookup_table,
2610 )
2611 .1,
2612 })
2613 }
2614 }
2615 }
2616}
2617impl SourceMapperExt for CodeGenResultSourceMap {
2618 fn get_code_map(&self) -> &dyn SourceMapper {
2619 self
2620 }
2621}
2622
2623#[derive(Debug)]
2624enum CodeGenResultOriginalSourceMap {
2625 Single(Option<ResolvedVc<Box<dyn GenerateSourceMap>>>),
2626 ScopeHoisting(SmallVec<[ResolvedVc<Box<dyn GenerateSourceMap>>; 1]>),
2627}
2628
2629impl CodeGenResultOriginalSourceMap {
2630 fn iter(&self) -> impl Iterator<Item = ResolvedVc<Box<dyn GenerateSourceMap>>> {
2631 match self {
2632 CodeGenResultOriginalSourceMap::Single(map) => Either::Left(map.iter().copied()),
2633 CodeGenResultOriginalSourceMap::ScopeHoisting(maps) => {
2634 Either::Right(maps.iter().copied())
2635 }
2636 }
2637 }
2638}
2639
2640struct ModulePosition(u32, u32);
2642
2643enum CodeGenResultComments {
2644 Single {
2645 comments: Either<ImmutableComments, Arc<ImmutableComments>>,
2646 extra_comments: SwcComments,
2647 },
2648 ScopeHoisting {
2649 modules_header_width: u32,
2652 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2653 comments: Vec<CodeGenResultComments>,
2654 },
2655 Empty,
2656}
2657
2658unsafe impl Send for CodeGenResultComments {}
2659unsafe impl Sync for CodeGenResultComments {}
2660
2661impl CodeGenResultComments {
2662 const CONTINUATION_BIT: u32 = 1 << 31;
2663 const SIGN_EXTENSION_BIT: u32 = 1 << 30;
2664
2665 #[inline]
2666 fn encode_bytepos_impl(
2667 modules_header_width: u32,
2668 module: u32,
2669 pos: BytePos,
2670 push_into_lookup: &mut impl FnMut(u32, u32) -> Result<u32>,
2671 ) -> Result<BytePos> {
2672 if pos.is_dummy() {
2673 return Ok(pos);
2675 }
2676
2677 let header_width = modules_header_width + 2;
2708 let pos_width = 32 - header_width;
2709
2710 let pos = pos.0;
2711
2712 let old_high_bits = pos >> pos_width;
2713 let high_bits_set = if (2u32.pow(header_width) - 1) == old_high_bits {
2714 true
2715 } else if old_high_bits == 0 {
2716 false
2717 } else {
2718 let ix = push_into_lookup(module, pos)?;
2722 assert_eq!(ix & CodeGenResultComments::CONTINUATION_BIT, 0);
2724
2725 return Ok(BytePos(ix | CodeGenResultComments::CONTINUATION_BIT));
2726 };
2727
2728 let pos = pos & !((2u32.pow(header_width) - 1) << pos_width);
2729 let encoded_high_bits = if high_bits_set {
2730 CodeGenResultComments::SIGN_EXTENSION_BIT
2731 } else {
2732 0
2733 };
2734 let encoded_module = module << pos_width;
2735
2736 Ok(BytePos(encoded_module | encoded_high_bits | pos))
2737 }
2738
2739 fn take(&mut self) -> Self {
2740 std::mem::replace(self, CodeGenResultComments::Empty)
2741 }
2742
2743 fn consumable(&self) -> CodeGenResultCommentsConsumable<'_> {
2744 match self {
2745 CodeGenResultComments::Single {
2746 comments,
2747 extra_comments,
2748 } => CodeGenResultCommentsConsumable::Single {
2749 comments: match comments {
2750 Either::Left(comments) => comments.consumable(),
2751 Either::Right(comments) => comments.consumable(),
2752 },
2753 extra_comments,
2754 },
2755 CodeGenResultComments::ScopeHoisting {
2756 modules_header_width,
2757 lookup_table,
2758 comments,
2759 } => CodeGenResultCommentsConsumable::ScopeHoisting {
2760 modules_header_width: *modules_header_width,
2761 lookup_table: lookup_table.clone(),
2762 comments: comments.iter().map(|c| c.consumable()).collect(),
2763 },
2764 CodeGenResultComments::Empty => CodeGenResultCommentsConsumable::Empty,
2765 }
2766 }
2767
2768 fn encode_bytepos(
2769 modules_header_width: u32,
2770 module: u32,
2771 pos: BytePos,
2772 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2773 ) -> Result<BytePos> {
2774 let mut push = |module: u32, pos_u32: u32| -> Result<u32> {
2775 let mut lookup_table = lookup_table
2776 .lock()
2777 .map_err(|_| anyhow!("Failed to grab lock on the index map for byte positions"))?;
2778 let ix = lookup_table.len() as u32;
2779 if ix >= 1 << 30 {
2780 return Err(anyhow!("Too many byte positions being stored"));
2781 }
2782 lookup_table.push(ModulePosition(module, pos_u32));
2783 Ok(ix)
2784 };
2785 Self::encode_bytepos_impl(modules_header_width, module, pos, &mut push)
2786 }
2787
2788 fn encode_bytepos_with_vec(
2789 modules_header_width: u32,
2790 module: u32,
2791 pos: BytePos,
2792 lookup_table: &mut Vec<ModulePosition>,
2793 ) -> Result<BytePos> {
2794 let mut push = |module: u32, pos_u32: u32| -> Result<u32> {
2795 let ix = lookup_table.len() as u32;
2796 if ix >= 1 << 30 {
2797 return Err(anyhow!("Too many byte positions being stored"));
2798 }
2799 lookup_table.push(ModulePosition(module, pos_u32));
2800 Ok(ix)
2801 };
2802 Self::encode_bytepos_impl(modules_header_width, module, pos, &mut push)
2803 }
2804
2805 fn decode_bytepos(
2806 modules_header_width: u32,
2807 pos: BytePos,
2808 lookup_table: &Mutex<Vec<ModulePosition>>,
2809 ) -> (usize, BytePos) {
2810 if pos.is_dummy() {
2811 panic!("Cannot decode dummy BytePos");
2813 }
2814
2815 let header_width = modules_header_width + 2;
2816 let pos_width = 32 - header_width;
2817
2818 if (CodeGenResultComments::CONTINUATION_BIT & pos.0)
2819 == CodeGenResultComments::CONTINUATION_BIT
2820 {
2821 let lookup_table = lookup_table
2822 .lock()
2823 .expect("Failed to grab lock on the index map for byte position");
2824 let ix = pos.0 & !CodeGenResultComments::CONTINUATION_BIT;
2825 let ModulePosition(module, pos) = lookup_table[ix as usize];
2826
2827 return (module as usize, BytePos(pos));
2828 }
2829
2830 let high_bits_set = pos.0 >> 30 & 1 == 1;
2831 let module = (pos.0 << 2) >> (pos_width + 2);
2832 let pos = pos.0 & !((2u32.pow(header_width) - 1) << pos_width);
2833 let pos = if high_bits_set {
2834 pos | ((2u32.pow(header_width) - 1) << pos_width)
2835 } else {
2836 pos
2837 };
2838 (module as usize, BytePos(pos))
2839 }
2840}
2841
2842enum CodeGenResultCommentsConsumable<'a> {
2843 Single {
2844 comments: CowComments<'a>,
2845 extra_comments: &'a SwcComments,
2846 },
2847 ScopeHoisting {
2848 modules_header_width: u32,
2849 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2850 comments: Vec<CodeGenResultCommentsConsumable<'a>>,
2851 },
2852 Empty,
2853}
2854fn encode_module_into_comment_span(
2858 modules_header_width: u32,
2859 module: usize,
2860 mut comment: Comment,
2861 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2862) -> Comment {
2863 comment.span.lo = CodeGenResultComments::encode_bytepos(
2864 modules_header_width,
2865 module as u32,
2866 comment.span.lo,
2867 lookup_table.clone(),
2868 )
2869 .unwrap();
2870 comment.span.hi = CodeGenResultComments::encode_bytepos(
2871 modules_header_width,
2872 module as u32,
2873 comment.span.hi,
2874 lookup_table,
2875 )
2876 .unwrap();
2877 comment
2878}
2879
2880impl Comments for CodeGenResultCommentsConsumable<'_> {
2881 fn add_leading(&self, _pos: BytePos, _cmt: Comment) {
2882 unimplemented!("add_leading")
2883 }
2884
2885 fn add_leading_comments(&self, _pos: BytePos, _comments: Vec<Comment>) {
2886 unimplemented!("add_leading_comments")
2887 }
2888
2889 fn has_leading(&self, pos: BytePos) -> bool {
2890 if pos.is_dummy() {
2891 return false;
2892 }
2893 match self {
2894 Self::Single {
2895 comments,
2896 extra_comments,
2897 } => comments.has_leading(pos) || extra_comments.has_leading(pos),
2898 Self::ScopeHoisting {
2899 modules_header_width,
2900 lookup_table,
2901 comments,
2902 } => {
2903 let (module, pos) =
2904 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2905 comments[module].has_leading(pos)
2906 }
2907 Self::Empty => false,
2908 }
2909 }
2910
2911 fn move_leading(&self, _from: BytePos, _to: BytePos) {
2912 unimplemented!("move_leading")
2913 }
2914
2915 fn take_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
2916 if pos.is_dummy() {
2917 return None;
2918 }
2919 match self {
2920 Self::Single {
2921 comments,
2922 extra_comments,
2923 } => merge_option_vec(comments.take_leading(pos), extra_comments.take_leading(pos)),
2924 Self::ScopeHoisting {
2925 modules_header_width,
2926 lookup_table,
2927 comments,
2928 } => {
2929 let (module, pos) =
2930 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2931 comments[module].take_leading(pos).map(|comments| {
2932 comments
2933 .into_iter()
2934 .map(|c| {
2935 encode_module_into_comment_span(
2936 *modules_header_width,
2937 module,
2938 c,
2939 lookup_table.clone(),
2940 )
2941 })
2942 .collect()
2943 })
2944 }
2945 Self::Empty => None,
2946 }
2947 }
2948
2949 fn get_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
2950 if pos.is_dummy() {
2951 return None;
2952 }
2953 match self {
2954 Self::Single {
2955 comments,
2956 extra_comments,
2957 } => merge_option_vec(comments.get_leading(pos), extra_comments.get_leading(pos)),
2958 Self::ScopeHoisting {
2959 modules_header_width,
2960 lookup_table,
2961 comments,
2962 } => {
2963 let (module, pos) =
2964 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2965 comments[module].get_leading(pos).map(|comments| {
2966 comments
2967 .into_iter()
2968 .map(|c| {
2969 encode_module_into_comment_span(
2970 *modules_header_width,
2971 module,
2972 c,
2973 lookup_table.clone(),
2974 )
2975 })
2976 .collect()
2977 })
2978 }
2979 Self::Empty => None,
2980 }
2981 }
2982
2983 fn add_trailing(&self, _pos: BytePos, _cmt: Comment) {
2984 unimplemented!("add_trailing")
2985 }
2986
2987 fn add_trailing_comments(&self, _pos: BytePos, _comments: Vec<Comment>) {
2988 unimplemented!("add_trailing_comments")
2989 }
2990
2991 fn has_trailing(&self, pos: BytePos) -> bool {
2992 if pos.is_dummy() {
2993 return false;
2994 }
2995 match self {
2996 Self::Single {
2997 comments,
2998 extra_comments,
2999 } => comments.has_trailing(pos) || extra_comments.has_trailing(pos),
3000 Self::ScopeHoisting {
3001 modules_header_width,
3002 lookup_table,
3003 comments,
3004 } => {
3005 let (module, pos) =
3006 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
3007 comments[module].has_trailing(pos)
3008 }
3009 Self::Empty => false,
3010 }
3011 }
3012
3013 fn move_trailing(&self, _from: BytePos, _to: BytePos) {
3014 unimplemented!("move_trailing")
3015 }
3016
3017 fn take_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
3018 if pos.is_dummy() {
3019 return None;
3020 }
3021 match self {
3022 Self::Single {
3023 comments,
3024 extra_comments,
3025 } => merge_option_vec(
3026 comments.take_trailing(pos),
3027 extra_comments.take_trailing(pos),
3028 ),
3029 Self::ScopeHoisting {
3030 modules_header_width,
3031 lookup_table,
3032 comments,
3033 } => {
3034 let (module, pos) =
3035 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
3036 comments[module].take_trailing(pos).map(|comments| {
3037 comments
3038 .into_iter()
3039 .map(|c| {
3040 encode_module_into_comment_span(
3041 *modules_header_width,
3042 module,
3043 c,
3044 lookup_table.clone(),
3045 )
3046 })
3047 .collect()
3048 })
3049 }
3050 Self::Empty => None,
3051 }
3052 }
3053
3054 fn get_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
3055 if pos.is_dummy() {
3056 return None;
3057 }
3058 match self {
3059 Self::Single {
3060 comments,
3061 extra_comments,
3062 } => merge_option_vec(comments.get_leading(pos), extra_comments.get_leading(pos)),
3063 Self::ScopeHoisting {
3064 modules_header_width,
3065 lookup_table,
3066 comments,
3067 } => {
3068 let (module, pos) =
3069 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
3070 comments[module].get_leading(pos).map(|comments| {
3071 comments
3072 .into_iter()
3073 .map(|c| {
3074 encode_module_into_comment_span(
3075 *modules_header_width,
3076 module,
3077 c,
3078 lookup_table.clone(),
3079 )
3080 })
3081 .collect()
3082 })
3083 }
3084 Self::Empty => None,
3085 }
3086 }
3087
3088 fn add_pure_comment(&self, _pos: BytePos) {
3089 unimplemented!("add_pure_comment")
3090 }
3091}
3092
3093fn merge_option_vec<T>(a: Option<Vec<T>>, b: Option<Vec<T>>) -> Option<Vec<T>> {
3094 match (a, b) {
3095 (Some(a), Some(b)) => Some(a.into_iter().chain(b).collect()),
3096 (Some(a), None) => Some(a),
3097 (None, Some(b)) => Some(b),
3098 (None, None) => None,
3099 }
3100}
3101
3102#[cfg(test)]
3103mod tests {
3104 use super::*;
3105 fn bytepos_ensure_identical(modules_header_width: u32, pos: BytePos) {
3106 let module_count = 2u32.pow(modules_header_width);
3107 let lookup_table = Arc::new(Mutex::new(Vec::new()));
3108
3109 for module in [
3110 0,
3111 1,
3112 2,
3113 module_count / 2,
3114 module_count.wrapping_sub(5),
3115 module_count.wrapping_sub(1),
3116 ]
3117 .into_iter()
3118 .filter(|&m| m < module_count)
3119 {
3120 let encoded = CodeGenResultComments::encode_bytepos(
3121 modules_header_width,
3122 module,
3123 pos,
3124 lookup_table.clone(),
3125 )
3126 .unwrap();
3127 let (decoded_module, decoded_pos) =
3128 CodeGenResultComments::decode_bytepos(modules_header_width, encoded, &lookup_table);
3129 assert_eq!(
3130 decoded_module as u32, module,
3131 "Testing width {modules_header_width} and pos {pos:?}"
3132 );
3133 assert_eq!(
3134 decoded_pos, pos,
3135 "Testing width {modules_header_width} and pos {pos:?}"
3136 );
3137 }
3138 }
3139
3140 #[test]
3141 fn test_encode_decode_bytepos_format() {
3142 let table = Arc::new(Mutex::new(Vec::new()));
3143
3144 for (pos, module, modules_header_width, result) in [
3145 (
3146 0b00000000000000000000000000000101,
3147 0b1,
3148 1,
3149 0b00100000000000000000000000000101,
3150 ),
3151 (
3152 0b00000000000000000000000000000101,
3153 0b01,
3154 2,
3155 0b00010000000000000000000000000101,
3156 ),
3157 (
3158 0b11111111111111110000000000000101,
3159 0b0110,
3160 4,
3161 0b01011011111111110000000000000101,
3162 ),
3163 (
3164 BytePos::PLACEHOLDER.0,
3165 0b01111,
3166 5,
3167 0b01011111111111111111111111111101,
3168 ),
3169 (
3170 BytePos::PURE.0,
3171 0b01111,
3172 5,
3173 0b01011111111111111111111111111110,
3174 ),
3175 (
3176 BytePos::SYNTHESIZED.0,
3177 0b01111,
3178 5,
3179 0b01011111111111111111111111111111,
3180 ),
3181 (
3184 0b00000111111111110000000000000101,
3185 0b0001,
3186 4,
3187 0b10000000000000000000000000000000,
3188 ),
3189 (
3191 0b00000111111111110000000000111110,
3192 0b0001,
3193 4,
3194 0b10000000000000000000000000000001,
3195 ),
3196 (BytePos::DUMMY.0, 0b0001, 4, BytePos::DUMMY.0),
3198 ] {
3199 let encoded = CodeGenResultComments::encode_bytepos(
3200 modules_header_width,
3201 module,
3202 BytePos(pos),
3203 table.clone(),
3204 )
3205 .unwrap();
3206 assert_eq!(encoded.0, result);
3207
3208 if encoded.0 & CodeGenResultComments::CONTINUATION_BIT
3210 == CodeGenResultComments::CONTINUATION_BIT
3211 {
3212 let index = encoded.0 & !CodeGenResultComments::CONTINUATION_BIT;
3213 let ModulePosition(encoded_module, encoded_pos) =
3214 table.lock().unwrap()[index as usize];
3215 assert_eq!(encoded_module, module);
3216 assert_eq!(encoded_pos, pos);
3217 }
3218 }
3219 }
3220
3221 #[test]
3222 fn test_encode_decode_bytepos_lossless() {
3223 const DUMMY_RESERVE: u32 = u32::MAX - 2_u32.pow(16);
3225
3226 for modules_header_width in 1..=10 {
3227 for pos in [
3228 BytePos(1),
3230 BytePos(2),
3231 BytePos(100),
3232 BytePos(4_000_000),
3233 BytePos(600_000_000),
3234 BytePos(u32::MAX - 3), BytePos::PLACEHOLDER,
3236 BytePos::SYNTHESIZED,
3237 BytePos::PURE,
3238 BytePos(DUMMY_RESERVE),
3239 BytePos(DUMMY_RESERVE + 10),
3240 BytePos(DUMMY_RESERVE + 10000),
3241 ] {
3242 bytepos_ensure_identical(modules_header_width, pos);
3243 }
3244 }
3245 }
3246}