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