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