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