diff --git a/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs b/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs index 3a4506e7090d6..fb7fa8cc38e9a 100644 --- a/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs +++ b/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs @@ -35,7 +35,7 @@ use crate::{ utils::{AstPathRange, unparen}, }; -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct EffectsBlock { pub effects: Vec, pub range: AstPathRange, @@ -47,7 +47,7 @@ impl EffectsBlock { } } -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum ConditionalKind { /// The blocks of an `if` statement without an `else` block. If { then: Box }, @@ -117,7 +117,7 @@ impl ConditionalKind { } } -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum EffectArg { Value(JsValue), Closure(JsValue, Box), @@ -140,7 +140,7 @@ impl EffectArg { } } -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum Effect { /// Some condition which affects which effects might be executed. If the /// condition evaluates to some compile-time constant, we can use that @@ -2428,14 +2428,16 @@ impl VisitAstPath for Analyzer<'_> { self.effects.append(&mut block); self.effects.append(&mut handler); if let Some(finalizer) = stmt.finalizer.as_ref() { - let mut ast_path = - ast_path.with_guard(AstParentNodeRef::TryStmt(stmt, TryStmtField::Finalizer)); - finalizer.visit_with_ast_path(self, &mut ast_path); + { + let mut ast_path = + ast_path.with_guard(AstParentNodeRef::TryStmt(stmt, TryStmtField::Finalizer)); + finalizer.visit_with_ast_path(self, &mut ast_path); + } // If a finally block early returns the parent block does too. if self.end_early_return_block() { self.early_return_stack.push(EarlyReturn::Always { prev_effects: take(&mut self.effects), - start_ast_path: as_parent_path(&ast_path), + start_ast_path: as_parent_path(ast_path), }); } }; diff --git a/turbopack/crates/turbopack-ecmascript/src/code_gen.rs b/turbopack/crates/turbopack-ecmascript/src/code_gen.rs index a0518805682ff..2af172fb6e00d 100644 --- a/turbopack/crates/turbopack-ecmascript/src/code_gen.rs +++ b/turbopack/crates/turbopack-ecmascript/src/code_gen.rs @@ -173,7 +173,9 @@ impl_modify!(visit_mut_block_stmt, BlockStmt); impl_modify!(visit_mut_switch_case, SwitchCase); impl_modify!(visit_mut_program, Program); -#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)] +#[derive( + PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue, Hash, Debug, +)] pub enum CodeGen { // AMD occurs very rarely and makes the enum much bigger AmdDefineWithDependenciesCodeGen(Box), diff --git a/turbopack/crates/turbopack-ecmascript/src/references/amd.rs b/turbopack/crates/turbopack-ecmascript/src/references/amd.rs index b6de7493329e5..ccaa1136b3e57 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/amd.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/amd.rs @@ -98,6 +98,7 @@ impl ChunkableModuleReference for AmdDefineAssetReference {} TraceRawVcs, Clone, NonLocalValue, + Hash, )] pub enum AmdDefineDependencyElement { Request { @@ -120,6 +121,7 @@ pub enum AmdDefineDependencyElement { Copy, Clone, NonLocalValue, + Hash, )] pub enum AmdDefineFactoryType { Unknown, @@ -127,7 +129,9 @@ pub enum AmdDefineFactoryType { Value, } -#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)] +#[derive( + PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue, Hash, Debug, +)] pub struct AmdDefineWithDependenciesCodeGen { dependencies_requests: Vec, origin: ResolvedVc>, diff --git a/turbopack/crates/turbopack-ecmascript/src/references/cjs.rs b/turbopack/crates/turbopack-ecmascript/src/references/cjs.rs index 1e6c8d255639d..d22a482133973 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/cjs.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/cjs.rs @@ -150,7 +150,9 @@ impl IntoCodeGenReference for CjsRequireAssetReference { } } -#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)] +#[derive( + PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue, Hash, Debug, +)] pub struct CjsRequireAssetReferenceCodeGen { reference: ResolvedVc, path: AstPath, @@ -274,7 +276,9 @@ impl IntoCodeGenReference for CjsRequireResolveAssetReference { } } -#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)] +#[derive( + PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue, Hash, Debug, +)] pub struct CjsRequireResolveAssetReferenceCodeGen { reference: ResolvedVc, path: AstPath, @@ -334,7 +338,9 @@ impl CjsRequireResolveAssetReferenceCodeGen { } } -#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)] +#[derive( + PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue, Debug, Hash, +)] pub struct CjsRequireCacheAccess { pub path: AstPath, } diff --git a/turbopack/crates/turbopack-ecmascript/src/references/constant_condition.rs b/turbopack/crates/turbopack-ecmascript/src/references/constant_condition.rs index 12d8a10baba5e..dc6fd8b57faca 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/constant_condition.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/constant_condition.rs @@ -19,7 +19,9 @@ pub enum ConstantConditionValue { Nullish, } -#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)] +#[derive( + PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue, Debug, Hash, +)] pub struct ConstantConditionCodeGen { value: ConstantConditionValue, path: AstPath, diff --git a/turbopack/crates/turbopack-ecmascript/src/references/dynamic_expression.rs b/turbopack/crates/turbopack-ecmascript/src/references/dynamic_expression.rs index 02e5dba56d187..45fea8527893b 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/dynamic_expression.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/dynamic_expression.rs @@ -10,13 +10,17 @@ use crate::{ create_visitor, }; -#[derive(PartialEq, Eq, TraceRawVcs, Serialize, Deserialize, ValueDebugFormat, NonLocalValue)] +#[derive( + PartialEq, Eq, TraceRawVcs, Serialize, Deserialize, ValueDebugFormat, NonLocalValue, Hash, Debug, +)] enum DynamicExpressionType { Promise, Normal, } -#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)] +#[derive( + PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue, Debug, Hash, +)] pub struct DynamicExpression { path: AstPath, ty: DynamicExpressionType, diff --git a/turbopack/crates/turbopack-ecmascript/src/references/esm/dynamic.rs b/turbopack/crates/turbopack-ecmascript/src/references/esm/dynamic.rs index b77e5a82d06ba..33f438f3ed423 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/esm/dynamic.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/esm/dynamic.rs @@ -121,7 +121,9 @@ impl IntoCodeGenReference for EsmAsyncAssetReference { } } -#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)] +#[derive( + PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue, Hash, Debug, +)] pub struct EsmAsyncAssetReferenceCodeGen { path: AstPath, reference: ResolvedVc, diff --git a/turbopack/crates/turbopack-ecmascript/src/references/esm/meta.rs b/turbopack/crates/turbopack-ecmascript/src/references/esm/meta.rs index 88407cb45de2d..8b516d39e7e76 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/esm/meta.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/esm/meta.rs @@ -26,7 +26,9 @@ use crate::{ /// in the file. But we must only initialize the binding a single time. /// /// This singleton behavior must be enforced by the caller! -#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)] +#[derive( + PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue, Debug, Hash, +)] pub struct ImportMetaBinding { path: FileSystemPath, } @@ -85,7 +87,9 @@ impl From for CodeGen { /// /// There can be many references to import.meta, and they appear at any nesting /// in the file. But all references refer to the same mutable object. -#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)] +#[derive( + PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue, Hash, Debug, +)] pub struct ImportMetaRef { ast_path: AstPath, } diff --git a/turbopack/crates/turbopack-ecmascript/src/references/esm/module_id.rs b/turbopack/crates/turbopack-ecmascript/src/references/esm/module_id.rs index 4e112856cbeb9..a5f69e967d526 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/esm/module_id.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/esm/module_id.rs @@ -73,7 +73,9 @@ impl IntoCodeGenReference for EsmModuleIdAssetReference { } } -#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)] +#[derive( + PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue, Hash, Debug, +)] pub struct EsmModuleIdAssetReferenceCodeGen { path: AstPath, reference: ResolvedVc, diff --git a/turbopack/crates/turbopack-ecmascript/src/references/esm/module_item.rs b/turbopack/crates/turbopack-ecmascript/src/references/esm/module_item.rs index 752c42bbe1f3c..67dc999ca5571 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/esm/module_item.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/esm/module_item.rs @@ -22,7 +22,9 @@ use crate::{ /// Makes code changes to remove export/import declarations and places the /// expr/decl in a normal statement. Unnamed expr/decl will be named with the /// magic identifier "export default" -#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)] +#[derive( + PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue, Debug, Hash, +)] pub struct EsmModuleItem { pub path: AstPath, } diff --git a/turbopack/crates/turbopack-ecmascript/src/references/esm/url.rs b/turbopack/crates/turbopack-ecmascript/src/references/esm/url.rs index 8149229bff154..18a9a8bfa2856 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/esm/url.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/esm/url.rs @@ -146,7 +146,9 @@ impl IntoCodeGenReference for UrlAssetReference { } } -#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)] +#[derive( + PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue, Hash, Debug, +)] pub struct UrlAssetReferenceCodeGen { reference: ResolvedVc, path: AstPath, diff --git a/turbopack/crates/turbopack-ecmascript/src/references/exports_info.rs b/turbopack/crates/turbopack-ecmascript/src/references/exports_info.rs index 95abefb922e27..d37379a9f91c1 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/exports_info.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/exports_info.rs @@ -23,7 +23,9 @@ use crate::{ /// initialize the binding a single time. /// /// This singleton behavior must be enforced by the caller! -#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)] +#[derive( + PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue, Hash, Debug, +)] pub struct ExportsInfoBinding {} impl ExportsInfoBinding { @@ -89,7 +91,9 @@ impl From for CodeGen { /// /// There can be many references, and they appear at any nesting in the file. But all references /// refer to the same mutable object. -#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)] +#[derive( + PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue, Hash, Debug, +)] pub struct ExportsInfoRef { ast_path: AstPath, } diff --git a/turbopack/crates/turbopack-ecmascript/src/references/ident.rs b/turbopack/crates/turbopack-ecmascript/src/references/ident.rs index 5920933589944..8e6167e87b445 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/ident.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/ident.rs @@ -11,7 +11,9 @@ use crate::{ create_visitor, }; -#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)] +#[derive( + PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue, Debug, Hash, +)] pub struct IdentReplacement { value: RcStr, path: AstPath, diff --git a/turbopack/crates/turbopack-ecmascript/src/references/member.rs b/turbopack/crates/turbopack-ecmascript/src/references/member.rs index f96232d35e58d..d78141970e210 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/member.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/member.rs @@ -20,7 +20,9 @@ use crate::{ create_visitor, }; -#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)] +#[derive( + PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue, Hash, Debug, +)] pub struct MemberReplacement { key: RcStr, value: RcStr, diff --git a/turbopack/crates/turbopack-ecmascript/src/references/mod.rs b/turbopack/crates/turbopack-ecmascript/src/references/mod.rs index 3a7a0535fa49c..1e5fdfdbdb86e 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/mod.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/mod.rs @@ -59,6 +59,7 @@ use swc_core::{ }, }, }; +use tokio::sync::OnceCell; use tracing::Instrument; use turbo_rcstr::{RcStr, rcstr}; use turbo_tasks::{ @@ -210,9 +211,16 @@ impl AnalyzeEcmascriptModuleResult { } } +/// In debug builds, use FxIndexSet to catch duplicate code gens +/// In release builds, use Vec for better performance +#[cfg(debug_assertions)] +type CodeGenCollection = FxIndexSet; +#[cfg(not(debug_assertions))] +type CodeGenCollection = Vec; + /// A temporary analysis result builder to pass around, to be turned into an /// `Vc` eventually. -pub struct AnalyzeEcmascriptModuleResultBuilder { +struct AnalyzeEcmascriptModuleResultBuilder { analyze_mode: AnalyzeMode, references: FxIndexSet>>, @@ -226,16 +234,18 @@ pub struct AnalyzeEcmascriptModuleResultBuilder { // This caches repeated access because EsmAssetReference::new is not a turbo task function. esm_references_rewritten: FxHashMap>>, - code_gens: Vec, + code_gens: CodeGenCollection, exports: EcmascriptExports, async_module: ResolvedVc, successful: bool, source_map: Option>>, has_side_effect_free_directive: bool, + #[cfg(debug_assertions)] + ident: RcStr, } impl AnalyzeEcmascriptModuleResultBuilder { - pub fn new(analyze_mode: AnalyzeMode) -> Self { + fn new(analyze_mode: AnalyzeMode) -> Self { Self { analyze_mode, references: Default::default(), @@ -250,6 +260,8 @@ impl AnalyzeEcmascriptModuleResultBuilder { successful: false, source_map: None, has_side_effect_free_directive: false, + #[cfg(debug_assertions)] + ident: Default::default(), } } @@ -292,7 +304,20 @@ impl AnalyzeEcmascriptModuleResultBuilder { C: Into, { if self.analyze_mode.is_code_gen() { - self.code_gens.push(code_gen.into()) + #[cfg(debug_assertions)] + { + let (index, added) = self.code_gens.insert_full(code_gen.into()); + debug_assert!( + added, + "Duplicate code gen added: {:?} in {}", + self.code_gens.get_index(index).unwrap(), + self.ident + ); + } + #[cfg(not(debug_assertions))] + { + self.code_gens.push(code_gen.into()); + } } } @@ -311,7 +336,7 @@ impl AnalyzeEcmascriptModuleResultBuilder { self.async_module = ResolvedVc::cell(Some(async_module)); } - /// Set whether this module is side-efffect free according to a user-provided directive. + /// Set whether this module is side-effect free according to a user-provided directive. pub fn set_has_side_effect_free_directive(&mut self, value: bool) { self.has_side_effect_free_directive = value; } @@ -407,6 +432,12 @@ impl AnalyzeEcmascriptModuleResultBuilder { } self.code_gens.shrink_to_fit(); + + #[cfg(debug_assertions)] + let code_generation = self.code_gens.into_iter().collect::>(); + #[cfg(not(debug_assertions))] + let code_generation = self.code_gens; + Ok(AnalyzeEcmascriptModuleResult::cell( AnalyzeEcmascriptModuleResult { references, @@ -415,7 +446,7 @@ impl AnalyzeEcmascriptModuleResultBuilder { esm_reexport_references: ResolvedVc::cell( esm_reexport_references.unwrap_or_default(), ), - code_generation: ResolvedVc::cell(self.code_gens), + code_generation: ResolvedVc::cell(code_generation), exports: self.exports.resolved_cell(), async_module: self.async_module, has_side_effect_free_directive: self.has_side_effect_free_directive, @@ -527,8 +558,12 @@ async fn analyze_ecmascript_module_internal( let analyze_mode = options.analyze_mode; let origin = ResolvedVc::upcast::>(module); - let mut analysis = AnalyzeEcmascriptModuleResultBuilder::new(analyze_mode); let path = &*origin.origin_path().await?; + let mut analysis = AnalyzeEcmascriptModuleResultBuilder::new(analyze_mode); + #[cfg(debug_assertions)] + { + analysis.ident = source.ident().to_string().owned().await?; + } // Is this a typescript file that requires analyzing type references? let analyze_types = match &ty { @@ -1633,44 +1668,133 @@ async fn handle_call) + Send + Sync>( ignore_dynamic_requests, url_rewrite_behavior, collect_affecting_sources, - allow_project_root_tracing, + allow_project_root_tracing: _, .. } = state; - fn explain_args(args: &[JsValue]) -> (String, String) { - JsValue::explain_args(args, 10, 2) - } - let linked_args = |args: Vec| async move { - args.into_iter() - .map(|arg| { - let add_effects = &add_effects; - async move { - let value = match arg { - EffectArg::Value(value) => value, - EffectArg::Closure(value, block) => { - add_effects(block.effects); - value - } - EffectArg::Spread => { - JsValue::unknown_empty(true, "spread is not supported yet") - } - }; - state.link_value(value, ImportAttributes::empty_ref()).await - } + + // Process all effects first so they happen exactly once. + // If we end up modeling the behavior of the closures passed to any of these functions then we + // will need to inline this into the appropriate spot just like Array.prototype.map support. + let unlinked_args = args + .into_iter() + .map(|effect_arg| match effect_arg { + EffectArg::Value(value) => value, + EffectArg::Closure(value, block) => { + add_effects(block.effects); + value + } + EffectArg::Spread => JsValue::unknown_empty(true, "spread is not supported yet"), + }) + .collect::>(); + + // Create a OnceCell to cache linked args across multiple calls + let linked_args_cache = OnceCell::new(); + + // Create the lazy linking closure that will be passed to handle_well_known_function_call + let linked_args = || async { + linked_args_cache + .get_or_try_init(|| async { + unlinked_args + .iter() + .cloned() + .map(|arg| state.link_value(arg, ImportAttributes::empty_ref())) + .try_join() + .await }) - .try_join() .await }; + match func { + JsValue::Alternatives { + total_nodes: _, + values, + logical_property: _, + } => { + for alt in values { + if let JsValue::WellKnownFunction(wkf) = alt { + handle_well_known_function_call( + wkf, + new, + &linked_args, + handler, + span, + ignore_dynamic_requests, + analysis, + origin, + compile_time_info, + url_rewrite_behavior, + source, + ast_path, + in_try, + state, + collect_affecting_sources, + ) + .await?; + } + } + } + JsValue::WellKnownFunction(wkf) => { + handle_well_known_function_call( + wkf, + new, + &linked_args, + handler, + span, + ignore_dynamic_requests, + analysis, + origin, + compile_time_info, + url_rewrite_behavior, + source, + ast_path, + in_try, + state, + collect_affecting_sources, + ) + .await?; + } + _ => {} + } + + Ok(()) +} + +async fn handle_well_known_function_call<'a, F, Fut>( + func: WellKnownFunctionKind, + new: bool, + linked_args: &F, + handler: &Handler, + span: Span, + ignore_dynamic_requests: bool, + analysis: &mut AnalyzeEcmascriptModuleResultBuilder, + origin: ResolvedVc>, + compile_time_info: ResolvedVc, + url_rewrite_behavior: Option, + source: ResolvedVc>, + ast_path: &[AstParentKind], + in_try: bool, + state: &'a AnalysisState<'a>, + collect_affecting_sources: bool, +) -> Result<()> +where + F: Fn() -> Fut, + Fut: Future>>, +{ + fn explain_args(args: &[JsValue]) -> (String, String) { + JsValue::explain_args(args, 10, 2) + } + let get_traced_project_dir = async || -> Result { // readFileSync("./foo") should always be relative to the project root, but this is - // dangerous inside of node_modules as it can cause a lot of false positives in the tracing, - // if some package does `path.join(dynamic)`, it would include everything from the project - // root as well. + // dangerous inside of node_modules as it can cause a lot of false positives in the + // tracing, if some package does `path.join(dynamic)`, it would include + // everything from the project root as well. // - // Also, when there's no cwd set (i.e. in a tracing-specific module context, as we shouldn't - // assume a `process.cwd()` for all of node_modules), fallback to the source file directory. - // This still allows relative file accesses, just not from the project root. - if allow_project_root_tracing + // Also, when there's no cwd set (i.e. in a tracing-specific module context, as we + // shouldn't assume a `process.cwd()` for all of node_modules), fallback to + // the source file directory. This still allows relative file accesses, just + // not from the project root. + if state.allow_project_root_tracing && let Some(cwd) = compile_time_info.environment().cwd().owned().await? { Ok(cwd) @@ -1681,11 +1805,10 @@ async fn handle_call) + Send + Sync>( let get_issue_source = || IssueSource::from_swc_offsets(source, span.lo.to_u32(), span.hi.to_u32()); - if new { match func { - JsValue::WellKnownFunction(WellKnownFunctionKind::URLConstructor) => { - let args = linked_args(args).await?; + WellKnownFunctionKind::URLConstructor => { + let args = linked_args().await?; if let [ url, JsValue::Member( @@ -1698,7 +1821,7 @@ async fn handle_call) + Send + Sync>( { let pat = js_value_to_pattern(url); if !pat.has_constant_parts() { - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!("new URL({args}) is very dynamic{hints}",), @@ -1725,12 +1848,12 @@ async fn handle_call) + Send + Sync>( } return Ok(()); } - JsValue::WellKnownFunction(WellKnownFunctionKind::WorkerConstructor) => { - let args = linked_args(args).await?; + WellKnownFunctionKind::WorkerConstructor => { + let args = linked_args().await?; if let Some(url @ JsValue::Url(_, JsValueUrlKind::Relative)) = args.first() { let pat = js_value_to_pattern(url); if !pat.has_constant_parts() { - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!("new Worker({args}) is very dynamic{hints}",), @@ -1760,15 +1883,13 @@ async fn handle_call) + Send + Sync>( // Ignore (e.g. dynamic parameter or string literal), just as Webpack does return Ok(()); } - JsValue::WellKnownFunction(WellKnownFunctionKind::NodeWorkerConstructor) - if analysis.analyze_mode.is_tracing() => - { + WellKnownFunctionKind::NodeWorkerConstructor if analysis.analyze_mode.is_tracing() => { // Only for tracing, not for bundling (yet?) - let args = linked_args(args).await?; + let args = linked_args().await?; if !args.is_empty() { let pat = js_value_to_pattern(&args[0]); if !pat.has_constant_parts() { - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!("new Worker({args}) is very dynamic{hints}",), @@ -1793,7 +1914,7 @@ async fn handle_call) + Send + Sync>( ); return Ok(()); } - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!("new Worker({args}) is not statically analyze-able{hints}",), @@ -1807,38 +1928,12 @@ async fn handle_call) + Send + Sync>( _ => {} } - // linked_args wasn't called, so manually add the closure effects - for arg in args { - if let EffectArg::Closure(_, block) = arg { - add_effects(block.effects); - } - } return Ok(()); } match func { - JsValue::Alternatives { - total_nodes: _, - values, - logical_property: _, - } => { - for alt in values { - Box::pin(handle_call( - ast_path, - span, - alt, - args.clone(), - state, - add_effects, - analysis, - in_try, - new, - )) - .await?; - } - } - JsValue::WellKnownFunction(WellKnownFunctionKind::Import) => { - let args = linked_args(args).await?; + WellKnownFunctionKind::Import => { + let args = linked_args().await?; if args.len() == 1 || args.len() == 2 { let pat = js_value_to_pattern(&args[0]); let options = args.get(1); @@ -1863,7 +1958,7 @@ async fn handle_call) + Send + Sync>( .and_then(ImportAnnotations::parse_dynamic) .unwrap_or_default(); if !pat.has_constant_parts() { - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!("import({args}) is very dynamic{hints}",), @@ -1890,7 +1985,7 @@ async fn handle_call) + Send + Sync>( ); return Ok(()); } - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!("import({args}) is not statically analyze-able{hints}",), @@ -1899,12 +1994,12 @@ async fn handle_call) + Send + Sync>( ), ) } - JsValue::WellKnownFunction(WellKnownFunctionKind::Require) => { - let args = linked_args(args).await?; + WellKnownFunctionKind::Require => { + let args = linked_args().await?; if args.len() == 1 { let pat = js_value_to_pattern(&args[0]); if !pat.has_constant_parts() { - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!("require({args}) is very dynamic{hints}",), @@ -1928,14 +2023,14 @@ async fn handle_call) + Send + Sync>( ); return Ok(()); } - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!("require({args}) is not statically analyze-able{hints}",), DiagnosticId::Error(errors::failed_to_analyze::ecmascript::REQUIRE.to_string()), ) } - JsValue::WellKnownFunction(WellKnownFunctionKind::Define) => { + WellKnownFunctionKind::Define => { analyze_amd_define( source, analysis, @@ -1943,20 +2038,21 @@ async fn handle_call) + Send + Sync>( handler, span, ast_path, - linked_args(args).await?, + linked_args().await?, in_try, ) .await?; } - JsValue::WellKnownFunction(WellKnownFunctionKind::RequireResolve) => { - let args = linked_args(args).await?; + WellKnownFunctionKind::RequireResolve => { + let args = linked_args().await?; if args.len() == 1 || args.len() == 2 { - // TODO error TP1003 require.resolve(???*0*, {"paths": [???*1*]}) is not statically - // analyze-able with ignore_dynamic_requests = true + // TODO error TP1003 require.resolve(???*0*, {"paths": [???*1*]}) is not + // statically analyze-able with ignore_dynamic_requests = + // true let pat = js_value_to_pattern(&args[0]); if !pat.has_constant_parts() { - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!("require.resolve({args}) is very dynamic{hints}",), @@ -1980,7 +2076,7 @@ async fn handle_call) + Send + Sync>( ); return Ok(()); } - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!("require.resolve({args}) is not statically analyze-able{hints}",), @@ -1990,12 +2086,12 @@ async fn handle_call) + Send + Sync>( ) } - JsValue::WellKnownFunction(WellKnownFunctionKind::RequireContext) => { - let args = linked_args(args).await?; - let options = match parse_require_context(&args) { + WellKnownFunctionKind::RequireContext => { + let args = linked_args().await?; + let options = match parse_require_context(args) { Ok(options) => options, Err(err) => { - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_err_with_code( span, &format!( @@ -2025,14 +2121,12 @@ async fn handle_call) + Send + Sync>( ); } - JsValue::WellKnownFunction(WellKnownFunctionKind::FsReadMethod(name)) - if analysis.analyze_mode.is_tracing() => - { - let args = linked_args(args).await?; + WellKnownFunctionKind::FsReadMethod(name) if analysis.analyze_mode.is_tracing() => { + let args = linked_args().await?; if !args.is_empty() { let pat = js_value_to_pattern(&args[0]); if !pat.has_constant_parts() { - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!("fs.{name}({args}) is very dynamic{hints}",), @@ -2056,7 +2150,7 @@ async fn handle_call) + Send + Sync>( ); return Ok(()); } - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!("fs.{name}({args}) is not statically analyze-able{hints}",), @@ -2064,11 +2158,9 @@ async fn handle_call) + Send + Sync>( ) } - JsValue::WellKnownFunction(WellKnownFunctionKind::PathResolve(..)) - if analysis.analyze_mode.is_tracing() => - { + WellKnownFunctionKind::PathResolve(..) if analysis.analyze_mode.is_tracing() => { let parent_path = origin.origin_path().owned().await?.parent(); - let args = linked_args(args).await?; + let args = linked_args().await?; let linked_func_call = state .link_value( @@ -2086,7 +2178,7 @@ async fn handle_call) + Send + Sync>( let pat = js_value_to_pattern(&linked_func_call); if !pat.has_constant_parts() { - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!("path.resolve({args}) is very dynamic{hints}",), @@ -2110,15 +2202,13 @@ async fn handle_call) + Send + Sync>( return Ok(()); } - JsValue::WellKnownFunction(WellKnownFunctionKind::PathJoin) - if analysis.analyze_mode.is_tracing() => - { + WellKnownFunctionKind::PathJoin if analysis.analyze_mode.is_tracing() => { let context_path = source.ident().path().await?; // ignore path.join in `node-gyp`, it will includes too many files if context_path.path.contains("node_modules/node-gyp") { return Ok(()); } - let args = linked_args(args).await?; + let args = linked_args().await?; let linked_func_call = state .link_value( JsValue::call( @@ -2130,7 +2220,7 @@ async fn handle_call) + Send + Sync>( .await?; let pat = js_value_to_pattern(&linked_func_call); if !pat.has_constant_parts() { - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!("path.join({args}) is very dynamic{hints}",), @@ -2153,13 +2243,13 @@ async fn handle_call) + Send + Sync>( ); return Ok(()); } - JsValue::WellKnownFunction(WellKnownFunctionKind::ChildProcessSpawnMethod(name)) + WellKnownFunctionKind::ChildProcessSpawnMethod(name) if analysis.analyze_mode.is_tracing() => { - let args = linked_args(args).await?; + let args = linked_args().await?; // Is this specifically `spawn(process.argv[0], ['-e', ...])`? - if is_invoking_node_process_eval(&args) { + if is_invoking_node_process_eval(args) { return Ok(()); } @@ -2211,7 +2301,7 @@ async fn handle_call) + Send + Sync>( ); } if show_dynamic_warning { - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!("child_process.{name}({args}) is very dynamic{hints}",), @@ -2222,7 +2312,7 @@ async fn handle_call) + Send + Sync>( } return Ok(()); } - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!("child_process.{name}({args}) is not statically analyze-able{hints}",), @@ -2231,15 +2321,13 @@ async fn handle_call) + Send + Sync>( ), ) } - JsValue::WellKnownFunction(WellKnownFunctionKind::ChildProcessFork) - if analysis.analyze_mode.is_tracing() => - { - let args = linked_args(args).await?; + WellKnownFunctionKind::ChildProcessFork if analysis.analyze_mode.is_tracing() => { + let args = linked_args().await?; if !args.is_empty() { let first_arg = &args[0]; let pat = js_value_to_pattern(first_arg); if !pat.has_constant_parts() { - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!("child_process.fork({args}) is very dynamic{hints}",), @@ -2263,7 +2351,7 @@ async fn handle_call) + Send + Sync>( ); return Ok(()); } - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!("child_process.fork({args}) is not statically analyze-able{hints}",), @@ -2272,17 +2360,15 @@ async fn handle_call) + Send + Sync>( ), ) } - JsValue::WellKnownFunction(WellKnownFunctionKind::NodePreGypFind) - if analysis.analyze_mode.is_tracing() => - { + WellKnownFunctionKind::NodePreGypFind if analysis.analyze_mode.is_tracing() => { use turbopack_resolve::node_native_binding::NodePreGypConfigReference; - let args = linked_args(args).await?; + let args = linked_args().await?; if args.len() == 1 { let first_arg = &args[0]; let pat = js_value_to_pattern(first_arg); if !pat.has_constant_parts() { - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!("node-pre-gyp.find({args}) is very dynamic{hints}",), @@ -2305,7 +2391,7 @@ async fn handle_call) + Send + Sync>( ); return Ok(()); } - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!( @@ -2317,12 +2403,10 @@ async fn handle_call) + Send + Sync>( ), ) } - JsValue::WellKnownFunction(WellKnownFunctionKind::NodeGypBuild) - if analysis.analyze_mode.is_tracing() => - { + WellKnownFunctionKind::NodeGypBuild if analysis.analyze_mode.is_tracing() => { use turbopack_resolve::node_native_binding::NodeGypBuildReference; - let args = linked_args(args).await?; + let args = linked_args().await?; if args.len() == 1 { let first_arg = state .link_value(args[0].clone(), ImportAttributes::empty_ref()) @@ -2347,23 +2431,21 @@ async fn handle_call) + Send + Sync>( return Ok(()); } } - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( - span, - &format!( - "require('node-gyp-build')({args}) is not statically analyze-able{hints}", - ), - DiagnosticId::Error( - errors::failed_to_analyze::ecmascript::NODE_GYP_BUILD.to_string(), - ), - ) + span, + &format!( + "require('node-gyp-build')({args}) is not statically analyze-able{hints}", + ), + DiagnosticId::Error( + errors::failed_to_analyze::ecmascript::NODE_GYP_BUILD.to_string(), + ), + ) } - JsValue::WellKnownFunction(WellKnownFunctionKind::NodeBindings) - if analysis.analyze_mode.is_tracing() => - { + WellKnownFunctionKind::NodeBindings if analysis.analyze_mode.is_tracing() => { use turbopack_resolve::node_native_binding::NodeBindingsReference; - let args = linked_args(args).await?; + let args = linked_args().await?; if args.len() == 1 { let first_arg = state .link_value(args[0].clone(), ImportAttributes::empty_ref()) @@ -2381,7 +2463,7 @@ async fn handle_call) + Send + Sync>( return Ok(()); } } - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!("require('bindings')({args}) is not statically analyze-able{hints}",), @@ -2390,17 +2472,15 @@ async fn handle_call) + Send + Sync>( ), ) } - JsValue::WellKnownFunction(WellKnownFunctionKind::NodeExpressSet) - if analysis.analyze_mode.is_tracing() => - { - let args = linked_args(args).await?; + WellKnownFunctionKind::NodeExpressSet if analysis.analyze_mode.is_tracing() => { + let args = linked_args().await?; if args.len() == 2 && let Some(s) = args.first().and_then(|arg| arg.as_str()) { let pkg_or_dir = args.get(1).unwrap(); let pat = js_value_to_pattern(pkg_or_dir); if !pat.has_constant_parts() { - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!("require('express')().set({args}) is very dynamic{hints}",), @@ -2466,7 +2546,7 @@ async fn handle_call) + Send + Sync>( _ => {} } } - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!("require('express')().set({args}) is not statically analyze-able{hints}",), @@ -2475,10 +2555,10 @@ async fn handle_call) + Send + Sync>( ), ) } - JsValue::WellKnownFunction(WellKnownFunctionKind::NodeStrongGlobalizeSetRootDir) + WellKnownFunctionKind::NodeStrongGlobalizeSetRootDir if analysis.analyze_mode.is_tracing() => { - let args = linked_args(args).await?; + let args = linked_args().await?; if let Some(p) = args.first().and_then(|arg| arg.as_str()) { let abs_pattern = if p.starts_with("/ROOT/") { Pattern::Constant(format!("{p}/intl").into()) @@ -2511,7 +2591,7 @@ async fn handle_call) + Send + Sync>( ); return Ok(()); } - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!( @@ -2523,10 +2603,8 @@ async fn handle_call) + Send + Sync>( ), ) } - JsValue::WellKnownFunction(WellKnownFunctionKind::NodeResolveFrom) - if analysis.analyze_mode.is_tracing() => - { - let args = linked_args(args).await?; + WellKnownFunctionKind::NodeResolveFrom if analysis.analyze_mode.is_tracing() => { + let args = linked_args().await?; if args.len() == 2 && args.get(1).and_then(|arg| arg.as_str()).is_some() { analysis.add_reference( CjsAssetReference::new( @@ -2540,7 +2618,7 @@ async fn handle_call) + Send + Sync>( ); return Ok(()); } - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!("require('resolve-from')({args}) is not statically analyze-able{hints}",), @@ -2549,10 +2627,8 @@ async fn handle_call) + Send + Sync>( ), ) } - JsValue::WellKnownFunction(WellKnownFunctionKind::NodeProtobufLoad) - if analysis.analyze_mode.is_tracing() => - { - let args = linked_args(args).await?; + WellKnownFunctionKind::NodeProtobufLoad if analysis.analyze_mode.is_tracing() => { + let args = linked_args().await?; if args.len() == 2 && let Some(JsValue::Object { parts, .. }) = args.get(1) { @@ -2586,7 +2662,7 @@ async fn handle_call) + Send + Sync>( return Ok(()); } - let (args, hints) = explain_args(&args); + let (args, hints) = explain_args(args); handler.span_warn_with_code( span, &format!( @@ -2598,17 +2674,10 @@ async fn handle_call) + Send + Sync>( ), ) } - _ => { - for arg in args { - if let EffectArg::Closure(_, block) = arg { - add_effects(block.effects); - } - } - } - } + _ => {} + }; Ok(()) } - async fn handle_member( ast_path: &[AstParentKind], link_obj: impl Future> + Send + Sync, @@ -2843,10 +2912,10 @@ async fn analyze_amd_define( handler: &Handler, span: Span, ast_path: &[AstParentKind], - args: Vec, + args: &[JsValue], in_try: bool, ) -> Result<()> { - match &args[..] { + match args { [JsValue::Constant(id), JsValue::Array { items: deps, .. }, _] if id.as_str().is_some() => { analyze_amd_define_with_deps( source, diff --git a/turbopack/crates/turbopack-ecmascript/src/references/require_context.rs b/turbopack/crates/turbopack-ecmascript/src/references/require_context.rs index d34218b657e82..b6dccb9b7c871 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/require_context.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/require_context.rs @@ -316,7 +316,9 @@ impl IntoCodeGenReference for RequireContextAssetReference { } } -#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)] +#[derive( + PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue, Hash, Debug, +)] pub struct RequireContextAssetReferenceCodeGen { path: AstPath, reference: ResolvedVc, diff --git a/turbopack/crates/turbopack-ecmascript/src/references/unreachable.rs b/turbopack/crates/turbopack-ecmascript/src/references/unreachable.rs index babaa3c0fe4a9..805d51d150953 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/unreachable.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/unreachable.rs @@ -31,7 +31,9 @@ use crate::{ utils::AstPathRange, }; -#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)] +#[derive( + PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue, Debug, Hash, +)] pub struct Unreachable { range: AstPathRange, diff --git a/turbopack/crates/turbopack-ecmascript/src/references/worker.rs b/turbopack/crates/turbopack-ecmascript/src/references/worker.rs index 17e7b474fd6ed..8c8d555a73325 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/worker.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/worker.rs @@ -125,7 +125,9 @@ impl IntoCodeGenReference for WorkerAssetReference { } } -#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)] +#[derive( + PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue, Hash, Debug, +)] pub struct WorkerAssetReferenceCodeGen { reference: ResolvedVc, path: AstPath, diff --git a/turbopack/crates/turbopack-ecmascript/src/utils.rs b/turbopack/crates/turbopack-ecmascript/src/utils.rs index b7e5523794cc5..4f0b24d6e1749 100644 --- a/turbopack/crates/turbopack-ecmascript/src/utils.rs +++ b/turbopack/crates/turbopack-ecmascript/src/utils.rs @@ -178,7 +178,7 @@ format_iter!(std::fmt::Pointer); format_iter!(std::fmt::UpperExp); format_iter!(std::fmt::UpperHex); -#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, Debug, NonLocalValue)] +#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, Debug, NonLocalValue, Hash)] pub enum AstPathRange { /// The ast path to the block or expression. Exact(#[turbo_tasks(trace_ignore)] Vec),