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