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