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 module_fragments;
24pub mod parse;
25mod path_visitor;
26pub mod references;
27pub mod rename;
28pub mod runtime_functions;
29pub mod side_effect_optimization;
30pub mod single_file_ecmascript_output;
31pub mod source_map;
32pub(crate) mod static_code;
33mod swc_comments;
34pub mod text;
35pub mod text_source_transform;
36pub mod transform;
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, structured::StructuredSourceMap},
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)]
162pub enum AnalyzeMode {
163 #[default]
165 CodeGeneration,
166 CodeGenerationAndTracing,
168 Tracing,
170}
171
172impl AnalyzeMode {
173 pub fn is_tracing_assets(self) -> bool {
175 match self {
176 AnalyzeMode::Tracing | AnalyzeMode::CodeGenerationAndTracing => true,
177 AnalyzeMode::CodeGeneration => false,
178 }
179 }
180
181 pub fn is_code_gen(self) -> bool {
182 match self {
183 AnalyzeMode::CodeGeneration | AnalyzeMode::CodeGenerationAndTracing => true,
184 AnalyzeMode::Tracing => false,
185 }
186 }
187}
188
189#[turbo_tasks::task_input]
191#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, TraceRawVcs, Encode, Decode)]
192pub enum TypeofWindow {
193 Object,
194 Undefined,
195}
196
197#[turbo_tasks::value(shared)]
198#[derive(Debug, Default, Copy, Clone)]
199pub struct EcmascriptOptions {
200 pub follow_reexports: bool,
202 pub module_fragments_enabled: bool,
204 pub specified_module_type: SpecifiedModuleType,
206 pub url_rewrite_behavior: Option<UrlRewriteBehavior>,
210 pub import_externals: bool,
213 pub ignore_dynamic_requests: bool,
217 pub extract_source_map: bool,
220 pub keep_last_successful_parse: bool,
224 pub analyze_mode: AnalyzeMode,
227 pub enable_typeof_window_inlining: Option<TypeofWindow>,
232 pub enable_exports_info_inlining: bool,
234
235 pub inline_helpers: bool,
236 pub infer_module_side_effects: bool,
238 pub cjs_tree_shaking: bool,
240}
241
242#[turbo_tasks::value(task_input)]
243#[derive(Hash, Debug, Copy, Clone)]
244pub enum EcmascriptModuleAssetType {
245 Ecmascript,
247 EcmascriptExtensionless,
249 Typescript {
251 tsx: bool,
253 analyze_types: bool,
255 },
256 TypescriptDeclaration,
258}
259
260impl Display for EcmascriptModuleAssetType {
261 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
262 match self {
263 EcmascriptModuleAssetType::Ecmascript => write!(f, "ecmascript"),
264 EcmascriptModuleAssetType::EcmascriptExtensionless => {
265 write!(f, "ecmascript extensionless")
266 }
267 EcmascriptModuleAssetType::Typescript { tsx, analyze_types } => {
268 write!(f, "typescript")?;
269 if *tsx {
270 write!(f, " with JSX")?;
271 }
272 if *analyze_types {
273 write!(f, " with types")?;
274 }
275 Ok(())
276 }
277 EcmascriptModuleAssetType::TypescriptDeclaration => write!(f, "typescript declaration"),
278 }
279 }
280}
281
282#[derive(Clone)]
283pub struct EcmascriptModuleAssetBuilder {
284 source: ResolvedVc<Box<dyn Source>>,
285 asset_context: ResolvedVc<Box<dyn AssetContext>>,
286 ty: EcmascriptModuleAssetType,
287 transforms: ResolvedVc<EcmascriptInputTransforms>,
288 options: ResolvedVc<EcmascriptOptions>,
289 compile_time_info: ResolvedVc<CompileTimeInfo>,
290 side_effect_free_packages: Option<ResolvedVc<Glob>>,
291 inner_assets: Option<ResolvedVc<InnerAssets>>,
292}
293
294impl EcmascriptModuleAssetBuilder {
295 pub fn with_inner_assets(mut self, inner_assets: ResolvedVc<InnerAssets>) -> Self {
296 self.inner_assets = Some(inner_assets);
297 self
298 }
299
300 pub fn with_type(mut self, ty: EcmascriptModuleAssetType) -> Self {
301 self.ty = ty;
302 self
303 }
304
305 pub fn build(self) -> Vc<EcmascriptModuleAsset> {
306 if let Some(inner_assets) = self.inner_assets {
307 EcmascriptModuleAsset::new_with_inner_assets(
308 *self.source,
309 *self.asset_context,
310 self.ty,
311 *self.transforms,
312 *self.options,
313 *self.compile_time_info,
314 self.side_effect_free_packages.map(|g| *g),
315 *inner_assets,
316 )
317 } else {
318 EcmascriptModuleAsset::new(
319 *self.source,
320 *self.asset_context,
321 self.ty,
322 *self.transforms,
323 *self.options,
324 *self.compile_time_info,
325 self.side_effect_free_packages.map(|g| *g),
326 )
327 }
328 }
329}
330
331#[turbo_tasks::value(eq = "manual")]
339struct LastSuccessfulSource {
340 #[bincode(with = "parking_lot_mutex_bincode")]
341 #[turbo_tasks(trace_ignore, debug_ignore)]
342 source: parking_lot::Mutex<Option<Rope>>,
343 #[turbo_tasks(debug_ignore)]
346 serialization_invalidator: SerializationInvalidator,
347}
348
349impl LastSuccessfulSource {
350 fn get(&self) -> Option<Rope> {
351 self.source.lock().clone()
352 }
353
354 fn set(&self, rope: Rope) {
355 *self.source.lock() = Some(rope);
356 self.serialization_invalidator.invalidate();
357 }
358
359 fn clear(&self) {
360 *self.source.lock() = None;
361 self.serialization_invalidator.invalidate();
362 }
363}
364
365impl Default for LastSuccessfulSource {
366 fn default() -> Self {
367 Self {
368 source: parking_lot::Mutex::new(None),
369 serialization_invalidator: get_serialization_invalidator(),
370 }
371 }
372}
373
374impl std::fmt::Debug for LastSuccessfulSource {
375 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
376 f.write_str("LastSuccessfulSource")
377 }
378}
379
380impl PartialEq for LastSuccessfulSource {
384 fn eq(&self, _other: &Self) -> bool {
385 true
386 }
387}
388
389impl Eq for LastSuccessfulSource {}
390
391impl std::hash::Hash for LastSuccessfulSource {
394 fn hash<H: std::hash::Hasher>(&self, _state: &mut H) {}
395}
396
397#[turbo_tasks::value]
398#[derive(Debug)]
399pub struct EcmascriptModuleAsset {
400 pub source: ResolvedVc<Box<dyn Source>>,
401 pub asset_context: ResolvedVc<Box<dyn AssetContext>>,
402 pub ty: EcmascriptModuleAssetType,
403 pub transforms: ResolvedVc<EcmascriptInputTransforms>,
404 pub options: ResolvedVc<EcmascriptOptions>,
405 pub compile_time_info: ResolvedVc<CompileTimeInfo>,
406 pub side_effect_free_packages: Option<ResolvedVc<Glob>>,
407 pub inner_assets: Option<ResolvedVc<InnerAssets>>,
408 origin_path: FileSystemPath,
410}
411
412#[turbo_tasks::value_trait]
413pub trait EcmascriptParsable {
414 #[turbo_tasks::function]
415 fn failsafe_parse(self: Vc<Self>) -> Vc<ParseResult>;
416}
417
418#[turbo_tasks::value_trait]
419pub trait EcmascriptAnalyzable: Module {
420 #[turbo_tasks::function]
421 fn analyze(self: Vc<Self>) -> Vc<AnalyzeEcmascriptModuleResult>;
422
423 #[turbo_tasks::function]
426 async fn module_content_without_analysis(
427 self: Vc<Self>,
428 generate_source_map: bool,
429 ) -> Result<Vc<EcmascriptModuleContent>>;
430
431 #[turbo_tasks::function]
432 async fn module_content_options(
433 self: Vc<Self>,
434 chunking_context: Vc<Box<dyn ChunkingContext>>,
435 async_module_info: Option<Vc<AsyncModuleInfo>>,
436 ) -> Result<Vc<EcmascriptModuleContentOptions>>;
437}
438
439pub trait EcmascriptAnalyzableExt {
440 fn module_content(
441 self: Vc<Self>,
442 chunking_context: Vc<Box<dyn ChunkingContext>>,
443 async_module_info: Option<Vc<AsyncModuleInfo>>,
444 ) -> Vc<EcmascriptModuleContent>;
445}
446
447impl<T> EcmascriptAnalyzableExt for T
448where
449 T: EcmascriptAnalyzable + Upcast<Box<dyn EcmascriptAnalyzable>>,
450{
451 fn module_content(
452 self: Vc<Self>,
453 chunking_context: Vc<Box<dyn ChunkingContext>>,
454 async_module_info: Option<Vc<AsyncModuleInfo>>,
455 ) -> Vc<EcmascriptModuleContent> {
456 let analyzable = Vc::upcast_non_strict::<Box<dyn EcmascriptAnalyzable>>(self);
457 let own_options = analyzable.module_content_options(chunking_context, async_module_info);
458 EcmascriptModuleContent::new(own_options)
459 }
460}
461
462impl EcmascriptModuleAsset {
463 pub fn builder(
464 source: ResolvedVc<Box<dyn Source>>,
465 asset_context: ResolvedVc<Box<dyn AssetContext>>,
466 transforms: ResolvedVc<EcmascriptInputTransforms>,
467 options: ResolvedVc<EcmascriptOptions>,
468 compile_time_info: ResolvedVc<CompileTimeInfo>,
469 side_effect_free_packages: Option<ResolvedVc<Glob>>,
470 ) -> EcmascriptModuleAssetBuilder {
471 EcmascriptModuleAssetBuilder {
472 source,
473 asset_context,
474 ty: EcmascriptModuleAssetType::Ecmascript,
475 transforms,
476 options,
477 compile_time_info,
478 side_effect_free_packages,
479 inner_assets: None,
480 }
481 }
482}
483
484#[turbo_tasks::value]
485#[derive(Clone)]
486pub(crate) struct ModuleTypeResult {
487 pub module_type: SpecifiedModuleType,
488 pub referenced_package_json: Option<FileSystemPath>,
489}
490
491#[turbo_tasks::value_impl]
492impl ModuleTypeResult {
493 #[turbo_tasks::function]
494 fn new(module_type: SpecifiedModuleType) -> Vc<Self> {
495 Self::cell(ModuleTypeResult {
496 module_type,
497 referenced_package_json: None,
498 })
499 }
500
501 #[turbo_tasks::function]
502 fn new_with_package_json(
503 module_type: SpecifiedModuleType,
504 package_json: FileSystemPath,
505 ) -> Vc<Self> {
506 Self::cell(ModuleTypeResult {
507 module_type,
508 referenced_package_json: Some(package_json),
509 })
510 }
511}
512
513impl EcmascriptModuleAsset {
514 async fn try_parse_last_successful_source(
520 &self,
521 last_successful_source: &LastSuccessfulSource,
522 ) -> Option<Vc<ParseResult>> {
523 let rope = last_successful_source.get()?;
524 let result: Result<Vc<ParseResult>> = async {
525 let node_env = self
526 .compile_time_info
527 .await?
528 .defines
529 .read_process_env(rcstr!("NODE_ENV"))
530 .owned()
531 .await?
532 .unwrap_or_else(|| rcstr!("development"));
533 crate::parse::parse_from_rope(rope, self.source, self.ty, self.transforms, node_env)
534 .await
535 }
536 .await;
537 match result {
538 Ok(result) => Some(result),
539 Err(_) => {
540 last_successful_source.clear();
542 None
543 }
544 }
545 }
546}
547
548#[turbo_tasks::value_impl]
549impl EcmascriptParsable for EcmascriptModuleAsset {
550 #[turbo_tasks::function]
551 async fn failsafe_parse(&self) -> Result<Vc<ParseResult>> {
552 let real_result = self.parse().await?;
553 if self.options.await?.keep_last_successful_parse {
554 let real_result_value = real_result.await?;
555 let last_successful_source = LastSuccessfulSource::default().cell().await?;
561 if let ParseResult::Ok { program_source, .. } = &*real_result_value {
562 last_successful_source.set(program_source.clone());
565 Ok(real_result)
566 } else {
567 Ok(self
568 .try_parse_last_successful_source(&last_successful_source)
569 .await
570 .unwrap_or(real_result))
571 }
572 } else {
573 Ok(real_result)
574 }
575 }
576}
577
578#[turbo_tasks::value_impl]
579impl EcmascriptAnalyzable for EcmascriptModuleAsset {
580 #[turbo_tasks::function]
581 fn analyze(self: Vc<Self>) -> Vc<AnalyzeEcmascriptModuleResult> {
582 analyze_ecmascript_module(self, None)
583 }
584
585 #[turbo_tasks::function]
588 async fn module_content_without_analysis(
589 self: Vc<Self>,
590 generate_source_map: bool,
591 ) -> Result<Vc<EcmascriptModuleContent>> {
592 let this = self.await?;
593
594 let parsed = this.parse().await?;
595
596 Ok(EcmascriptModuleContent::new_without_analysis(
597 parsed,
598 self.ident(),
599 this.options.await?.specified_module_type,
600 generate_source_map,
601 ))
602 }
603
604 #[turbo_tasks::function]
605 async fn module_content_options(
606 self: ResolvedVc<Self>,
607 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
608 async_module_info: Option<ResolvedVc<AsyncModuleInfo>>,
609 ) -> Result<Vc<EcmascriptModuleContentOptions>> {
610 let parsed = self.await?.parse().await?.to_resolved().await?;
611
612 let analyze = self.analyze();
613 let analyze_ref = analyze.await?;
614
615 let module_type_result = self.determine_module_type().await?;
616 let generate_source_map = *chunking_context
617 .reference_module_source_maps(Vc::upcast(*self))
618 .await?;
619
620 Ok(EcmascriptModuleContentOptions {
621 parsed: Some(parsed),
622 module: ResolvedVc::upcast(self),
623 specified_module_type: module_type_result.module_type,
624 chunking_context,
625 references: analyze.references().to_resolved().await?,
626 esm_references: analyze_ref.esm_references,
627 part_references: vec![],
628 code_generation: analyze_ref.code_generation,
629 async_module: analyze_ref.async_module,
630 generate_source_map,
631 original_source_map: analyze_ref.source_map,
632 exports: self.get_exports().to_resolved().await?,
633 async_module_info,
634 }
635 .cell())
636 }
637}
638
639#[turbo_tasks::function]
640async fn determine_module_type_for_directory(
641 context_path: FileSystemPath,
642) -> Result<Vc<ModuleTypeResult>> {
643 let find_package_json =
644 find_context_file(context_path, *package_json().to_resolved().await?, false).await?;
645 let FindContextFileResult::Found(package_json, _) = &*find_package_json else {
646 return Ok(ModuleTypeResult::new(SpecifiedModuleType::Automatic));
647 };
648
649 if let FileJsonContent::Content(content) = &*package_json.read_json().await?
651 && let Some(r#type) = content.get("type")
652 {
653 return Ok(ModuleTypeResult::new_with_package_json(
654 match r#type.as_str() {
655 Some("module") => SpecifiedModuleType::EcmaScript,
656 Some("commonjs") => SpecifiedModuleType::CommonJs,
657 _ => SpecifiedModuleType::Automatic,
658 },
659 package_json.clone(),
660 ));
661 }
662
663 Ok(ModuleTypeResult::new_with_package_json(
664 SpecifiedModuleType::Automatic,
665 package_json.clone(),
666 ))
667}
668
669#[turbo_tasks::value_impl]
670impl EcmascriptModuleAsset {
671 #[turbo_tasks::function]
672 async fn new(
673 source: ResolvedVc<Box<dyn Source>>,
674 asset_context: ResolvedVc<Box<dyn AssetContext>>,
675 ty: EcmascriptModuleAssetType,
676 transforms: ResolvedVc<EcmascriptInputTransforms>,
677 options: ResolvedVc<EcmascriptOptions>,
678 compile_time_info: ResolvedVc<CompileTimeInfo>,
679 side_effect_free_packages: Option<ResolvedVc<Glob>>,
680 ) -> Result<Vc<Self>> {
681 Ok(Self::cell(EcmascriptModuleAsset {
682 origin_path: source.ident().await?.path.clone(),
683 source,
684 asset_context,
685 ty,
686 transforms,
687 options,
688 compile_time_info,
689 side_effect_free_packages,
690 inner_assets: None,
691 }))
692 }
693
694 #[turbo_tasks::function]
695 async fn new_with_inner_assets(
696 source: ResolvedVc<Box<dyn Source>>,
697 asset_context: ResolvedVc<Box<dyn AssetContext>>,
698 ty: EcmascriptModuleAssetType,
699 transforms: ResolvedVc<EcmascriptInputTransforms>,
700 options: ResolvedVc<EcmascriptOptions>,
701 compile_time_info: ResolvedVc<CompileTimeInfo>,
702 side_effect_free_packages: Option<ResolvedVc<Glob>>,
703 inner_assets: ResolvedVc<InnerAssets>,
704 ) -> Result<Vc<Self>> {
705 if inner_assets.await?.is_empty() {
706 Ok(Self::new(
707 *source,
708 *asset_context,
709 ty,
710 *transforms,
711 *options,
712 *compile_time_info,
713 side_effect_free_packages.map(|g| *g),
714 ))
715 } else {
716 Ok(Self::cell(EcmascriptModuleAsset {
717 origin_path: source.ident().await?.path.clone(),
718 source,
719 asset_context,
720 ty,
721 transforms,
722 options,
723 compile_time_info,
724 side_effect_free_packages,
725 inner_assets: Some(inner_assets),
726 }))
727 }
728 }
729
730 #[turbo_tasks::function]
731 pub fn source(&self) -> Vc<Box<dyn Source>> {
732 *self.source
733 }
734
735 #[turbo_tasks::function]
736 pub fn options(&self) -> Vc<EcmascriptOptions> {
737 *self.options
738 }
739}
740
741impl EcmascriptModuleAsset {
742 pub fn analyze(self: Vc<Self>) -> Vc<AnalyzeEcmascriptModuleResult> {
743 analyze_ecmascript_module(self, None)
744 }
745
746 pub async fn parse(&self) -> Result<Vc<ParseResult>> {
747 let options = self.options.await?;
748 let node_env = self
749 .compile_time_info
750 .await?
751 .defines
752 .read_process_env(rcstr!("NODE_ENV"))
753 .owned()
754 .await?
755 .unwrap_or_else(|| rcstr!("development"));
756 Ok(parse(
757 *self.source,
758 self.ty,
759 *self.transforms,
760 node_env,
761 options.analyze_mode == AnalyzeMode::Tracing,
762 options.inline_helpers,
763 ))
764 }
765
766 #[tracing::instrument(level = "trace", skip_all)]
767 pub(crate) async fn determine_module_type(self: Vc<Self>) -> Result<ReadRef<ModuleTypeResult>> {
768 let this = self.await?;
769
770 match this.options.await?.specified_module_type {
771 SpecifiedModuleType::EcmaScript => {
772 return ModuleTypeResult::new(SpecifiedModuleType::EcmaScript).await;
773 }
774 SpecifiedModuleType::CommonJs => {
775 return ModuleTypeResult::new(SpecifiedModuleType::CommonJs).await;
776 }
777 SpecifiedModuleType::Automatic => {}
778 }
779
780 determine_module_type_for_directory(this.origin_path.parent()).await
781 }
782}
783
784#[turbo_tasks::value_impl]
785impl Module for EcmascriptModuleAsset {
786 #[turbo_tasks::function]
787 async fn ident(&self) -> Result<Vc<AssetIdent>> {
788 let mut ident = self.source.ident().owned().await?;
789 if let Some(inner_assets) = self.inner_assets {
790 for (name, asset) in inner_assets.await?.iter() {
791 ident = ident.with_asset(name.clone(), asset.ident().to_resolved().await?);
792 }
793 }
794 Ok(ident
795 .with_modifier(rcstr!("ecmascript"))
796 .with_layer(self.asset_context.into_trait_ref().await?.layer())
797 .into_vc())
798 }
799
800 #[turbo_tasks::function]
801 fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
802 Vc::cell(Some(self.source))
803 }
804
805 #[turbo_tasks::function]
806 fn references(self: Vc<Self>) -> Result<Vc<ModuleReferences>> {
807 Ok(self.analyze().references())
808 }
809
810 #[turbo_tasks::function]
811 async fn is_self_async(self: Vc<Self>) -> Result<Vc<bool>> {
812 if let Some(async_module) = *self.get_async_module().await? {
813 Ok(async_module.is_self_async(self.references()))
814 } else {
815 Ok(Vc::cell(false))
816 }
817 }
818
819 #[turbo_tasks::function]
820 async fn side_effects(self: Vc<Self>) -> Result<Vc<ModuleSideEffects>> {
821 let this = self.await?;
822 Ok((match *get_side_effect_free_declaration(
825 self.ident().await?.path.clone(),
826 this.side_effect_free_packages.map(|g| *g),
827 )
828 .await?
829 {
830 SideEffectsDeclaration::SideEffectful => ModuleSideEffects::SideEffectful,
831 SideEffectsDeclaration::SideEffectFree => ModuleSideEffects::SideEffectFree,
832 SideEffectsDeclaration::None => self.analyze().await?.side_effects,
833 })
834 .cell())
835 }
836}
837
838#[turbo_tasks::value_impl]
839impl ChunkableModule for EcmascriptModuleAsset {
840 #[turbo_tasks::function]
841 fn as_chunk_item(
842 self: ResolvedVc<Self>,
843 module_graph: ResolvedVc<ModuleGraph>,
844 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
845 ) -> Vc<Box<dyn ChunkItem>> {
846 ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
847 }
848}
849
850#[turbo_tasks::value_impl]
851impl EcmascriptChunkPlaceable for EcmascriptModuleAsset {
852 #[turbo_tasks::function]
853 async fn get_exports(self: Vc<Self>) -> Result<Vc<EcmascriptExports>> {
854 Ok(*compute_ecmascript_module_exports(self, None).await?.exports)
855 }
856
857 #[turbo_tasks::function]
858 async fn get_async_module(self: Vc<Self>) -> Result<Vc<OptionAsyncModule>> {
859 Ok(*self.analyze().await?.async_module)
860 }
861
862 #[turbo_tasks::function]
863 async fn chunk_item_content(
864 self: Vc<Self>,
865 chunking_context: Vc<Box<dyn ChunkingContext>>,
866 _module_graph: Vc<ModuleGraph>,
867 async_module_info: Option<Vc<AsyncModuleInfo>>,
868 _estimated: bool,
869 ) -> Result<Vc<EcmascriptChunkItemContent>> {
870 let span = tracing::info_span!(
871 "code generation",
872 name = display(self.ident().to_string().await?)
873 );
874 async {
875 let async_module_options = self.get_async_module().module_options(async_module_info);
876 let content = self.module_content(chunking_context, async_module_info);
877 EcmascriptChunkItemContent::new(content, chunking_context, async_module_options)
878 .to_resolved()
879 .await
880 .map(|r| *r)
881 }
882 .instrument(span)
883 .await
884 }
885}
886
887#[turbo_tasks::value_impl]
888impl MergeableModule for EcmascriptModuleAsset {
889 #[turbo_tasks::function]
890 async fn is_mergeable(self: ResolvedVc<Self>) -> Result<Vc<bool>> {
891 if matches!(
892 &*self.get_exports().await?,
893 EcmascriptExports::EsmExports(_)
894 ) {
895 return Ok(Vc::cell(true));
896 }
897
898 Ok(Vc::cell(false))
899 }
900
901 #[turbo_tasks::function]
902 async fn merge(
903 self: Vc<Self>,
904 modules: Vc<MergeableModulesExposed>,
905 entry_points: Vc<MergeableModules>,
906 ) -> Result<Vc<Box<dyn ChunkableModule>>> {
907 Ok(Vc::upcast(
908 *MergedEcmascriptModule::new(
909 modules,
910 entry_points,
911 self.options().to_resolved().await?,
912 )
913 .await?,
914 ))
915 }
916}
917
918#[turbo_tasks::value_impl]
919impl EvaluatableAsset for EcmascriptModuleAsset {}
920
921#[turbo_tasks::value_impl]
922impl ResolveOrigin for EcmascriptModuleAsset {
923 fn origin_path(&self) -> FileSystemPath {
924 self.origin_path.clone()
925 }
926
927 fn asset_context(&self) -> ResolvedVc<Box<dyn AssetContext>> {
928 self.asset_context
929 }
930}
931
932#[turbo_tasks::value(shared)]
934pub struct EcmascriptModuleContent {
935 pub inner_code: Rope,
936 pub source_map: Option<StructuredSourceMap>,
937 pub is_esm: bool,
938 pub strict: bool,
939 pub additional_ids: SmallVec<[ModuleId; 1]>,
940}
941
942#[turbo_tasks::value(shared)]
943#[derive(Clone, Debug, Hash)]
944pub struct EcmascriptModuleContentOptions {
945 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
946 parsed: Option<ResolvedVc<ParseResult>>,
947 specified_module_type: SpecifiedModuleType,
948 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
949 references: ResolvedVc<ModuleReferences>,
950 part_references: Vec<ResolvedVc<EcmascriptModulePartReference>>,
951 esm_references: ResolvedVc<EsmAssetReferences>,
952 code_generation: ResolvedVc<CodeGens>,
953 async_module: ResolvedVc<OptionAsyncModule>,
954 generate_source_map: bool,
955 original_source_map: Option<ResolvedVc<Box<dyn GenerateSourceMap>>>,
956 exports: ResolvedVc<EcmascriptExports>,
957 async_module_info: Option<ResolvedVc<AsyncModuleInfo>>,
958}
959
960impl EcmascriptModuleContentOptions {
961 async fn merged_code_gens(
962 &self,
963 scope_hoisting_context: ScopeHoistingContext<'_>,
964 eval_context: &EvalContext,
965 ) -> Result<Vec<CodeGeneration>> {
966 let EcmascriptModuleContentOptions {
969 module,
970 chunking_context,
971 references,
972 part_references,
973 esm_references,
974 code_generation,
975 async_module,
976 exports,
977 async_module_info,
978 ..
979 } = self;
980
981 async {
982 let additional_code_gens = [
983 if let Some(async_module) = &*async_module.await? {
984 Some(
985 async_module
986 .code_generation(
987 async_module_info.map(|info| *info),
988 **references,
989 **chunking_context,
990 )
991 .await?,
992 )
993 } else {
994 None
995 },
996 if let EcmascriptExports::EsmExports(exports) = *exports.await? {
997 Some(
998 exports
999 .code_generation(
1000 **chunking_context,
1001 scope_hoisting_context,
1002 eval_context,
1003 *module,
1004 )
1005 .await?,
1006 )
1007 } else {
1008 None
1009 },
1010 ];
1011
1012 let part_code_gens = part_references
1013 .iter()
1014 .map(|r| r.code_generation(**chunking_context, scope_hoisting_context))
1015 .try_join()
1016 .await?;
1017
1018 let esm_code_gens = esm_references
1019 .await?
1020 .iter()
1021 .map(|r| r.code_generation(**chunking_context, scope_hoisting_context))
1022 .try_join()
1023 .await?;
1024
1025 let code_gens = code_generation
1026 .await?
1027 .iter()
1028 .map(|c| {
1029 c.code_generation(
1030 **chunking_context,
1031 scope_hoisting_context,
1032 *module,
1033 *exports,
1034 )
1035 })
1036 .try_join()
1037 .await?;
1038
1039 anyhow::Ok(
1040 part_code_gens
1041 .into_iter()
1042 .chain(esm_code_gens)
1043 .chain(additional_code_gens.into_iter().flatten())
1044 .chain(code_gens)
1045 .collect(),
1046 )
1047 }
1048 .instrument(tracing::info_span!("precompute code generation"))
1049 .await
1050 }
1051}
1052
1053#[turbo_tasks::value_impl]
1054impl EcmascriptModuleContent {
1055 #[turbo_tasks::function]
1057 pub async fn new(input: Vc<EcmascriptModuleContentOptions>) -> Result<Vc<Self>> {
1058 let input = input.await?;
1059 let EcmascriptModuleContentOptions {
1060 parsed,
1061 module,
1062 specified_module_type,
1063 generate_source_map,
1064 original_source_map,
1065 chunking_context,
1066 ..
1067 } = &*input;
1068
1069 let minify = chunking_context.minify_type().await?;
1070
1071 let content = process_parse_result(
1072 *parsed,
1073 module.ident(),
1074 *specified_module_type,
1075 *generate_source_map,
1076 *original_source_map,
1077 *minify,
1078 Some(&*input),
1079 None,
1080 )
1081 .await?;
1082 emit_content(content, Default::default()).await
1083 }
1084
1085 #[turbo_tasks::function]
1087 pub async fn new_without_analysis(
1088 parsed: Vc<ParseResult>,
1089 ident: Vc<AssetIdent>,
1090 specified_module_type: SpecifiedModuleType,
1091 generate_source_map: bool,
1092 ) -> Result<Vc<Self>> {
1093 let content = process_parse_result(
1094 Some(parsed.to_resolved().await?),
1095 ident,
1096 specified_module_type,
1097 generate_source_map,
1098 None,
1099 MinifyType::NoMinify,
1100 None,
1101 None,
1102 )
1103 .await?;
1104 emit_content(content, Default::default()).await
1105 }
1106
1107 #[turbo_tasks::function]
1114 pub async fn new_merged(
1115 modules: Vec<(
1116 ResolvedVc<Box<dyn EcmascriptAnalyzable>>,
1117 MergeableModuleExposure,
1118 )>,
1119 module_options: Vec<Vc<EcmascriptModuleContentOptions>>,
1120 entry_points: Vec<ResolvedVc<Box<dyn EcmascriptAnalyzable>>>,
1121 ) -> Result<Vc<Self>> {
1122 async {
1123 let modules = modules
1124 .into_iter()
1125 .map(|(m, exposed)| {
1126 (
1127 ResolvedVc::try_sidecast::<Box<dyn EcmascriptChunkPlaceable>>(m).unwrap(),
1128 exposed,
1129 )
1130 })
1131 .collect::<FxIndexMap<_, _>>();
1132 let entry_points = entry_points
1133 .into_iter()
1134 .map(|m| {
1135 let m =
1136 ResolvedVc::try_sidecast::<Box<dyn EcmascriptChunkPlaceable>>(m).unwrap();
1137 (m, modules.get_index_of(&m).unwrap())
1138 })
1139 .collect::<Vec<_>>();
1140
1141 let globals_merged = Globals::default();
1142
1143 let contents = module_options
1144 .iter()
1145 .map(async |options| {
1146 let options = options.await?;
1147 let EcmascriptModuleContentOptions {
1148 chunking_context,
1149 parsed,
1150 module,
1151 specified_module_type,
1152 generate_source_map,
1153 original_source_map,
1154 ..
1155 } = &*options;
1156
1157 let result = process_parse_result(
1158 *parsed,
1159 module.ident(),
1160 *specified_module_type,
1161 *generate_source_map,
1162 *original_source_map,
1163 *chunking_context.minify_type().await?,
1164 Some(&*options),
1165 Some(ScopeHoistingOptions {
1166 module: *module,
1167 modules: &modules,
1168 }),
1169 )
1170 .await?;
1171
1172 Ok((*module, result))
1173 })
1174 .try_join()
1175 .await?;
1176
1177 let (merged_ast, comments, source_maps, original_source_maps, lookup_table) =
1178 merge_modules(contents, &entry_points, &globals_merged).await?;
1179
1180 let options = module_options.last().unwrap().await?;
1183
1184 let modules_header_width = modules.len().next_power_of_two().trailing_zeros();
1185 let content = CodeGenResult {
1186 program: merged_ast,
1187 source_map: CodeGenResultSourceMap::ScopeHoisting {
1188 modules_header_width,
1189 lookup_table: lookup_table.clone(),
1190 source_maps,
1191 },
1192 comments: CodeGenResultComments::ScopeHoisting {
1193 modules_header_width,
1194 lookup_table,
1195 comments,
1196 },
1197 is_esm: true,
1198 strict: true,
1199 original_source_map: CodeGenResultOriginalSourceMap::ScopeHoisting(
1200 original_source_maps,
1201 ),
1202 minify: *options.chunking_context.minify_type().await?,
1203 scope_hoisting_syntax_contexts: None,
1204 };
1205
1206 let first_entry = entry_points.first().unwrap().0;
1207 let additional_ids = modules
1208 .keys()
1209 .filter(|m| {
1216 **m != first_entry
1217 && *modules.get(*m).unwrap() == MergeableModuleExposure::External
1218 })
1219 .map(|m| m.chunk_item_id(*options.chunking_context))
1220 .try_join()
1221 .await?
1222 .into();
1223
1224 emit_content(content, additional_ids)
1225 .instrument(tracing::info_span!("emit code"))
1226 .await
1227 }
1228 .instrument(tracing::info_span!(
1229 "generate merged code",
1230 modules = module_options.len()
1231 ))
1232 .await
1233 }
1234}
1235
1236#[instrument(level = Level::TRACE, skip_all, name = "merge")]
1245#[allow(clippy::type_complexity)]
1246async fn merge_modules(
1247 mut contents: Vec<(ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, CodeGenResult)>,
1248 entry_points: &Vec<(ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, usize)>,
1249 globals_merged: &'_ Globals,
1250) -> Result<(
1251 Program,
1252 Vec<CodeGenResultComments>,
1253 Vec<CodeGenResultSourceMap>,
1254 SmallVec<[ResolvedVc<Box<dyn GenerateSourceMap>>; 1]>,
1255 Arc<Mutex<Vec<ModulePosition>>>,
1256)> {
1257 struct SetSyntaxContextVisitor<'a> {
1258 modules_header_width: u32,
1259 current_module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1260 current_module_idx: u32,
1261 lookup_table: &'a mut Vec<ModulePosition>,
1262 reverse_module_contexts:
1264 FxHashMap<SyntaxContext, ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
1265 export_contexts:
1268 &'a FxHashMap<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, &'a FxHashMap<RcStr, Id>>,
1269 unique_contexts_cache: &'a mut FxHashMap<
1272 (ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, SyntaxContext),
1273 SyntaxContext,
1274 >,
1275
1276 error: anyhow::Result<()>,
1277 }
1278
1279 impl<'a> SetSyntaxContextVisitor<'a> {
1280 fn get_context_for(
1281 &mut self,
1282 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1283 local_ctxt: SyntaxContext,
1284 ) -> SyntaxContext {
1285 if let Some(&global_ctxt) = self.unique_contexts_cache.get(&(module, local_ctxt)) {
1286 global_ctxt
1287 } else {
1288 let global_ctxt = SyntaxContext::empty().apply_mark(Mark::new());
1289 self.unique_contexts_cache
1290 .insert((module, local_ctxt), global_ctxt);
1291 global_ctxt
1292 }
1293 }
1294 }
1295
1296 impl VisitMut for SetSyntaxContextVisitor<'_> {
1297 fn visit_mut_ident(&mut self, ident: &mut Ident) {
1298 let Ident {
1299 sym, ctxt, span, ..
1300 } = ident;
1301
1302 if let Some(&module) = self.reverse_module_contexts.get(ctxt) {
1305 let eval_context_exports = self.export_contexts.get(&module).unwrap();
1306 let sym_rc_str: RcStr = sym.as_str().into();
1309 let (local, local_ctxt) = if let Some((local, local_ctxt)) =
1310 eval_context_exports.get(&sym_rc_str)
1311 {
1312 (Some(local), *local_ctxt)
1313 } else if sym.starts_with("__TURBOPACK__imported__module__") {
1314 (None, SyntaxContext::empty())
1319 } else {
1320 self.error = Err(anyhow::anyhow!(
1321 "Expected to find a local export for {sym} with ctxt {ctxt:#?} in \
1322 {eval_context_exports:?}",
1323 ));
1324 return;
1325 };
1326
1327 let global_ctxt = self.get_context_for(module, local_ctxt);
1328
1329 if let Some(local) = local {
1330 *sym = local.clone();
1331 }
1332 *ctxt = global_ctxt;
1333 span.visit_mut_with(self);
1334 } else {
1335 ident.visit_mut_children_with(self);
1336 }
1337 }
1338
1339 fn visit_mut_syntax_context(&mut self, local_ctxt: &mut SyntaxContext) {
1340 let module = self
1343 .reverse_module_contexts
1344 .get(local_ctxt)
1345 .copied()
1346 .unwrap_or(self.current_module);
1347
1348 let global_ctxt = self.get_context_for(module, *local_ctxt);
1349 *local_ctxt = global_ctxt;
1350 }
1351 fn visit_mut_span(&mut self, span: &mut Span) {
1352 span.lo = CodeGenResultComments::encode_bytepos_with_vec(
1355 self.modules_header_width,
1356 self.current_module_idx,
1357 span.lo,
1358 self.lookup_table,
1359 )
1360 .unwrap_or_else(|err| {
1361 self.error = Err(err);
1362 span.lo
1363 });
1364 span.hi = CodeGenResultComments::encode_bytepos_with_vec(
1365 self.modules_header_width,
1366 self.current_module_idx,
1367 span.hi,
1368 self.lookup_table,
1369 )
1370 .unwrap_or_else(|err| {
1371 self.error = Err(err);
1372 span.hi
1373 });
1374 }
1375 }
1376
1377 let mut programs = contents
1380 .iter_mut()
1381 .map(|(_, content)| content.program.take())
1382 .collect::<Vec<_>>();
1383
1384 let export_contexts = contents
1385 .iter()
1386 .map(|(module, content)| {
1387 Ok((
1388 *module,
1389 content
1390 .scope_hoisting_syntax_contexts
1391 .as_ref()
1392 .map(|(_, export_contexts)| export_contexts)
1393 .context("expected exports contexts")?,
1394 ))
1395 })
1396 .collect::<Result<FxHashMap<_, _>>>()?;
1397
1398 let mut lookup_table = Vec::new();
1399 let result = GLOBALS.set(globals_merged, || {
1400 let _ = tracing::trace_span!("merge inner").entered();
1401 let mut unique_contexts_cache =
1403 FxHashMap::with_capacity_and_hasher(contents.len() * 5, Default::default());
1404
1405 let mut prepare_module =
1406 |module_count: usize,
1407 current_module_idx: usize,
1408 (module, content): &(ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, CodeGenResult),
1409 program: &mut Program,
1410 lookup_table: &mut Vec<ModulePosition>| {
1411 let _ = tracing::trace_span!("prepare module").entered();
1412 if let CodeGenResult {
1413 scope_hoisting_syntax_contexts: Some((module_contexts, _)),
1414 ..
1415 } = content
1416 {
1417 let modules_header_width = module_count.next_power_of_two().trailing_zeros();
1418 GLOBALS.set(globals_merged, || {
1419 let mut visitor = SetSyntaxContextVisitor {
1420 modules_header_width,
1421 current_module: *module,
1422 current_module_idx: current_module_idx as u32,
1423 lookup_table,
1424 reverse_module_contexts: module_contexts
1425 .iter()
1426 .map(|e| (*e.value(), *e.key()))
1427 .collect(),
1428 export_contexts: &export_contexts,
1429 unique_contexts_cache: &mut unique_contexts_cache,
1430 error: Ok(()),
1431 };
1432 program.visit_mut_with(&mut visitor);
1433 visitor.error
1434 })?;
1435
1436 Ok(match program.take() {
1437 Program::Module(module) => Either::Left(module.body.into_iter()),
1438 Program::Script(script) => {
1441 Either::Right(script.body.into_iter().map(ModuleItem::Stmt))
1442 }
1443 })
1444 } else {
1445 bail!("Expected scope_hosting_syntax_contexts");
1446 }
1447 };
1448
1449 let mut inserted = FxHashSet::with_capacity_and_hasher(contents.len(), Default::default());
1450 inserted.extend(entry_points.iter().map(|(_, i)| *i));
1452
1453 let mut inserted_imports = FxHashMap::default();
1454
1455 let span = tracing::trace_span!("merge ASTs");
1456 let mut queue = entry_points
1459 .iter()
1460 .map(|&(_, i)| {
1461 prepare_module(
1462 contents.len(),
1463 i,
1464 &contents[i],
1465 &mut programs[i],
1466 &mut lookup_table,
1467 )
1468 .map_err(|err| (i, err))
1469 })
1470 .flatten_ok()
1471 .rev()
1472 .collect::<Result<Vec<_>, _>>()?;
1473 let mut result = vec![];
1474 while let Some(item) = queue.pop() {
1475 if let ModuleItem::Stmt(stmt) = &item {
1476 match stmt {
1477 Stmt::Expr(ExprStmt { expr, .. }) => {
1478 if let Expr::Call(CallExpr {
1479 callee: Callee::Expr(callee),
1480 args,
1481 ..
1482 }) = &**expr
1483 && callee.is_ident_ref_to("__turbopack_merged_esm__")
1484 {
1485 let index =
1486 args[0].expr.as_lit().unwrap().as_num().unwrap().value as usize;
1487
1488 if inserted.insert(index) {
1490 queue.extend(
1491 prepare_module(
1492 contents.len(),
1493 index,
1494 &contents[index],
1495 &mut programs[index],
1496 &mut lookup_table,
1497 )
1498 .map_err(|err| (index, err))?
1499 .into_iter()
1500 .rev(),
1501 );
1502 }
1503 continue;
1504 }
1505 }
1506 Stmt::Decl(Decl::Var(var)) => {
1507 if let [decl] = &*var.decls
1508 && let Some(name) = decl.name.as_ident()
1509 && name.sym.starts_with("__TURBOPACK__imported__module__")
1510 {
1511 match inserted_imports.entry(name.sym.clone()) {
1516 Entry::Occupied(entry) => {
1517 let entry_ctxt = *entry.get();
1522 let new = Ident::new(name.sym.clone(), DUMMY_SP, name.ctxt);
1523 let old = Ident::new(name.sym.clone(), DUMMY_SP, entry_ctxt);
1524 result.push(ModuleItem::Stmt(
1525 quote!("var $new = $old;" as Stmt,
1526 new: Ident = new,
1527 old: Ident = old
1528 ),
1529 ));
1530 continue;
1531 }
1532 Entry::Vacant(entry) => {
1533 entry.insert(name.ctxt);
1534 }
1535 }
1536 }
1537 }
1538 _ => (),
1539 }
1540 }
1541
1542 result.push(item);
1543 }
1544 drop(span);
1545
1546 let span = tracing::trace_span!("hygiene").entered();
1547 let mut merged_ast = Program::Module(swc_core::ecma::ast::Module {
1548 body: result,
1549 span: DUMMY_SP,
1550 shebang: None,
1551 });
1552 merged_ast.visit_mut_with(&mut swc_core::ecma::transforms::base::hygiene::hygiene());
1553 drop(span);
1554
1555 Ok((merged_ast, inserted))
1556 });
1557
1558 let (merged_ast, inserted) = match result {
1559 Ok(v) => v,
1560 Err((content_idx, err)) => {
1561 return Err(
1562 err.context(turbofmt!("Processing {}", contents[content_idx].0.ident()).await?),
1564 );
1565 }
1566 };
1567
1568 if cfg!(debug_assertions) && inserted.len() != contents.len() {
1569 bail!(
1570 "Not all merged modules were inserted: {:?}",
1571 contents
1572 .iter()
1573 .enumerate()
1574 .map(async |(i, m)| Ok((inserted.contains(&i), m.0.ident().to_string().await?)))
1575 .try_join()
1576 .await?,
1577 );
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_ids),
1781 |e| Cow::Borrowed(&e.imports.exports_ids),
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: (&*turbofmt!(" MERGED MODULE: {}", ident).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 error_messages = messages
1922 .as_ref()
1923 .and_then(|m| m.first().map(|f| format!("\n{f}")))
1924 .unwrap_or("".into());
1925 let msg = &*turbofmt!(
1926 "Could not parse module '{}'\n{error_messages}",
1927 ident.await?.path
1928 )
1929 .await?;
1930 let body = vec![
1931 quote!(
1932 "var e = new Error($msg);" as Stmt,
1933 msg: Expr = Expr::Lit(msg.into()),
1934 ),
1935 quote!("e.code = 'MODULE_UNPARSABLE';" as Stmt),
1936 quote!("throw e;" as Stmt),
1937 ];
1938
1939 CodeGenResult {
1940 program: Program::Script(Script {
1941 span: DUMMY_SP,
1942 body,
1943 shebang: None,
1944 }),
1945 source_map: CodeGenResultSourceMap::None,
1946 comments: CodeGenResultComments::Empty,
1947 is_esm: false,
1948 strict: false,
1949 original_source_map: CodeGenResultOriginalSourceMap::Single(None),
1950 minify: MinifyType::NoMinify,
1951 scope_hoisting_syntax_contexts: None,
1952 }
1953 }
1954 ParseResult::NotFound => {
1955 let msg = &*turbofmt!(
1956 "Could not parse module '{}', file not found",
1957 ident.await?.path
1958 )
1959 .await?;
1960 let body = vec![
1961 quote!(
1962 "var e = new Error($msg);" as Stmt,
1963 msg: Expr = Expr::Lit(msg.into()),
1964 ),
1965 quote!("e.code = 'MODULE_UNPARSABLE';" as Stmt),
1966 quote!("throw e;" as Stmt),
1967 ];
1968 CodeGenResult {
1969 program: Program::Script(Script {
1970 span: DUMMY_SP,
1971 body,
1972 shebang: None,
1973 }),
1974 source_map: CodeGenResultSourceMap::None,
1975 comments: CodeGenResultComments::Empty,
1976 is_esm: false,
1977 strict: false,
1978 original_source_map: CodeGenResultOriginalSourceMap::Single(None),
1979 minify: MinifyType::NoMinify,
1980 scope_hoisting_syntax_contexts: None,
1981 }
1982 }
1983 })
1984 },
1985 )
1986 .instrument(tracing::trace_span!(
1987 "process parse result",
1988 ident = display(ident.to_string().await?),
1989 ))
1990 .await
1991}
1992
1993async fn with_consumed_parse_result<T>(
1995 parsed: Option<ResolvedVc<ParseResult>>,
1996 success: impl AsyncFnOnce(
1997 Program,
1998 &Arc<SourceMap>,
1999 &Arc<Globals>,
2000 Either<EvalContext, &'_ EvalContext>,
2001 Either<ImmutableComments, Arc<ImmutableComments>>,
2002 ) -> Result<T>,
2003 error: impl AsyncFnOnce(&ParseResult) -> Result<T>,
2004) -> Result<T> {
2005 let Some(parsed) = parsed else {
2006 let globals = Globals::new();
2007 let eval_context = GLOBALS.set(&globals, || EvalContext {
2008 unresolved_mark: Mark::new(),
2009 top_level_mark: Mark::new(),
2010 imports: Default::default(),
2011 force_free_values: Default::default(),
2012 });
2013 return success(
2014 Program::Module(swc_core::ecma::ast::Module::dummy()),
2015 &Default::default(),
2016 &Default::default(),
2017 Either::Left(eval_context),
2018 Either::Left(Default::default()),
2019 )
2020 .await;
2021 };
2022
2023 let parsed = parsed.final_read_hint().await?;
2024 match &*parsed {
2025 ParseResult::Ok { .. } => {
2026 let mut parsed = ReadRef::try_unwrap(parsed);
2027 let (program, source_map, globals, eval_context, comments) = match &mut parsed {
2028 Ok(ParseResult::Ok {
2029 program,
2030 source_map,
2031 globals,
2032 eval_context,
2033 comments,
2034 ..
2035 }) => (
2036 program.take(),
2037 &*source_map,
2038 &*globals,
2039 Either::Left(std::mem::replace(
2040 eval_context,
2041 EvalContext {
2042 unresolved_mark: eval_context.unresolved_mark,
2043 top_level_mark: eval_context.top_level_mark,
2044 imports: Default::default(),
2045 force_free_values: Default::default(),
2046 },
2047 )),
2048 match Arc::try_unwrap(take(comments)) {
2049 Ok(comments) => Either::Left(comments),
2050 Err(comments) => Either::Right(comments),
2051 },
2052 ),
2053 Err(parsed) => {
2054 let ParseResult::Ok {
2055 program,
2056 source_map,
2057 globals,
2058 eval_context,
2059 comments,
2060 ..
2061 } = &**parsed
2062 else {
2063 unreachable!();
2064 };
2065 (
2066 program.clone(),
2067 source_map,
2068 globals,
2069 Either::Right(eval_context),
2070 Either::Right(comments.clone()),
2071 )
2072 }
2073 _ => unreachable!(),
2074 };
2075
2076 success(program, source_map, globals, eval_context, comments).await
2077 }
2078 _ => error(&parsed).await,
2079 }
2080}
2081
2082async fn emit_content(
2083 content: CodeGenResult,
2084 additional_ids: SmallVec<[ModuleId; 1]>,
2085) -> Result<Vc<EcmascriptModuleContent>> {
2086 let CodeGenResult {
2087 program,
2088 source_map,
2089 comments,
2090 is_esm,
2091 strict,
2092 original_source_map,
2093 minify,
2094 scope_hoisting_syntax_contexts: _,
2095 } = content;
2096
2097 let generate_source_map = source_map.is_some();
2098
2099 let source_map_names = if generate_source_map {
2101 let mut collector = IdentCollector::default();
2102 program.visit_with(&mut collector);
2103 collector.into_map()
2104 } else {
2105 Default::default()
2106 };
2107
2108 let mut bytes: Vec<u8> = vec![];
2109 let mut mappings = vec![];
2113
2114 let source_map = Arc::new(source_map);
2115
2116 {
2117 let mut wr = JsWriter::new(
2118 Default::default(),
2120 "\n",
2121 &mut bytes,
2122 generate_source_map.then_some(&mut mappings),
2123 );
2124 if matches!(minify, MinifyType::Minify { .. }) {
2125 wr.set_indent_str("");
2126 }
2127
2128 let comments = comments.consumable();
2129
2130 let mut emitter = Emitter {
2131 cfg: swc_core::ecma::codegen::Config::default(),
2132 cm: source_map.clone(),
2133 comments: Some(&comments as &dyn Comments),
2134 wr,
2135 };
2136
2137 emitter.emit_program(&program)?;
2138 drop(program);
2140 }
2141
2142 let source_map = if generate_source_map {
2143 let original_source_maps = original_source_map
2144 .iter()
2145 .map(|map| map.generate_source_map())
2146 .try_join()
2147 .await?;
2148 let original_source_maps = original_source_maps
2149 .iter()
2150 .filter_map(|map| map.as_content())
2151 .map(|map| map.content())
2152 .collect::<Vec<_>>();
2153
2154 Some(generate_js_source_map(
2155 &*source_map,
2156 mappings,
2157 original_source_maps,
2158 matches!(
2159 original_source_map,
2160 CodeGenResultOriginalSourceMap::Single(_)
2161 ),
2162 true,
2163 source_map_names,
2164 )?)
2165 } else {
2166 None
2167 };
2168
2169 Ok(EcmascriptModuleContent {
2170 inner_code: bytes.into(),
2171 source_map,
2172 is_esm,
2173 strict,
2174 additional_ids,
2175 }
2176 .cell())
2177}
2178
2179#[instrument(level = Level::TRACE, skip_all, name = "apply code generation")]
2180fn process_content_with_code_gens(
2181 program: &mut Program,
2182 globals: &Globals,
2183 code_gens: &mut Vec<CodeGeneration>,
2184) {
2185 let mut visitors = Vec::new();
2186 let mut root_visitors = Vec::new();
2187 let mut early_hoisted_stmts = FxIndexMap::default();
2188 let mut hoisted_stmts = FxIndexMap::default();
2189 let mut early_late_stmts = FxIndexMap::default();
2190 let mut late_stmts = FxIndexMap::default();
2191 for code_gen in code_gens {
2192 for CodeGenerationHoistedStmt { key, stmt } in code_gen.hoisted_stmts.drain(..) {
2193 hoisted_stmts.entry(key).or_insert(stmt);
2194 }
2195 for CodeGenerationHoistedStmt { key, stmt } in code_gen.early_hoisted_stmts.drain(..) {
2196 early_hoisted_stmts.insert(key.clone(), stmt);
2197 }
2198 for CodeGenerationHoistedStmt { key, stmt } in code_gen.late_stmts.drain(..) {
2199 late_stmts.insert(key.clone(), stmt);
2200 }
2201 for CodeGenerationHoistedStmt { key, stmt } in code_gen.early_late_stmts.drain(..) {
2202 early_late_stmts.insert(key.clone(), stmt);
2203 }
2204 for (path, visitor) in &code_gen.visitors {
2205 if path.is_empty() {
2206 root_visitors.push(&**visitor);
2207 } else {
2208 visitors.push((path, &**visitor));
2209 }
2210 }
2211 }
2212
2213 GLOBALS.set(globals, || {
2214 if !visitors.is_empty() {
2215 program.visit_mut_with_ast_path(
2216 &mut ApplyVisitors::new(visitors),
2217 &mut Default::default(),
2218 );
2219 }
2220 for pass in root_visitors {
2221 program.modify(pass);
2222 }
2223 });
2224
2225 match program {
2226 Program::Module(ast::Module { body, .. }) => {
2227 body.splice(
2228 0..0,
2229 early_hoisted_stmts
2230 .into_values()
2231 .chain(hoisted_stmts.into_values())
2232 .map(ModuleItem::Stmt),
2233 );
2234 body.extend(
2235 early_late_stmts
2236 .into_values()
2237 .chain(late_stmts.into_values())
2238 .map(ModuleItem::Stmt),
2239 );
2240 }
2241 Program::Script(Script { body, .. }) => {
2242 body.splice(
2243 0..0,
2244 early_hoisted_stmts
2245 .into_values()
2246 .chain(hoisted_stmts.into_values()),
2247 );
2248 body.extend(
2249 early_late_stmts
2250 .into_values()
2251 .chain(late_stmts.into_values()),
2252 );
2253 }
2254 };
2255}
2256
2257fn hygiene_rename_only(
2264 top_level_mark: Option<Mark>,
2265 is_import_mark: Mark,
2266 preserved_exports: &FxHashSet<Id>,
2267) -> impl VisitMut {
2268 struct HygieneRenamer<'a> {
2269 preserved_exports: &'a FxHashSet<Id>,
2270 is_import_mark: Mark,
2271 }
2272 impl swc_core::ecma::transforms::base::rename::Renamer for HygieneRenamer<'_> {
2274 type Target = Id;
2275
2276 const MANGLE: bool = false;
2277 const RESET_N: bool = true;
2278
2279 fn new_name_for(&self, orig: &Id, n: &mut usize) -> Atom {
2280 let res = if *n == 0 {
2281 orig.0.clone()
2282 } else {
2283 format!("{}{}", orig.0, n).into()
2284 };
2285 *n += 1;
2286 res
2287 }
2288
2289 fn preserve_name(&self, orig: &Id) -> bool {
2290 self.preserved_exports.contains(orig) || orig.1.has_mark(self.is_import_mark)
2291 }
2292 }
2293 swc_core::ecma::transforms::base::rename::renamer_keep_contexts(
2294 swc_core::ecma::transforms::base::hygiene::Config {
2295 top_level_mark: top_level_mark.unwrap_or_default(),
2296 ..Default::default()
2297 },
2298 HygieneRenamer {
2299 preserved_exports,
2300 is_import_mark,
2301 },
2302 )
2303}
2304
2305#[derive(Default)]
2306enum CodeGenResultSourceMap {
2307 #[default]
2308 None,
2310 Single {
2311 source_map: Arc<SourceMap>,
2312 },
2313 ScopeHoisting {
2314 modules_header_width: u32,
2317 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2318 source_maps: Vec<CodeGenResultSourceMap>,
2319 },
2320}
2321
2322impl CodeGenResultSourceMap {
2323 fn is_some(&self) -> bool {
2324 match self {
2325 CodeGenResultSourceMap::None => false,
2326 CodeGenResultSourceMap::Single { .. }
2327 | CodeGenResultSourceMap::ScopeHoisting { .. } => true,
2328 }
2329 }
2330}
2331
2332impl Debug for CodeGenResultSourceMap {
2333 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2334 match self {
2335 CodeGenResultSourceMap::None => write!(f, "CodeGenResultSourceMap::None"),
2336 CodeGenResultSourceMap::Single { source_map } => {
2337 write!(
2338 f,
2339 "CodeGenResultSourceMap::Single {{ source_map: {:?} }}",
2340 source_map.files().clone()
2341 )
2342 }
2343 CodeGenResultSourceMap::ScopeHoisting {
2344 modules_header_width,
2345 source_maps,
2346 ..
2347 } => write!(
2348 f,
2349 "CodeGenResultSourceMap::ScopeHoisting {{ modules_header_width: \
2350 {modules_header_width}, source_maps: {source_maps:?} }}",
2351 ),
2352 }
2353 }
2354}
2355
2356impl Files for CodeGenResultSourceMap {
2357 fn try_lookup_source_file(
2358 &self,
2359 pos: BytePos,
2360 ) -> Result<Option<Arc<SourceFile>>, SourceMapLookupError> {
2361 match self {
2362 CodeGenResultSourceMap::None => Ok(None),
2363 CodeGenResultSourceMap::Single { source_map } => source_map.try_lookup_source_file(pos),
2364 CodeGenResultSourceMap::ScopeHoisting {
2365 modules_header_width,
2366 lookup_table,
2367 source_maps,
2368 } => {
2369 let (module, pos) =
2370 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2371 source_maps[module].try_lookup_source_file(pos)
2372 }
2373 }
2374 }
2375
2376 fn is_in_file(&self, f: &Arc<SourceFile>, raw_pos: BytePos) -> bool {
2377 match self {
2378 CodeGenResultSourceMap::None => false,
2379 CodeGenResultSourceMap::Single { .. } => f.start_pos <= raw_pos && raw_pos < f.end_pos,
2380 CodeGenResultSourceMap::ScopeHoisting { .. } => {
2381 false
2387 }
2388 }
2389 }
2390
2391 fn map_raw_pos(&self, pos: BytePos) -> BytePos {
2392 match self {
2393 CodeGenResultSourceMap::None => BytePos::DUMMY,
2394 CodeGenResultSourceMap::Single { .. } => pos,
2395 CodeGenResultSourceMap::ScopeHoisting {
2396 modules_header_width,
2397 lookup_table,
2398 ..
2399 } => CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table).1,
2400 }
2401 }
2402}
2403
2404impl SourceMapper for CodeGenResultSourceMap {
2405 fn lookup_char_pos(&self, pos: BytePos) -> Loc {
2406 match self {
2407 CodeGenResultSourceMap::None => {
2408 panic!("CodeGenResultSourceMap::None cannot lookup_char_pos")
2409 }
2410 CodeGenResultSourceMap::Single { source_map } => source_map.lookup_char_pos(pos),
2411 CodeGenResultSourceMap::ScopeHoisting {
2412 modules_header_width,
2413 lookup_table,
2414 source_maps,
2415 } => {
2416 let (module, pos) =
2417 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2418 source_maps[module].lookup_char_pos(pos)
2419 }
2420 }
2421 }
2422 fn span_to_lines(&self, sp: Span) -> FileLinesResult {
2423 match self {
2424 CodeGenResultSourceMap::None => {
2425 panic!("CodeGenResultSourceMap::None cannot span_to_lines")
2426 }
2427 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_lines(sp),
2428 CodeGenResultSourceMap::ScopeHoisting {
2429 modules_header_width,
2430 lookup_table,
2431 source_maps,
2432 } => {
2433 let (module, lo) = CodeGenResultComments::decode_bytepos(
2434 *modules_header_width,
2435 sp.lo,
2436 lookup_table,
2437 );
2438 source_maps[module].span_to_lines(Span {
2439 lo,
2440 hi: CodeGenResultComments::decode_bytepos(
2441 *modules_header_width,
2442 sp.hi,
2443 lookup_table,
2444 )
2445 .1,
2446 })
2447 }
2448 }
2449 }
2450 fn span_to_string(&self, sp: Span) -> String {
2451 match self {
2452 CodeGenResultSourceMap::None => {
2453 panic!("CodeGenResultSourceMap::None cannot span_to_string")
2454 }
2455 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_string(sp),
2456 CodeGenResultSourceMap::ScopeHoisting {
2457 modules_header_width,
2458 lookup_table,
2459 source_maps,
2460 } => {
2461 let (module, lo) = CodeGenResultComments::decode_bytepos(
2462 *modules_header_width,
2463 sp.lo,
2464 lookup_table,
2465 );
2466 source_maps[module].span_to_string(Span {
2467 lo,
2468 hi: CodeGenResultComments::decode_bytepos(
2469 *modules_header_width,
2470 sp.hi,
2471 lookup_table,
2472 )
2473 .1,
2474 })
2475 }
2476 }
2477 }
2478 fn span_to_filename(&self, sp: Span) -> Arc<FileName> {
2479 match self {
2480 CodeGenResultSourceMap::None => {
2481 panic!("CodeGenResultSourceMap::None cannot span_to_filename")
2482 }
2483 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_filename(sp),
2484 CodeGenResultSourceMap::ScopeHoisting {
2485 modules_header_width,
2486 lookup_table,
2487 source_maps,
2488 } => {
2489 let (module, lo) = CodeGenResultComments::decode_bytepos(
2490 *modules_header_width,
2491 sp.lo,
2492 lookup_table,
2493 );
2494 source_maps[module].span_to_filename(Span {
2495 lo,
2496 hi: CodeGenResultComments::decode_bytepos(
2497 *modules_header_width,
2498 sp.hi,
2499 lookup_table,
2500 )
2501 .1,
2502 })
2503 }
2504 }
2505 }
2506 fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span> {
2507 match self {
2508 CodeGenResultSourceMap::None => {
2509 panic!("CodeGenResultSourceMap::None cannot merge_spans")
2510 }
2511 CodeGenResultSourceMap::Single { source_map } => source_map.merge_spans(sp_lhs, sp_rhs),
2512 CodeGenResultSourceMap::ScopeHoisting {
2513 modules_header_width,
2514 lookup_table,
2515 source_maps,
2516 } => {
2517 let (module_lhs, lo_lhs) = CodeGenResultComments::decode_bytepos(
2518 *modules_header_width,
2519 sp_lhs.lo,
2520 lookup_table,
2521 );
2522 let (module_rhs, lo_rhs) = CodeGenResultComments::decode_bytepos(
2523 *modules_header_width,
2524 sp_rhs.lo,
2525 lookup_table,
2526 );
2527 if module_lhs != module_rhs {
2528 return None;
2529 }
2530 source_maps[module_lhs].merge_spans(
2531 Span {
2532 lo: lo_lhs,
2533 hi: CodeGenResultComments::decode_bytepos(
2534 *modules_header_width,
2535 sp_lhs.hi,
2536 lookup_table,
2537 )
2538 .1,
2539 },
2540 Span {
2541 lo: lo_rhs,
2542 hi: CodeGenResultComments::decode_bytepos(
2543 *modules_header_width,
2544 sp_rhs.hi,
2545 lookup_table,
2546 )
2547 .1,
2548 },
2549 )
2550 }
2551 }
2552 }
2553 fn call_span_if_macro(&self, sp: Span) -> Span {
2554 match self {
2555 CodeGenResultSourceMap::None => {
2556 panic!("CodeGenResultSourceMap::None cannot call_span_if_macro")
2557 }
2558 CodeGenResultSourceMap::Single { source_map } => source_map.call_span_if_macro(sp),
2559 CodeGenResultSourceMap::ScopeHoisting {
2560 modules_header_width,
2561 lookup_table,
2562 source_maps,
2563 } => {
2564 let (module, lo) = CodeGenResultComments::decode_bytepos(
2565 *modules_header_width,
2566 sp.lo,
2567 lookup_table,
2568 );
2569 source_maps[module].call_span_if_macro(Span {
2570 lo,
2571 hi: CodeGenResultComments::decode_bytepos(
2572 *modules_header_width,
2573 sp.hi,
2574 lookup_table,
2575 )
2576 .1,
2577 })
2578 }
2579 }
2580 }
2581 fn doctest_offset_line(&self, _line: usize) -> usize {
2582 panic!("doctest_offset_line is not implemented for CodeGenResultSourceMap");
2583 }
2584 fn span_to_snippet(&self, sp: Span) -> Result<String, Box<SpanSnippetError>> {
2585 match self {
2586 CodeGenResultSourceMap::None => Err(Box::new(SpanSnippetError::SourceNotAvailable {
2587 filename: FileName::Anon,
2588 })),
2589 CodeGenResultSourceMap::Single { source_map } => source_map.span_to_snippet(sp),
2590 CodeGenResultSourceMap::ScopeHoisting {
2591 modules_header_width,
2592 lookup_table,
2593 source_maps,
2594 } => {
2595 let (module, lo) = CodeGenResultComments::decode_bytepos(
2596 *modules_header_width,
2597 sp.lo,
2598 lookup_table,
2599 );
2600 source_maps[module].span_to_snippet(Span {
2601 lo,
2602 hi: CodeGenResultComments::decode_bytepos(
2603 *modules_header_width,
2604 sp.hi,
2605 lookup_table,
2606 )
2607 .1,
2608 })
2609 }
2610 }
2611 }
2612 fn map_raw_pos(&self, pos: BytePos) -> BytePos {
2613 match self {
2614 CodeGenResultSourceMap::None => BytePos::DUMMY,
2615 CodeGenResultSourceMap::Single { .. } => pos,
2616 CodeGenResultSourceMap::ScopeHoisting {
2617 modules_header_width,
2618 lookup_table,
2619 ..
2620 } => CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table).1,
2621 }
2622 }
2623}
2624impl SourceMapperExt for CodeGenResultSourceMap {
2625 fn get_code_map(&self) -> &dyn SourceMapper {
2626 self
2627 }
2628}
2629
2630#[derive(Debug)]
2631enum CodeGenResultOriginalSourceMap {
2632 Single(Option<ResolvedVc<Box<dyn GenerateSourceMap>>>),
2633 ScopeHoisting(SmallVec<[ResolvedVc<Box<dyn GenerateSourceMap>>; 1]>),
2634}
2635
2636impl CodeGenResultOriginalSourceMap {
2637 fn iter(&self) -> impl Iterator<Item = ResolvedVc<Box<dyn GenerateSourceMap>>> {
2638 match self {
2639 CodeGenResultOriginalSourceMap::Single(map) => Either::Left(map.iter().copied()),
2640 CodeGenResultOriginalSourceMap::ScopeHoisting(maps) => {
2641 Either::Right(maps.iter().copied())
2642 }
2643 }
2644 }
2645}
2646
2647struct ModulePosition(u32, u32);
2649
2650enum CodeGenResultComments {
2651 Single {
2652 comments: Either<ImmutableComments, Arc<ImmutableComments>>,
2653 extra_comments: SwcComments,
2654 },
2655 ScopeHoisting {
2656 modules_header_width: u32,
2659 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2660 comments: Vec<CodeGenResultComments>,
2661 },
2662 Empty,
2663}
2664
2665unsafe impl Send for CodeGenResultComments {}
2666unsafe impl Sync for CodeGenResultComments {}
2667
2668impl CodeGenResultComments {
2669 const CONTINUATION_BIT: u32 = 1 << 31;
2670 const SIGN_EXTENSION_BIT: u32 = 1 << 30;
2671
2672 #[inline]
2673 fn encode_bytepos_impl(
2674 modules_header_width: u32,
2675 module: u32,
2676 pos: BytePos,
2677 push_into_lookup: &mut impl FnMut(u32, u32) -> Result<u32>,
2678 ) -> Result<BytePos> {
2679 if pos.is_dummy() {
2680 return Ok(pos);
2682 }
2683
2684 let header_width = modules_header_width + 2;
2715 let pos_width = 32 - header_width;
2716
2717 let pos = pos.0;
2718
2719 let old_high_bits = pos >> pos_width;
2720 let high_bits_set = if (2u32.pow(header_width) - 1) == old_high_bits {
2721 true
2722 } else if old_high_bits == 0 {
2723 false
2724 } else {
2725 let ix = push_into_lookup(module, pos)?;
2729 assert_eq!(ix & CodeGenResultComments::CONTINUATION_BIT, 0);
2731
2732 return Ok(BytePos(ix | CodeGenResultComments::CONTINUATION_BIT));
2733 };
2734
2735 let pos = pos & !((2u32.pow(header_width) - 1) << pos_width);
2736 let encoded_high_bits = if high_bits_set {
2737 CodeGenResultComments::SIGN_EXTENSION_BIT
2738 } else {
2739 0
2740 };
2741 let encoded_module = module << pos_width;
2742
2743 Ok(BytePos(encoded_module | encoded_high_bits | pos))
2744 }
2745
2746 fn take(&mut self) -> Self {
2747 std::mem::replace(self, CodeGenResultComments::Empty)
2748 }
2749
2750 fn consumable(&self) -> CodeGenResultCommentsConsumable<'_> {
2751 match self {
2752 CodeGenResultComments::Single {
2753 comments,
2754 extra_comments,
2755 } => CodeGenResultCommentsConsumable::Single {
2756 comments: match comments {
2757 Either::Left(comments) => comments.consumable(),
2758 Either::Right(comments) => comments.consumable(),
2759 },
2760 extra_comments,
2761 },
2762 CodeGenResultComments::ScopeHoisting {
2763 modules_header_width,
2764 lookup_table,
2765 comments,
2766 } => CodeGenResultCommentsConsumable::ScopeHoisting {
2767 modules_header_width: *modules_header_width,
2768 lookup_table: lookup_table.clone(),
2769 comments: comments.iter().map(|c| c.consumable()).collect(),
2770 },
2771 CodeGenResultComments::Empty => CodeGenResultCommentsConsumable::Empty,
2772 }
2773 }
2774
2775 fn encode_bytepos(
2776 modules_header_width: u32,
2777 module: u32,
2778 pos: BytePos,
2779 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2780 ) -> Result<BytePos> {
2781 let mut push = |module: u32, pos_u32: u32| -> Result<u32> {
2782 let mut lookup_table = lookup_table
2783 .lock()
2784 .map_err(|_| anyhow!("Failed to grab lock on the index map for byte positions"))?;
2785 let ix = lookup_table.len() as u32;
2786 if ix >= 1 << 30 {
2787 bail!("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 encode_bytepos_with_vec(
2796 modules_header_width: u32,
2797 module: u32,
2798 pos: BytePos,
2799 lookup_table: &mut Vec<ModulePosition>,
2800 ) -> Result<BytePos> {
2801 let mut push = |module: u32, pos_u32: u32| -> Result<u32> {
2802 let ix = lookup_table.len() as u32;
2803 if ix >= 1 << 30 {
2804 bail!("Too many byte positions being stored");
2805 }
2806 lookup_table.push(ModulePosition(module, pos_u32));
2807 Ok(ix)
2808 };
2809 Self::encode_bytepos_impl(modules_header_width, module, pos, &mut push)
2810 }
2811
2812 fn decode_bytepos(
2813 modules_header_width: u32,
2814 pos: BytePos,
2815 lookup_table: &Mutex<Vec<ModulePosition>>,
2816 ) -> (usize, BytePos) {
2817 if pos.is_dummy() {
2818 panic!("Cannot decode dummy BytePos");
2820 }
2821
2822 let header_width = modules_header_width + 2;
2823 let pos_width = 32 - header_width;
2824
2825 if (CodeGenResultComments::CONTINUATION_BIT & pos.0)
2826 == CodeGenResultComments::CONTINUATION_BIT
2827 {
2828 let lookup_table = lookup_table
2829 .lock()
2830 .expect("Failed to grab lock on the index map for byte position");
2831 let ix = pos.0 & !CodeGenResultComments::CONTINUATION_BIT;
2832 let ModulePosition(module, pos) = lookup_table[ix as usize];
2833
2834 return (module as usize, BytePos(pos));
2835 }
2836
2837 let high_bits_set = pos.0 >> 30 & 1 == 1;
2838 let module = (pos.0 << 2) >> (pos_width + 2);
2839 let pos = pos.0 & !((2u32.pow(header_width) - 1) << pos_width);
2840 let pos = if high_bits_set {
2841 pos | ((2u32.pow(header_width) - 1) << pos_width)
2842 } else {
2843 pos
2844 };
2845 (module as usize, BytePos(pos))
2846 }
2847}
2848
2849enum CodeGenResultCommentsConsumable<'a> {
2850 Single {
2851 comments: CowComments<'a>,
2852 extra_comments: &'a SwcComments,
2853 },
2854 ScopeHoisting {
2855 modules_header_width: u32,
2856 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2857 comments: Vec<CodeGenResultCommentsConsumable<'a>>,
2858 },
2859 Empty,
2860}
2861fn encode_module_into_comment_span(
2865 modules_header_width: u32,
2866 module: usize,
2867 mut comment: Comment,
2868 lookup_table: Arc<Mutex<Vec<ModulePosition>>>,
2869) -> Comment {
2870 comment.span.lo = CodeGenResultComments::encode_bytepos(
2871 modules_header_width,
2872 module as u32,
2873 comment.span.lo,
2874 lookup_table.clone(),
2875 )
2876 .unwrap();
2877 comment.span.hi = CodeGenResultComments::encode_bytepos(
2878 modules_header_width,
2879 module as u32,
2880 comment.span.hi,
2881 lookup_table,
2882 )
2883 .unwrap();
2884 comment
2885}
2886
2887impl Comments for CodeGenResultCommentsConsumable<'_> {
2888 fn add_leading(&self, _pos: BytePos, _cmt: Comment) {
2889 unimplemented!("add_leading")
2890 }
2891
2892 fn add_leading_comments(&self, _pos: BytePos, _comments: Vec<Comment>) {
2893 unimplemented!("add_leading_comments")
2894 }
2895
2896 fn has_leading(&self, pos: BytePos) -> bool {
2897 if pos.is_dummy() {
2898 return false;
2899 }
2900 match self {
2901 Self::Single {
2902 comments,
2903 extra_comments,
2904 } => comments.has_leading(pos) || extra_comments.has_leading(pos),
2905 Self::ScopeHoisting {
2906 modules_header_width,
2907 lookup_table,
2908 comments,
2909 } => {
2910 let (module, pos) =
2911 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2912 comments[module].has_leading(pos)
2913 }
2914 Self::Empty => false,
2915 }
2916 }
2917
2918 fn move_leading(&self, _from: BytePos, _to: BytePos) {
2919 unimplemented!("move_leading")
2920 }
2921
2922 fn take_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
2923 if pos.is_dummy() {
2924 return None;
2925 }
2926 match self {
2927 Self::Single {
2928 comments,
2929 extra_comments,
2930 } => merge_option_vec(comments.take_leading(pos), extra_comments.take_leading(pos)),
2931 Self::ScopeHoisting {
2932 modules_header_width,
2933 lookup_table,
2934 comments,
2935 } => {
2936 let (module, pos) =
2937 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2938 comments[module].take_leading(pos).map(|comments| {
2939 comments
2940 .into_iter()
2941 .map(|c| {
2942 encode_module_into_comment_span(
2943 *modules_header_width,
2944 module,
2945 c,
2946 lookup_table.clone(),
2947 )
2948 })
2949 .collect()
2950 })
2951 }
2952 Self::Empty => None,
2953 }
2954 }
2955
2956 fn get_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
2957 if pos.is_dummy() {
2958 return None;
2959 }
2960 match self {
2961 Self::Single {
2962 comments,
2963 extra_comments,
2964 } => merge_option_vec(comments.get_leading(pos), extra_comments.get_leading(pos)),
2965 Self::ScopeHoisting {
2966 modules_header_width,
2967 lookup_table,
2968 comments,
2969 } => {
2970 let (module, pos) =
2971 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
2972 comments[module].get_leading(pos).map(|comments| {
2973 comments
2974 .into_iter()
2975 .map(|c| {
2976 encode_module_into_comment_span(
2977 *modules_header_width,
2978 module,
2979 c,
2980 lookup_table.clone(),
2981 )
2982 })
2983 .collect()
2984 })
2985 }
2986 Self::Empty => None,
2987 }
2988 }
2989
2990 fn add_trailing(&self, _pos: BytePos, _cmt: Comment) {
2991 unimplemented!("add_trailing")
2992 }
2993
2994 fn add_trailing_comments(&self, _pos: BytePos, _comments: Vec<Comment>) {
2995 unimplemented!("add_trailing_comments")
2996 }
2997
2998 fn has_trailing(&self, pos: BytePos) -> bool {
2999 if pos.is_dummy() {
3000 return false;
3001 }
3002 match self {
3003 Self::Single {
3004 comments,
3005 extra_comments,
3006 } => comments.has_trailing(pos) || extra_comments.has_trailing(pos),
3007 Self::ScopeHoisting {
3008 modules_header_width,
3009 lookup_table,
3010 comments,
3011 } => {
3012 let (module, pos) =
3013 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
3014 comments[module].has_trailing(pos)
3015 }
3016 Self::Empty => false,
3017 }
3018 }
3019
3020 fn move_trailing(&self, _from: BytePos, _to: BytePos) {
3021 unimplemented!("move_trailing")
3022 }
3023
3024 fn take_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
3025 if pos.is_dummy() {
3026 return None;
3027 }
3028 match self {
3029 Self::Single {
3030 comments,
3031 extra_comments,
3032 } => merge_option_vec(
3033 comments.take_trailing(pos),
3034 extra_comments.take_trailing(pos),
3035 ),
3036 Self::ScopeHoisting {
3037 modules_header_width,
3038 lookup_table,
3039 comments,
3040 } => {
3041 let (module, pos) =
3042 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
3043 comments[module].take_trailing(pos).map(|comments| {
3044 comments
3045 .into_iter()
3046 .map(|c| {
3047 encode_module_into_comment_span(
3048 *modules_header_width,
3049 module,
3050 c,
3051 lookup_table.clone(),
3052 )
3053 })
3054 .collect()
3055 })
3056 }
3057 Self::Empty => None,
3058 }
3059 }
3060
3061 fn get_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
3062 if pos.is_dummy() {
3063 return None;
3064 }
3065 match self {
3066 Self::Single {
3067 comments,
3068 extra_comments,
3069 } => merge_option_vec(comments.get_leading(pos), extra_comments.get_leading(pos)),
3070 Self::ScopeHoisting {
3071 modules_header_width,
3072 lookup_table,
3073 comments,
3074 } => {
3075 let (module, pos) =
3076 CodeGenResultComments::decode_bytepos(*modules_header_width, pos, lookup_table);
3077 comments[module].get_leading(pos).map(|comments| {
3078 comments
3079 .into_iter()
3080 .map(|c| {
3081 encode_module_into_comment_span(
3082 *modules_header_width,
3083 module,
3084 c,
3085 lookup_table.clone(),
3086 )
3087 })
3088 .collect()
3089 })
3090 }
3091 Self::Empty => None,
3092 }
3093 }
3094
3095 fn add_pure_comment(&self, _pos: BytePos) {
3096 unimplemented!("add_pure_comment")
3097 }
3098}
3099
3100fn merge_option_vec<T>(a: Option<Vec<T>>, b: Option<Vec<T>>) -> Option<Vec<T>> {
3101 match (a, b) {
3102 (Some(a), Some(b)) => Some(a.into_iter().chain(b).collect()),
3103 (Some(a), None) => Some(a),
3104 (None, Some(b)) => Some(b),
3105 (None, None) => None,
3106 }
3107}
3108
3109#[cfg(test)]
3110mod tests {
3111 use super::*;
3112 fn bytepos_ensure_identical(modules_header_width: u32, pos: BytePos) {
3113 let module_count = 2u32.pow(modules_header_width);
3114 let lookup_table = Arc::new(Mutex::new(Vec::new()));
3115
3116 for module in [
3117 0,
3118 1,
3119 2,
3120 module_count / 2,
3121 module_count.wrapping_sub(5),
3122 module_count.wrapping_sub(1),
3123 ]
3124 .into_iter()
3125 .filter(|&m| m < module_count)
3126 {
3127 let encoded = CodeGenResultComments::encode_bytepos(
3128 modules_header_width,
3129 module,
3130 pos,
3131 lookup_table.clone(),
3132 )
3133 .unwrap();
3134 let (decoded_module, decoded_pos) =
3135 CodeGenResultComments::decode_bytepos(modules_header_width, encoded, &lookup_table);
3136 assert_eq!(
3137 decoded_module as u32, module,
3138 "Testing width {modules_header_width} and pos {pos:?}"
3139 );
3140 assert_eq!(
3141 decoded_pos, pos,
3142 "Testing width {modules_header_width} and pos {pos:?}"
3143 );
3144 }
3145 }
3146
3147 #[test]
3148 fn test_encode_decode_bytepos_format() {
3149 let table = Arc::new(Mutex::new(Vec::new()));
3150
3151 for (pos, module, modules_header_width, result) in [
3152 (
3153 0b00000000000000000000000000000101,
3154 0b1,
3155 1,
3156 0b00100000000000000000000000000101,
3157 ),
3158 (
3159 0b00000000000000000000000000000101,
3160 0b01,
3161 2,
3162 0b00010000000000000000000000000101,
3163 ),
3164 (
3165 0b11111111111111110000000000000101,
3166 0b0110,
3167 4,
3168 0b01011011111111110000000000000101,
3169 ),
3170 (
3171 BytePos::PLACEHOLDER.0,
3172 0b01111,
3173 5,
3174 0b01011111111111111111111111111101,
3175 ),
3176 (
3177 BytePos::PURE.0,
3178 0b01111,
3179 5,
3180 0b01011111111111111111111111111110,
3181 ),
3182 (
3183 BytePos::SYNTHESIZED.0,
3184 0b01111,
3185 5,
3186 0b01011111111111111111111111111111,
3187 ),
3188 (
3191 0b00000111111111110000000000000101,
3192 0b0001,
3193 4,
3194 0b10000000000000000000000000000000,
3195 ),
3196 (
3198 0b00000111111111110000000000111110,
3199 0b0001,
3200 4,
3201 0b10000000000000000000000000000001,
3202 ),
3203 (BytePos::DUMMY.0, 0b0001, 4, BytePos::DUMMY.0),
3205 ] {
3206 let encoded = CodeGenResultComments::encode_bytepos(
3207 modules_header_width,
3208 module,
3209 BytePos(pos),
3210 table.clone(),
3211 )
3212 .unwrap();
3213 assert_eq!(encoded.0, result);
3214
3215 if encoded.0 & CodeGenResultComments::CONTINUATION_BIT
3217 == CodeGenResultComments::CONTINUATION_BIT
3218 {
3219 let index = encoded.0 & !CodeGenResultComments::CONTINUATION_BIT;
3220 let ModulePosition(encoded_module, encoded_pos) =
3221 table.lock().unwrap()[index as usize];
3222 assert_eq!(encoded_module, module);
3223 assert_eq!(encoded_pos, pos);
3224 }
3225 }
3226 }
3227
3228 #[test]
3229 fn test_encode_decode_bytepos_lossless() {
3230 const DUMMY_RESERVE: u32 = u32::MAX - 2_u32.pow(16);
3232
3233 for modules_header_width in 1..=10 {
3234 for pos in [
3235 BytePos(1),
3237 BytePos(2),
3238 BytePos(100),
3239 BytePos(4_000_000),
3240 BytePos(600_000_000),
3241 BytePos(u32::MAX - 3), BytePos::PLACEHOLDER,
3243 BytePos::SYNTHESIZED,
3244 BytePos::PURE,
3245 BytePos(DUMMY_RESERVE),
3246 BytePos(DUMMY_RESERVE + 10),
3247 BytePos(DUMMY_RESERVE + 10000),
3248 ] {
3249 bytepos_ensure_identical(modules_header_width, pos);
3250 }
3251 }
3252 }
3253}