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