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?),
1525 );
1526 }
1527 };
1528
1529 if cfg!(debug_assertions) && inserted.len() != contents.len() {
1530 bail!(
1531 "Not all merged modules were inserted: {:?}",
1532 contents
1533 .iter()
1534 .enumerate()
1535 .map(async |(i, m)| Ok((inserted.contains(&i), m.0.ident().to_string().await?)))
1536 .try_join()
1537 .await?,
1538 );
1539 }
1540
1541 let comments = contents
1542 .iter_mut()
1543 .map(|(_, content)| content.comments.take())
1544 .collect::<Vec<_>>();
1545
1546 let source_maps = contents
1547 .iter_mut()
1548 .map(|(_, content)| std::mem::take(&mut content.source_map))
1549 .collect::<Vec<_>>();
1550
1551 let original_source_maps = contents
1552 .iter_mut()
1553 .flat_map(|(_, content)| match content.original_source_map {
1554 CodeGenResultOriginalSourceMap::ScopeHoisting(_) => unreachable!(
1555 "Didn't expect nested CodeGenResultOriginalSourceMap::ScopeHoisting: {:?}",
1556 content.original_source_map
1557 ),
1558 CodeGenResultOriginalSourceMap::Single(map) => map,
1559 })
1560 .collect();
1561
1562 Ok((
1563 merged_ast,
1564 comments,
1565 source_maps,
1566 original_source_maps,
1567 Arc::new(Mutex::new(lookup_table)),
1568 ))
1569}
1570
1571#[derive(Clone, Copy)]
1576pub enum ScopeHoistingContext<'a> {
1577 Some {
1578 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1580 modules:
1582 &'a FxIndexMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, MergeableModuleExposure>,
1583
1584 is_import_mark: Mark,
1585 globals: &'a Arc<Globals>,
1586 module_syntax_contexts_cache:
1588 &'a FxDashMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, SyntaxContext>,
1589 },
1590 None,
1591}
1592
1593impl<'a> ScopeHoistingContext<'a> {
1594 pub fn module(&self) -> Option<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>> {
1596 match self {
1597 ScopeHoistingContext::Some { module, .. } => Some(*module),
1598 ScopeHoistingContext::None => None,
1599 }
1600 }
1601
1602 pub fn skip_module_exports(&self) -> bool {
1604 match self {
1605 ScopeHoistingContext::Some {
1606 module, modules, ..
1607 } => match modules.get(module).unwrap() {
1608 MergeableModuleExposure::None => true,
1609 MergeableModuleExposure::Internal | MergeableModuleExposure::External => false,
1610 },
1611 ScopeHoistingContext::None => false,
1612 }
1613 }
1614
1615 pub fn get_module_syntax_context(
1617 &self,
1618 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1619 ) -> Option<SyntaxContext> {
1620 match self {
1621 ScopeHoistingContext::Some {
1622 modules,
1623 module_syntax_contexts_cache,
1624 globals,
1625 is_import_mark,
1626 ..
1627 } => {
1628 if !modules.contains_key(&module) {
1629 return None;
1630 }
1631
1632 Some(match module_syntax_contexts_cache.entry(module) {
1633 dashmap::Entry::Occupied(e) => *e.get(),
1634 dashmap::Entry::Vacant(e) => {
1635 let ctxt = GLOBALS.set(globals, || {
1636 let mark = Mark::fresh(*is_import_mark);
1637 SyntaxContext::empty()
1638 .apply_mark(*is_import_mark)
1639 .apply_mark(mark)
1640 });
1641
1642 e.insert(ctxt);
1643 ctxt
1644 }
1645 })
1646 }
1647 ScopeHoistingContext::None => None,
1648 }
1649 }
1650
1651 pub fn get_module_index(
1652 &self,
1653 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1654 ) -> Option<usize> {
1655 match self {
1656 ScopeHoistingContext::Some { modules, .. } => modules.get_index_of(&module),
1657 ScopeHoistingContext::None => None,
1658 }
1659 }
1660}
1661
1662struct CodeGenResult {
1663 program: Program,
1664 source_map: CodeGenResultSourceMap,
1665 comments: CodeGenResultComments,
1666 is_esm: bool,
1667 strict: bool,
1668 original_source_map: CodeGenResultOriginalSourceMap,
1669 minify: MinifyType,
1670 #[allow(clippy::type_complexity)]
1671 scope_hoisting_syntax_contexts: Option<(
1673 FxDashMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable + 'static>>, SyntaxContext>,
1674 FxHashMap<RcStr, Id>,
1675 )>,
1676}
1677
1678struct ScopeHoistingOptions<'a> {
1679 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1680 modules: &'a FxIndexMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, MergeableModuleExposure>,
1681}
1682
1683async fn process_parse_result(
1684 parsed: Option<ResolvedVc<ParseResult>>,
1685 ident: Vc<AssetIdent>,
1686 specified_module_type: SpecifiedModuleType,
1687 generate_source_map: bool,
1688 original_source_map: Option<ResolvedVc<Box<dyn GenerateSourceMap>>>,
1689 minify: MinifyType,
1690 options: Option<&EcmascriptModuleContentOptions>,
1691 scope_hoisting_options: Option<ScopeHoistingOptions<'_>>,
1692) -> Result<CodeGenResult> {
1693 with_consumed_parse_result(
1694 parsed,
1695 async |mut program, source_map, globals, eval_context, comments| -> Result<CodeGenResult> {
1696 let (top_level_mark, is_esm, strict) = eval_context
1697 .as_ref()
1698 .map_either(
1699 |e| {
1700 (
1701 e.top_level_mark,
1702 e.is_esm(specified_module_type),
1703 e.imports.strict,
1704 )
1705 },
1706 |e| {
1707 (
1708 e.top_level_mark,
1709 e.is_esm(specified_module_type),
1710 e.imports.strict,
1711 )
1712 },
1713 )
1714 .into_inner();
1715
1716 let (mut code_gens, retain_syntax_context, prepend_ident_comment) =
1717 if let Some(scope_hoisting_options) = scope_hoisting_options {
1718 let is_import_mark = GLOBALS.set(globals, || Mark::new());
1719
1720 let module_syntax_contexts_cache = FxDashMap::default();
1721 let ctx = ScopeHoistingContext::Some {
1722 module: scope_hoisting_options.module,
1723 modules: scope_hoisting_options.modules,
1724 module_syntax_contexts_cache: &module_syntax_contexts_cache,
1725 is_import_mark,
1726 globals,
1727 };
1728 let code_gens = options
1729 .unwrap()
1730 .merged_code_gens(
1731 ctx,
1732 match &eval_context {
1733 Either::Left(e) => e,
1734 Either::Right(e) => e,
1735 },
1736 )
1737 .await?;
1738
1739 let export_contexts = eval_context
1740 .map_either(
1741 |e| Cow::Owned(e.imports.exports),
1742 |e| Cow::Borrowed(&e.imports.exports),
1743 )
1744 .into_inner();
1745 let preserved_exports =
1746 match &*scope_hoisting_options.module.get_exports().await? {
1747 EcmascriptExports::EsmExports(exports) => exports
1748 .await?
1749 .exports
1750 .iter()
1751 .filter(|(_, e)| matches!(e, export::EsmExport::LocalBinding(_, _)))
1752 .map(|(name, e)| {
1753 if let Some((sym, ctxt)) = export_contexts.get(name) {
1754 Ok((sym.clone(), *ctxt))
1755 } else {
1756 bail!("Couldn't find export {} for binding {:?}", name, e);
1757 }
1758 })
1759 .collect::<Result<FxHashSet<_>>>()?,
1760 _ => Default::default(),
1761 };
1762
1763 let prepend_ident_comment = if matches!(minify, MinifyType::NoMinify) {
1764 Some(Comment {
1765 kind: CommentKind::Line,
1766 span: DUMMY_SP,
1767 text: (&*turbofmt!(" MERGED MODULE: {}", ident).await?).into(),
1768 })
1769 } else {
1770 None
1771 };
1772
1773 (
1774 code_gens,
1775 Some((
1776 is_import_mark,
1777 module_syntax_contexts_cache,
1778 preserved_exports,
1779 export_contexts,
1780 )),
1781 prepend_ident_comment,
1782 )
1783 } else if let Some(options) = options {
1784 (
1785 options
1786 .merged_code_gens(
1787 ScopeHoistingContext::None,
1788 match &eval_context {
1789 Either::Left(e) => e,
1790 Either::Right(e) => e,
1791 },
1792 )
1793 .await?,
1794 None,
1795 None,
1796 )
1797 } else {
1798 (vec![], None, None)
1799 };
1800
1801 let extra_comments = SwcComments {
1802 leading: Default::default(),
1803 trailing: Default::default(),
1804 };
1805
1806 process_content_with_code_gens(&mut program, globals, &mut code_gens);
1807
1808 for comments in code_gens.iter_mut().flat_map(|cg| cg.comments.as_mut()) {
1809 let leading = Arc::unwrap_or_clone(take(&mut comments.leading));
1810 let trailing = Arc::unwrap_or_clone(take(&mut comments.trailing));
1811
1812 for (pos, v) in leading {
1813 extra_comments.leading.entry(pos).or_default().extend(v);
1814 }
1815
1816 for (pos, v) in trailing {
1817 extra_comments.trailing.entry(pos).or_default().extend(v);
1818 }
1819 }
1820
1821 GLOBALS.set(globals, || {
1822 if let Some(prepend_ident_comment) = prepend_ident_comment {
1823 let span = Span::dummy_with_cmt();
1824 extra_comments.add_leading(span.lo, prepend_ident_comment);
1825 let stmt = Stmt::Empty(EmptyStmt { span });
1826 match &mut program {
1827 Program::Module(module) => module.body.prepend_stmt(ModuleItem::Stmt(stmt)),
1828 Program::Script(script) => script.body.prepend_stmt(stmt),
1829 }
1830 }
1831
1832 if let Some((is_import_mark, _, preserved_exports, _)) = &retain_syntax_context {
1833 program.visit_mut_with(&mut hygiene_rename_only(
1834 Some(top_level_mark),
1835 *is_import_mark,
1836 preserved_exports,
1837 ));
1838 } else {
1839 program.visit_mut_with(
1840 &mut swc_core::ecma::transforms::base::hygiene::hygiene_with_config(
1841 swc_core::ecma::transforms::base::hygiene::Config {
1842 top_level_mark,
1843 ..Default::default()
1844 },
1845 ),
1846 );
1847 }
1848 program.visit_mut_with(&mut swc_core::ecma::transforms::base::fixer::fixer(None));
1849
1850 remove_shebang(&mut program);
1853 remove_directives(&mut program);
1854 });
1855
1856 Ok(CodeGenResult {
1857 program,
1858 source_map: if generate_source_map {
1859 CodeGenResultSourceMap::Single {
1860 source_map: source_map.clone(),
1861 }
1862 } else {
1863 CodeGenResultSourceMap::None
1864 },
1865 comments: CodeGenResultComments::Single {
1866 comments,
1867 extra_comments,
1868 },
1869 is_esm,
1870 strict,
1871 original_source_map: CodeGenResultOriginalSourceMap::Single(original_source_map),
1872 minify,
1873 scope_hoisting_syntax_contexts: retain_syntax_context
1874 .map(|(_, ctxts, _, export_contexts)| (ctxts, export_contexts.into_owned())),
1876 })
1877 },
1878 async |parse_result| -> Result<CodeGenResult> {
1879 Ok(match parse_result {
1880 ParseResult::Ok { .. } => unreachable!(),
1881 ParseResult::Unparsable { messages } => {
1882 let error_messages = messages
1883 .as_ref()
1884 .and_then(|m| m.first().map(|f| format!("\n{f}")))
1885 .unwrap_or("".into());
1886 let msg = &*turbofmt!(
1887 "Could not parse module '{}'\n{error_messages}",
1888 ident.path()
1889 )
1890 .await?;
1891 let body = vec![
1892 quote!(
1893 "const e = new Error($msg);" as Stmt,
1894 msg: Expr = Expr::Lit(msg.into()),
1895 ),
1896 quote!("e.code = 'MODULE_UNPARSABLE';" as Stmt),
1897 quote!("throw e;" as Stmt),
1898 ];
1899
1900 CodeGenResult {
1901 program: Program::Script(Script {
1902 span: DUMMY_SP,
1903 body,
1904 shebang: None,
1905 }),
1906 source_map: CodeGenResultSourceMap::None,
1907 comments: CodeGenResultComments::Empty,
1908 is_esm: false,
1909 strict: false,
1910 original_source_map: CodeGenResultOriginalSourceMap::Single(None),
1911 minify: MinifyType::NoMinify,
1912 scope_hoisting_syntax_contexts: None,
1913 }
1914 }
1915 ParseResult::NotFound => {
1916 let msg =
1917 &*turbofmt!("Could not parse module '{}', file not found", ident.path())
1918 .await?;
1919 let body = vec![
1920 quote!(
1921 "const e = new Error($msg);" as Stmt,
1922 msg: Expr = Expr::Lit(msg.into()),
1923 ),
1924 quote!("e.code = 'MODULE_UNPARSABLE';" as Stmt),
1925 quote!("throw e;" as Stmt),
1926 ];
1927 CodeGenResult {
1928 program: Program::Script(Script {
1929 span: DUMMY_SP,
1930 body,
1931 shebang: None,
1932 }),
1933 source_map: CodeGenResultSourceMap::None,
1934 comments: CodeGenResultComments::Empty,
1935 is_esm: false,
1936 strict: false,
1937 original_source_map: CodeGenResultOriginalSourceMap::Single(None),
1938 minify: MinifyType::NoMinify,
1939 scope_hoisting_syntax_contexts: None,
1940 }
1941 }
1942 })
1943 },
1944 )
1945 .instrument(tracing::trace_span!(
1946 "process parse result",
1947 ident = display(ident.to_string().await?),
1948 ))
1949 .await
1950}
1951
1952async fn with_consumed_parse_result<T>(
1954 parsed: Option<ResolvedVc<ParseResult>>,
1955 success: impl AsyncFnOnce(
1956 Program,
1957 &Arc<SourceMap>,
1958 &Arc<Globals>,
1959 Either<EvalContext, &'_ EvalContext>,
1960 Either<ImmutableComments, Arc<ImmutableComments>>,
1961 ) -> Result<T>,
1962 error: impl AsyncFnOnce(&ParseResult) -> Result<T>,
1963) -> Result<T> {
1964 let Some(parsed) = parsed else {
1965 let globals = Globals::new();
1966 let eval_context = GLOBALS.set(&globals, || EvalContext {
1967 unresolved_mark: Mark::new(),
1968 top_level_mark: Mark::new(),
1969 imports: Default::default(),
1970 force_free_values: Default::default(),
1971 });
1972 return success(
1973 Program::Module(swc_core::ecma::ast::Module::dummy()),
1974 &Default::default(),
1975 &Default::default(),
1976 Either::Left(eval_context),
1977 Either::Left(Default::default()),
1978 )
1979 .await;
1980 };
1981
1982 let parsed = parsed.final_read_hint().await?;
1983 match &*parsed {
1984 ParseResult::Ok { .. } => {
1985 let mut parsed = ReadRef::try_unwrap(parsed);
1986 let (program, source_map, globals, eval_context, comments) = match &mut parsed {
1987 Ok(ParseResult::Ok {
1988 program,
1989 source_map,
1990 globals,
1991 eval_context,
1992 comments,
1993 ..
1994 }) => (
1995 program.take(),
1996 &*source_map,
1997 &*globals,
1998 Either::Left(std::mem::replace(
1999 eval_context,
2000 EvalContext {
2001 unresolved_mark: eval_context.unresolved_mark,
2002 top_level_mark: eval_context.top_level_mark,
2003 imports: Default::default(),
2004 force_free_values: Default::default(),
2005 },
2006 )),
2007 match Arc::try_unwrap(take(comments)) {
2008 Ok(comments) => Either::Left(comments),
2009 Err(comments) => Either::Right(comments),
2010 },
2011 ),
2012 Err(parsed) => {
2013 let ParseResult::Ok {
2014 program,
2015 source_map,
2016 globals,
2017 eval_context,
2018 comments,
2019 ..
2020 } = &**parsed
2021 else {
2022 unreachable!();
2023 };
2024 (
2025 program.clone(),
2026 source_map,
2027 globals,
2028 Either::Right(eval_context),
2029 Either::Right(comments.clone()),
2030 )
2031 }
2032 _ => unreachable!(),
2033 };
2034
2035 success(program, source_map, globals, eval_context, comments).await
2036 }
2037 _ => error(&parsed).await,
2038 }
2039}
2040
2041async fn emit_content(
2042 content: CodeGenResult,
2043 additional_ids: SmallVec<[ModuleId; 1]>,
2044) -> Result<Vc<EcmascriptModuleContent>> {
2045 let CodeGenResult {
2046 program,
2047 source_map,
2048 comments,
2049 is_esm,
2050 strict,
2051 original_source_map,
2052 minify,
2053 scope_hoisting_syntax_contexts: _,
2054 } = content;
2055
2056 let generate_source_map = source_map.is_some();
2057
2058 let source_map_names = if generate_source_map {
2060 let mut collector = IdentCollector::default();
2061 program.visit_with(&mut collector);
2062 collector.into_map()
2063 } else {
2064 Default::default()
2065 };
2066
2067 let mut bytes: Vec<u8> = vec![];
2068 let mut mappings = vec![];
2072
2073 let source_map = Arc::new(source_map);
2074
2075 {
2076 let mut wr = JsWriter::new(
2077 Default::default(),
2079 "\n",
2080 &mut bytes,
2081 generate_source_map.then_some(&mut mappings),
2082 );
2083 if matches!(minify, MinifyType::Minify { .. }) {
2084 wr.set_indent_str("");
2085 }
2086
2087 let comments = comments.consumable();
2088
2089 let mut emitter = Emitter {
2090 cfg: swc_core::ecma::codegen::Config::default(),
2091 cm: source_map.clone(),
2092 comments: Some(&comments as &dyn Comments),
2093 wr,
2094 };
2095
2096 emitter.emit_program(&program)?;
2097 drop(program);
2099 }
2100
2101 let source_map = if generate_source_map {
2102 let original_source_maps = original_source_map
2103 .iter()
2104 .map(|map| map.generate_source_map())
2105 .try_join()
2106 .await?;
2107 let original_source_maps = original_source_maps
2108 .iter()
2109 .filter_map(|map| map.as_content())
2110 .map(|map| map.content())
2111 .collect::<Vec<_>>();
2112
2113 Some(generate_js_source_map(
2114 &*source_map,
2115 mappings,
2116 original_source_maps,
2117 matches!(
2118 original_source_map,
2119 CodeGenResultOriginalSourceMap::Single(_)
2120 ),
2121 true,
2122 source_map_names,
2123 )?)
2124 } else {
2125 None
2126 };
2127
2128 Ok(EcmascriptModuleContent {
2129 inner_code: bytes.into(),
2130 source_map,
2131 is_esm,
2132 strict,
2133 additional_ids,
2134 }
2135 .cell())
2136}
2137
2138#[instrument(level = Level::TRACE, skip_all, name = "apply code generation")]
2139fn process_content_with_code_gens(
2140 program: &mut Program,
2141 globals: &Globals,
2142 code_gens: &mut Vec<CodeGeneration>,
2143) {
2144 let mut visitors = Vec::new();
2145 let mut root_visitors = Vec::new();
2146 let mut early_hoisted_stmts = FxIndexMap::default();
2147 let mut hoisted_stmts = FxIndexMap::default();
2148 let mut early_late_stmts = FxIndexMap::default();
2149 let mut late_stmts = FxIndexMap::default();
2150 for code_gen in code_gens {
2151 for CodeGenerationHoistedStmt { key, stmt } in code_gen.hoisted_stmts.drain(..) {
2152 hoisted_stmts.entry(key).or_insert(stmt);
2153 }
2154 for CodeGenerationHoistedStmt { key, stmt } in code_gen.early_hoisted_stmts.drain(..) {
2155 early_hoisted_stmts.insert(key.clone(), stmt);
2156 }
2157 for CodeGenerationHoistedStmt { key, stmt } in code_gen.late_stmts.drain(..) {
2158 late_stmts.insert(key.clone(), stmt);
2159 }
2160 for CodeGenerationHoistedStmt { key, stmt } in code_gen.early_late_stmts.drain(..) {
2161 early_late_stmts.insert(key.clone(), stmt);
2162 }
2163 for (path, visitor) in &code_gen.visitors {
2164 if path.is_empty() {
2165 root_visitors.push(&**visitor);
2166 } else {
2167 visitors.push((path, &**visitor));
2168 }
2169 }
2170 }
2171
2172 GLOBALS.set(globals, || {
2173 if !visitors.is_empty() {
2174 program.visit_mut_with_ast_path(
2175 &mut ApplyVisitors::new(visitors),
2176 &mut Default::default(),
2177 );
2178 }
2179 for pass in root_visitors {
2180 program.modify(pass);
2181 }
2182 });
2183
2184 match program {
2185 Program::Module(ast::Module { body, .. }) => {
2186 body.splice(
2187 0..0,
2188 early_hoisted_stmts
2189 .into_values()
2190 .chain(hoisted_stmts.into_values())
2191 .map(ModuleItem::Stmt),
2192 );
2193 body.extend(
2194 early_late_stmts
2195 .into_values()
2196 .chain(late_stmts.into_values())
2197 .map(ModuleItem::Stmt),
2198 );
2199 }
2200 Program::Script(Script { body, .. }) => {
2201 body.splice(
2202 0..0,
2203 early_hoisted_stmts
2204 .into_values()
2205 .chain(hoisted_stmts.into_values()),
2206 );
2207 body.extend(
2208 early_late_stmts
2209 .into_values()
2210 .chain(late_stmts.into_values()),
2211 );
2212 }
2213 };
2214}
2215
2216fn hygiene_rename_only(
2223 top_level_mark: Option<Mark>,
2224 is_import_mark: Mark,
2225 preserved_exports: &FxHashSet<Id>,
2226) -> impl VisitMut {
2227 struct HygieneRenamer<'a> {
2228 preserved_exports: &'a FxHashSet<Id>,
2229 is_import_mark: Mark,
2230 }
2231 impl swc_core::ecma::transforms::base::rename::Renamer for HygieneRenamer<'_> {
2233 type Target = Id;
2234
2235 const MANGLE: bool = false;
2236 const RESET_N: bool = true;
2237
2238 fn new_name_for(&self, orig: &Id, n: &mut usize) -> Atom {
2239 let res = if *n == 0 {
2240 orig.0.clone()
2241 } else {
2242 format!("{}{}", orig.0, n).into()
2243 };
2244 *n += 1;
2245 res
2246 }
2247
2248 fn preserve_name(&self, orig: &Id) -> bool {
2249 self.preserved_exports.contains(orig) || orig.1.has_mark(self.is_import_mark)
2250 }
2251 }
2252 swc_core::ecma::transforms::base::rename::renamer_keep_contexts(
2253 swc_core::ecma::transforms::base::hygiene::Config {
2254 top_level_mark: top_level_mark.unwrap_or_default(),
2255 ..Default::default()
2256 },
2257 HygieneRenamer {
2258 preserved_exports,
2259 is_import_mark,
2260 },
2261 )
2262}
2263
2264#[derive(Default)]
2265enum CodeGenResultSourceMap {
2266 #[default]
2267 None,
2269 Single {
2270 source_map: Arc<SourceMap>,
2271 },
2272 ScopeHoisting {
2273 modules_header_width: u32,
2276 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2277 source_maps: Vec<CodeGenResultSourceMap>,
2278 },
2279}
2280
2281impl CodeGenResultSourceMap {
2282 fn is_some(&self) -> bool {
2283 match self {
2284 CodeGenResultSourceMap::None => false,
2285 CodeGenResultSourceMap::Single { .. }
2286 | CodeGenResultSourceMap::ScopeHoisting { .. } => true,
2287 }
2288 }
2289}
2290
2291impl Debug for CodeGenResultSourceMap {
2292 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2293 match self {
2294 CodeGenResultSourceMap::None => write!(f, "CodeGenResultSourceMap::None"),
2295 CodeGenResultSourceMap::Single { source_map } => {
2296 write!(
2297 f,
2298 "CodeGenResultSourceMap::Single {{ source_map: {:?} }}",
2299 source_map.files().clone()
2300 )
2301 }
2302 CodeGenResultSourceMap::ScopeHoisting {
2303 modules_header_width,
2304 source_maps,
2305 ..
2306 } => write!(
2307 f,
2308 "CodeGenResultSourceMap::ScopeHoisting {{ modules_header_width: \
2309 {modules_header_width}, source_maps: {source_maps:?} }}",
2310 ),
2311 }
2312 }
2313}
2314
2315impl Files for CodeGenResultSourceMap {
2316 fn try_lookup_source_file(
2317 &self,
2318 pos: BytePos,
2319 ) -> Result<Option<Arc<SourceFile>>, SourceMapLookupError> {
2320 match self {
2321 CodeGenResultSourceMap::None => Ok(None),
2322 CodeGenResultSourceMap::Single { source_map } => source_map.try_lookup_source_file(pos),
2323 CodeGenResultSourceMap::ScopeHoisting {
2324 modules_header_width,
2325 lookup_table,
2326 source_maps,
2327 } => {
2328 let (module, pos) =
2329 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2330 source_maps[module].try_lookup_source_file(pos)
2331 }
2332 }
2333 }
2334
2335 fn is_in_file(&self, f: &Arc<SourceFile>, raw_pos: BytePos) -> bool {
2336 match self {
2337 CodeGenResultSourceMap::None => false,
2338 CodeGenResultSourceMap::Single { .. } => f.start_pos <= raw_pos && raw_pos < f.end_pos,
2339 CodeGenResultSourceMap::ScopeHoisting { .. } => {
2340 false
2346 }
2347 }
2348 }
2349
2350 fn map_raw_pos(&self, pos: BytePos) -> BytePos {
2351 match self {
2352 CodeGenResultSourceMap::None => BytePos::DUMMY,
2353 CodeGenResultSourceMap::Single { .. } => pos,
2354 CodeGenResultSourceMap::ScopeHoisting {
2355 modules_header_width,
2356 lookup_table,
2357 ..
2358 } => CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table).1,
2359 }
2360 }
2361}
2362
2363impl SourceMapper for CodeGenResultSourceMap {
2364 fn lookup_char_pos(&self, pos: BytePos) -> Loc {
2365 match self {
2366 CodeGenResultSourceMap::None => {
2367 panic!("CodeGenResultSourceMap::None cannot lookup_char_pos")
2368 }
2369 CodeGenResultSourceMap::Single { source_map } => source_map.lookup_char_pos(pos),
2370 CodeGenResultSourceMap::ScopeHoisting {
2371 modules_header_width,
2372 lookup_table,
2373 source_maps,
2374 } => {
2375 let (module, pos) =
2376 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2377 source_maps[module].lookup_char_pos(pos)
2378 }
2379 }
2380 }
2381 fn span_to_lines(&self, sp: Span) -> FileLinesResult {
2382 match self {
2383 CodeGenResultSourceMap::None => {
2384 panic!("CodeGenResultSourceMap::None cannot span_to_lines")
2385 }
2386 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_lines(sp),
2387 CodeGenResultSourceMap::ScopeHoisting {
2388 modules_header_width,
2389 lookup_table,
2390 source_maps,
2391 } => {
2392 let (module, lo) = CodeGenResultComments::decode_bytepos(
2393 *modules_header_width,
2394 sp.lo,
2395 lookup_table,
2396 );
2397 source_maps[module].span_to_lines(Span {
2398 lo,
2399 hi: CodeGenResultComments::decode_bytepos(
2400 *modules_header_width,
2401 sp.hi,
2402 lookup_table,
2403 )
2404 .1,
2405 })
2406 }
2407 }
2408 }
2409 fn span_to_string(&self, sp: Span) -> String {
2410 match self {
2411 CodeGenResultSourceMap::None => {
2412 panic!("CodeGenResultSourceMap::None cannot span_to_string")
2413 }
2414 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_string(sp),
2415 CodeGenResultSourceMap::ScopeHoisting {
2416 modules_header_width,
2417 lookup_table,
2418 source_maps,
2419 } => {
2420 let (module, lo) = CodeGenResultComments::decode_bytepos(
2421 *modules_header_width,
2422 sp.lo,
2423 lookup_table,
2424 );
2425 source_maps[module].span_to_string(Span {
2426 lo,
2427 hi: CodeGenResultComments::decode_bytepos(
2428 *modules_header_width,
2429 sp.hi,
2430 lookup_table,
2431 )
2432 .1,
2433 })
2434 }
2435 }
2436 }
2437 fn span_to_filename(&self, sp: Span) -> Arc<FileName> {
2438 match self {
2439 CodeGenResultSourceMap::None => {
2440 panic!("CodeGenResultSourceMap::None cannot span_to_filename")
2441 }
2442 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_filename(sp),
2443 CodeGenResultSourceMap::ScopeHoisting {
2444 modules_header_width,
2445 lookup_table,
2446 source_maps,
2447 } => {
2448 let (module, lo) = CodeGenResultComments::decode_bytepos(
2449 *modules_header_width,
2450 sp.lo,
2451 lookup_table,
2452 );
2453 source_maps[module].span_to_filename(Span {
2454 lo,
2455 hi: CodeGenResultComments::decode_bytepos(
2456 *modules_header_width,
2457 sp.hi,
2458 lookup_table,
2459 )
2460 .1,
2461 })
2462 }
2463 }
2464 }
2465 fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span> {
2466 match self {
2467 CodeGenResultSourceMap::None => {
2468 panic!("CodeGenResultSourceMap::None cannot merge_spans")
2469 }
2470 CodeGenResultSourceMap::Single { source_map } => source_map.merge_spans(sp_lhs, sp_rhs),
2471 CodeGenResultSourceMap::ScopeHoisting {
2472 modules_header_width,
2473 lookup_table,
2474 source_maps,
2475 } => {
2476 let (module_lhs, lo_lhs) = CodeGenResultComments::decode_bytepos(
2477 *modules_header_width,
2478 sp_lhs.lo,
2479 lookup_table,
2480 );
2481 let (module_rhs, lo_rhs) = CodeGenResultComments::decode_bytepos(
2482 *modules_header_width,
2483 sp_rhs.lo,
2484 lookup_table,
2485 );
2486 if module_lhs != module_rhs {
2487 return None;
2488 }
2489 source_maps[module_lhs].merge_spans(
2490 Span {
2491 lo: lo_lhs,
2492 hi: CodeGenResultComments::decode_bytepos(
2493 *modules_header_width,
2494 sp_lhs.hi,
2495 lookup_table,
2496 )
2497 .1,
2498 },
2499 Span {
2500 lo: lo_rhs,
2501 hi: CodeGenResultComments::decode_bytepos(
2502 *modules_header_width,
2503 sp_rhs.hi,
2504 lookup_table,
2505 )
2506 .1,
2507 },
2508 )
2509 }
2510 }
2511 }
2512 fn call_span_if_macro(&self, sp: Span) -> Span {
2513 match self {
2514 CodeGenResultSourceMap::None => {
2515 panic!("CodeGenResultSourceMap::None cannot call_span_if_macro")
2516 }
2517 CodeGenResultSourceMap::Single { source_map } => source_map.call_span_if_macro(sp),
2518 CodeGenResultSourceMap::ScopeHoisting {
2519 modules_header_width,
2520 lookup_table,
2521 source_maps,
2522 } => {
2523 let (module, lo) = CodeGenResultComments::decode_bytepos(
2524 *modules_header_width,
2525 sp.lo,
2526 lookup_table,
2527 );
2528 source_maps[module].call_span_if_macro(Span {
2529 lo,
2530 hi: CodeGenResultComments::decode_bytepos(
2531 *modules_header_width,
2532 sp.hi,
2533 lookup_table,
2534 )
2535 .1,
2536 })
2537 }
2538 }
2539 }
2540 fn doctest_offset_line(&self, _line: usize) -> usize {
2541 panic!("doctest_offset_line is not implemented for CodeGenResultSourceMap");
2542 }
2543 fn span_to_snippet(&self, sp: Span) -> Result<String, Box<SpanSnippetError>> {
2544 match self {
2545 CodeGenResultSourceMap::None => Err(Box::new(SpanSnippetError::SourceNotAvailable {
2546 filename: FileName::Anon,
2547 })),
2548 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_snippet(sp),
2549 CodeGenResultSourceMap::ScopeHoisting {
2550 modules_header_width,
2551 lookup_table,
2552 source_maps,
2553 } => {
2554 let (module, lo) = CodeGenResultComments::decode_bytepos(
2555 *modules_header_width,
2556 sp.lo,
2557 lookup_table,
2558 );
2559 source_maps[module].span_to_snippet(Span {
2560 lo,
2561 hi: CodeGenResultComments::decode_bytepos(
2562 *modules_header_width,
2563 sp.hi,
2564 lookup_table,
2565 )
2566 .1,
2567 })
2568 }
2569 }
2570 }
2571}
2572impl SourceMapperExt for CodeGenResultSourceMap {
2573 fn get_code_map(&self) -> &dyn SourceMapper {
2574 self
2575 }
2576}
2577
2578#[derive(Debug)]
2579enum CodeGenResultOriginalSourceMap {
2580 Single(Option<ResolvedVc<Box<dyn GenerateSourceMap>>>),
2581 ScopeHoisting(SmallVec<[ResolvedVc<Box<dyn GenerateSourceMap>>; 1]>),
2582}
2583
2584impl CodeGenResultOriginalSourceMap {
2585 fn iter(&self) -> impl Iterator<Item = ResolvedVc<Box<dyn GenerateSourceMap>>> {
2586 match self {
2587 CodeGenResultOriginalSourceMap::Single(map) => Either::Left(map.iter().copied()),
2588 CodeGenResultOriginalSourceMap::ScopeHoisting(maps) => {
2589 Either::Right(maps.iter().copied())
2590 }
2591 }
2592 }
2593}
2594
2595struct ModulePosition(u32, u32);
2597
2598enum CodeGenResultComments {
2599 Single {
2600 comments: Either<ImmutableComments, Arc<ImmutableComments>>,
2601 extra_comments: SwcComments,
2602 },
2603 ScopeHoisting {
2604 modules_header_width: u32,
2607 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2608 comments: Vec<CodeGenResultComments>,
2609 },
2610 Empty,
2611}
2612
2613unsafe impl Send for CodeGenResultComments {}
2614unsafe impl Sync for CodeGenResultComments {}
2615
2616impl CodeGenResultComments {
2617 const CONTINUATION_BIT: u32 = 1 << 31;
2618 const SIGN_EXTENSION_BIT: u32 = 1 << 30;
2619
2620 #[inline]
2621 fn encode_bytepos_impl(
2622 modules_header_width: u32,
2623 module: u32,
2624 pos: BytePos,
2625 push_into_lookup: &mut impl FnMut(u32, u32) -> Result<u32>,
2626 ) -> Result<BytePos> {
2627 if pos.is_dummy() {
2628 return Ok(pos);
2630 }
2631
2632 let header_width = modules_header_width + 2;
2663 let pos_width = 32 - header_width;
2664
2665 let pos = pos.0;
2666
2667 let old_high_bits = pos >> pos_width;
2668 let high_bits_set = if (2u32.pow(header_width) - 1) == old_high_bits {
2669 true
2670 } else if old_high_bits == 0 {
2671 false
2672 } else {
2673 let ix = push_into_lookup(module, pos)?;
2677 assert_eq!(ix & CodeGenResultComments::CONTINUATION_BIT, 0);
2679
2680 return Ok(BytePos(ix | CodeGenResultComments::CONTINUATION_BIT));
2681 };
2682
2683 let pos = pos & !((2u32.pow(header_width) - 1) << pos_width);
2684 let encoded_high_bits = if high_bits_set {
2685 CodeGenResultComments::SIGN_EXTENSION_BIT
2686 } else {
2687 0
2688 };
2689 let encoded_module = module << pos_width;
2690
2691 Ok(BytePos(encoded_module | encoded_high_bits | pos))
2692 }
2693
2694 fn take(&mut self) -> Self {
2695 std::mem::replace(self, CodeGenResultComments::Empty)
2696 }
2697
2698 fn consumable(&self) -> CodeGenResultCommentsConsumable<'_> {
2699 match self {
2700 CodeGenResultComments::Single {
2701 comments,
2702 extra_comments,
2703 } => CodeGenResultCommentsConsumable::Single {
2704 comments: match comments {
2705 Either::Left(comments) => comments.consumable(),
2706 Either::Right(comments) => comments.consumable(),
2707 },
2708 extra_comments,
2709 },
2710 CodeGenResultComments::ScopeHoisting {
2711 modules_header_width,
2712 lookup_table,
2713 comments,
2714 } => CodeGenResultCommentsConsumable::ScopeHoisting {
2715 modules_header_width: *modules_header_width,
2716 lookup_table: lookup_table.clone(),
2717 comments: comments.iter().map(|c| c.consumable()).collect(),
2718 },
2719 CodeGenResultComments::Empty => CodeGenResultCommentsConsumable::Empty,
2720 }
2721 }
2722
2723 fn encode_bytepos(
2724 modules_header_width: u32,
2725 module: u32,
2726 pos: BytePos,
2727 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2728 ) -> Result<BytePos> {
2729 let mut push = |module: u32, pos_u32: u32| -> Result<u32> {
2730 let mut lookup_table = lookup_table
2731 .lock()
2732 .map_err(|_| anyhow!("Failed to grab lock on the index map for byte positions"))?;
2733 let ix = lookup_table.len() as u32;
2734 if ix >= 1 << 30 {
2735 bail!("Too many byte positions being stored");
2736 }
2737 lookup_table.push(ModulePosition(module, pos_u32));
2738 Ok(ix)
2739 };
2740 Self::encode_bytepos_impl(modules_header_width, module, pos, &mut push)
2741 }
2742
2743 fn encode_bytepos_with_vec(
2744 modules_header_width: u32,
2745 module: u32,
2746 pos: BytePos,
2747 lookup_table: &mut Vec<ModulePosition>,
2748 ) -> Result<BytePos> {
2749 let mut push = |module: u32, pos_u32: u32| -> Result<u32> {
2750 let ix = lookup_table.len() as u32;
2751 if ix >= 1 << 30 {
2752 bail!("Too many byte positions being stored");
2753 }
2754 lookup_table.push(ModulePosition(module, pos_u32));
2755 Ok(ix)
2756 };
2757 Self::encode_bytepos_impl(modules_header_width, module, pos, &mut push)
2758 }
2759
2760 fn decode_bytepos(
2761 modules_header_width: u32,
2762 pos: BytePos,
2763 lookup_table: &Mutex<Vec<ModulePosition>>,
2764 ) -> (usize, BytePos) {
2765 if pos.is_dummy() {
2766 panic!("Cannot decode dummy BytePos");
2768 }
2769
2770 let header_width = modules_header_width + 2;
2771 let pos_width = 32 - header_width;
2772
2773 if (CodeGenResultComments::CONTINUATION_BIT & pos.0)
2774 == CodeGenResultComments::CONTINUATION_BIT
2775 {
2776 let lookup_table = lookup_table
2777 .lock()
2778 .expect("Failed to grab lock on the index map for byte position");
2779 let ix = pos.0 & !CodeGenResultComments::CONTINUATION_BIT;
2780 let ModulePosition(module, pos) = lookup_table[ix as usize];
2781
2782 return (module as usize, BytePos(pos));
2783 }
2784
2785 let high_bits_set = pos.0 >> 30 & 1 == 1;
2786 let module = (pos.0 << 2) >> (pos_width + 2);
2787 let pos = pos.0 & !((2u32.pow(header_width) - 1) << pos_width);
2788 let pos = if high_bits_set {
2789 pos | ((2u32.pow(header_width) - 1) << pos_width)
2790 } else {
2791 pos
2792 };
2793 (module as usize, BytePos(pos))
2794 }
2795}
2796
2797enum CodeGenResultCommentsConsumable<'a> {
2798 Single {
2799 comments: CowComments<'a>,
2800 extra_comments: &'a SwcComments,
2801 },
2802 ScopeHoisting {
2803 modules_header_width: u32,
2804 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2805 comments: Vec<CodeGenResultCommentsConsumable<'a>>,
2806 },
2807 Empty,
2808}
2809fn encode_module_into_comment_span(
2813 modules_header_width: u32,
2814 module: usize,
2815 mut comment: Comment,
2816 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2817) -> Comment {
2818 comment.span.lo = CodeGenResultComments::encode_bytepos(
2819 modules_header_width,
2820 module as u32,
2821 comment.span.lo,
2822 lookup_table.clone(),
2823 )
2824 .unwrap();
2825 comment.span.hi = CodeGenResultComments::encode_bytepos(
2826 modules_header_width,
2827 module as u32,
2828 comment.span.hi,
2829 lookup_table,
2830 )
2831 .unwrap();
2832 comment
2833}
2834
2835impl Comments for CodeGenResultCommentsConsumable<'_> {
2836 fn add_leading(&self, _pos: BytePos, _cmt: Comment) {
2837 unimplemented!("add_leading")
2838 }
2839
2840 fn add_leading_comments(&self, _pos: BytePos, _comments: Vec<Comment>) {
2841 unimplemented!("add_leading_comments")
2842 }
2843
2844 fn has_leading(&self, pos: BytePos) -> bool {
2845 if pos.is_dummy() {
2846 return false;
2847 }
2848 match self {
2849 Self::Single {
2850 comments,
2851 extra_comments,
2852 } => comments.has_leading(pos) || extra_comments.has_leading(pos),
2853 Self::ScopeHoisting {
2854 modules_header_width,
2855 lookup_table,
2856 comments,
2857 } => {
2858 let (module, pos) =
2859 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2860 comments[module].has_leading(pos)
2861 }
2862 Self::Empty => false,
2863 }
2864 }
2865
2866 fn move_leading(&self, _from: BytePos, _to: BytePos) {
2867 unimplemented!("move_leading")
2868 }
2869
2870 fn take_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
2871 if pos.is_dummy() {
2872 return None;
2873 }
2874 match self {
2875 Self::Single {
2876 comments,
2877 extra_comments,
2878 } => merge_option_vec(comments.take_leading(pos), extra_comments.take_leading(pos)),
2879 Self::ScopeHoisting {
2880 modules_header_width,
2881 lookup_table,
2882 comments,
2883 } => {
2884 let (module, pos) =
2885 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2886 comments[module].take_leading(pos).map(|comments| {
2887 comments
2888 .into_iter()
2889 .map(|c| {
2890 encode_module_into_comment_span(
2891 *modules_header_width,
2892 module,
2893 c,
2894 lookup_table.clone(),
2895 )
2896 })
2897 .collect()
2898 })
2899 }
2900 Self::Empty => None,
2901 }
2902 }
2903
2904 fn get_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
2905 if pos.is_dummy() {
2906 return None;
2907 }
2908 match self {
2909 Self::Single {
2910 comments,
2911 extra_comments,
2912 } => merge_option_vec(comments.get_leading(pos), extra_comments.get_leading(pos)),
2913 Self::ScopeHoisting {
2914 modules_header_width,
2915 lookup_table,
2916 comments,
2917 } => {
2918 let (module, pos) =
2919 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2920 comments[module].get_leading(pos).map(|comments| {
2921 comments
2922 .into_iter()
2923 .map(|c| {
2924 encode_module_into_comment_span(
2925 *modules_header_width,
2926 module,
2927 c,
2928 lookup_table.clone(),
2929 )
2930 })
2931 .collect()
2932 })
2933 }
2934 Self::Empty => None,
2935 }
2936 }
2937
2938 fn add_trailing(&self, _pos: BytePos, _cmt: Comment) {
2939 unimplemented!("add_trailing")
2940 }
2941
2942 fn add_trailing_comments(&self, _pos: BytePos, _comments: Vec<Comment>) {
2943 unimplemented!("add_trailing_comments")
2944 }
2945
2946 fn has_trailing(&self, pos: BytePos) -> bool {
2947 if pos.is_dummy() {
2948 return false;
2949 }
2950 match self {
2951 Self::Single {
2952 comments,
2953 extra_comments,
2954 } => comments.has_trailing(pos) || extra_comments.has_trailing(pos),
2955 Self::ScopeHoisting {
2956 modules_header_width,
2957 lookup_table,
2958 comments,
2959 } => {
2960 let (module, pos) =
2961 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2962 comments[module].has_trailing(pos)
2963 }
2964 Self::Empty => false,
2965 }
2966 }
2967
2968 fn move_trailing(&self, _from: BytePos, _to: BytePos) {
2969 unimplemented!("move_trailing")
2970 }
2971
2972 fn take_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
2973 if pos.is_dummy() {
2974 return None;
2975 }
2976 match self {
2977 Self::Single {
2978 comments,
2979 extra_comments,
2980 } => merge_option_vec(
2981 comments.take_trailing(pos),
2982 extra_comments.take_trailing(pos),
2983 ),
2984 Self::ScopeHoisting {
2985 modules_header_width,
2986 lookup_table,
2987 comments,
2988 } => {
2989 let (module, pos) =
2990 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2991 comments[module].take_trailing(pos).map(|comments| {
2992 comments
2993 .into_iter()
2994 .map(|c| {
2995 encode_module_into_comment_span(
2996 *modules_header_width,
2997 module,
2998 c,
2999 lookup_table.clone(),
3000 )
3001 })
3002 .collect()
3003 })
3004 }
3005 Self::Empty => None,
3006 }
3007 }
3008
3009 fn get_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
3010 if pos.is_dummy() {
3011 return None;
3012 }
3013 match self {
3014 Self::Single {
3015 comments,
3016 extra_comments,
3017 } => merge_option_vec(comments.get_leading(pos), extra_comments.get_leading(pos)),
3018 Self::ScopeHoisting {
3019 modules_header_width,
3020 lookup_table,
3021 comments,
3022 } => {
3023 let (module, pos) =
3024 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
3025 comments[module].get_leading(pos).map(|comments| {
3026 comments
3027 .into_iter()
3028 .map(|c| {
3029 encode_module_into_comment_span(
3030 *modules_header_width,
3031 module,
3032 c,
3033 lookup_table.clone(),
3034 )
3035 })
3036 .collect()
3037 })
3038 }
3039 Self::Empty => None,
3040 }
3041 }
3042
3043 fn add_pure_comment(&self, _pos: BytePos) {
3044 unimplemented!("add_pure_comment")
3045 }
3046}
3047
3048fn merge_option_vec<T>(a: Option<Vec<T>>, b: Option<Vec<T>>) -> Option<Vec<T>> {
3049 match (a, b) {
3050 (Some(a), Some(b)) => Some(a.into_iter().chain(b).collect()),
3051 (Some(a), None) => Some(a),
3052 (None, Some(b)) => Some(b),
3053 (None, None) => None,
3054 }
3055}
3056
3057#[cfg(test)]
3058mod tests {
3059 use super::*;
3060 fn bytepos_ensure_identical(modules_header_width: u32, pos: BytePos) {
3061 let module_count = 2u32.pow(modules_header_width);
3062 let lookup_table = Arc::new(Mutex::new(Vec::new()));
3063
3064 for module in [
3065 0,
3066 1,
3067 2,
3068 module_count / 2,
3069 module_count.wrapping_sub(5),
3070 module_count.wrapping_sub(1),
3071 ]
3072 .into_iter()
3073 .filter(|&m| m < module_count)
3074 {
3075 let encoded = CodeGenResultComments::encode_bytepos(
3076 modules_header_width,
3077 module,
3078 pos,
3079 lookup_table.clone(),
3080 )
3081 .unwrap();
3082 let (decoded_module, decoded_pos) =
3083 CodeGenResultComments::decode_bytepos(modules_header_width, encoded, &lookup_table);
3084 assert_eq!(
3085 decoded_module as u32, module,
3086 "Testing width {modules_header_width} and pos {pos:?}"
3087 );
3088 assert_eq!(
3089 decoded_pos, pos,
3090 "Testing width {modules_header_width} and pos {pos:?}"
3091 );
3092 }
3093 }
3094
3095 #[test]
3096 fn test_encode_decode_bytepos_format() {
3097 let table = Arc::new(Mutex::new(Vec::new()));
3098
3099 for (pos, module, modules_header_width, result) in [
3100 (
3101 0b00000000000000000000000000000101,
3102 0b1,
3103 1,
3104 0b00100000000000000000000000000101,
3105 ),
3106 (
3107 0b00000000000000000000000000000101,
3108 0b01,
3109 2,
3110 0b00010000000000000000000000000101,
3111 ),
3112 (
3113 0b11111111111111110000000000000101,
3114 0b0110,
3115 4,
3116 0b01011011111111110000000000000101,
3117 ),
3118 (
3119 BytePos::PLACEHOLDER.0,
3120 0b01111,
3121 5,
3122 0b01011111111111111111111111111101,
3123 ),
3124 (
3125 BytePos::PURE.0,
3126 0b01111,
3127 5,
3128 0b01011111111111111111111111111110,
3129 ),
3130 (
3131 BytePos::SYNTHESIZED.0,
3132 0b01111,
3133 5,
3134 0b01011111111111111111111111111111,
3135 ),
3136 (
3139 0b00000111111111110000000000000101,
3140 0b0001,
3141 4,
3142 0b10000000000000000000000000000000,
3143 ),
3144 (
3146 0b00000111111111110000000000111110,
3147 0b0001,
3148 4,
3149 0b10000000000000000000000000000001,
3150 ),
3151 (BytePos::DUMMY.0, 0b0001, 4, BytePos::DUMMY.0),
3153 ] {
3154 let encoded = CodeGenResultComments::encode_bytepos(
3155 modules_header_width,
3156 module,
3157 BytePos(pos),
3158 table.clone(),
3159 )
3160 .unwrap();
3161 assert_eq!(encoded.0, result);
3162
3163 if encoded.0 & CodeGenResultComments::CONTINUATION_BIT
3165 == CodeGenResultComments::CONTINUATION_BIT
3166 {
3167 let index = encoded.0 & !CodeGenResultComments::CONTINUATION_BIT;
3168 let ModulePosition(encoded_module, encoded_pos) =
3169 table.lock().unwrap()[index as usize];
3170 assert_eq!(encoded_module, module);
3171 assert_eq!(encoded_pos, pos);
3172 }
3173 }
3174 }
3175
3176 #[test]
3177 fn test_encode_decode_bytepos_lossless() {
3178 const DUMMY_RESERVE: u32 = u32::MAX - 2_u32.pow(16);
3180
3181 for modules_header_width in 1..=10 {
3182 for pos in [
3183 BytePos(1),
3185 BytePos(2),
3186 BytePos(100),
3187 BytePos(4_000_000),
3188 BytePos(600_000_000),
3189 BytePos(u32::MAX - 3), BytePos::PLACEHOLDER,
3191 BytePos::SYNTHESIZED,
3192 BytePos::PURE,
3193 BytePos(DUMMY_RESERVE),
3194 BytePos(DUMMY_RESERVE + 10),
3195 BytePos(DUMMY_RESERVE + 10000),
3196 ] {
3197 bytepos_ensure_identical(modules_header_width, pos);
3198 }
3199 }
3200 }
3201}