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