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