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