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