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