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