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