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