Skip to main content

rspack_plugin_javascript/visitors/dependency/parser/
mod.rs

1use rspack_intern::AtomRef;
2use rspack_util::SpanExt;
3pub mod ast;
4mod call_hooks_name;
5pub mod estree;
6mod location_advancer;
7mod walk;
8mod walk_block_pre;
9mod walk_module_pre;
10mod walk_pre;
11
12use std::{
13  fmt::Display,
14  hash::{Hash, Hasher},
15  rc::Rc,
16  sync::Arc,
17};
18
19use bitflags::bitflags;
20pub use call_hooks_name::CallHooksName;
21use rspack_cacheable::{
22  cacheable,
23  with::{AsCacheable, AsOption, AsPreset, AsVec},
24};
25use rspack_core::{
26  ArcComputed, AsyncDependenciesBlock, BoxDependency, BuildInfo, BuildMeta, CompilerOptions,
27  DependenciesBlock, Dependency, DependencyCodeGeneration, DependencyCodeGenerationRef,
28  DependencyId, DependencyLocation, DependencyRange, FactoryMeta, ImportMeta,
29  ImportMetaKnownProperties, JavascriptParserCommonjsExportsOption, JavascriptParserOptions,
30  ModuleIdentifier, ModuleLayer, ModuleType, ParseMeta, ResolvedModuleOptions, ResourceData,
31  SideEffectsBailoutItemWithSpan,
32};
33use rspack_error::{Diagnostic, Result};
34use rspack_util::fx_hash::FxIndexSet;
35use rustc_hash::{FxHashMap, FxHashSet};
36use smallvec::SmallVec;
37use swc_experimental_allocator::{Allocator, CloneIn};
38use swc_experimental_ecma_ast::{
39  ArrayPat, AssignPat, AssignTarget, AssignTargetPat, CallExpr, Callee, Decl, Expr, GetSpan, Ident,
40  Lit, MemberExpr, MetaPropExpr, MetaPropKind, ObjectPat, ObjectPatProp, OptCall, OptChainBase,
41  OptChainExpr, Pat, Program, RestPat, Span, Stmt, ThisExpr,
42};
43
44use crate::{
45  Atom, BoxJavascriptParserPlugin,
46  dependency::{DependencyBranchGuard, local_module::LocalModule},
47  parser_and_generator::ParserRuntimeRequirementsData,
48  parser_plugin::{
49    self, CreatedRequireReferencesState, ImportsReferencesState, InnerGraphParserPlugin,
50    JavaScriptParserPluginDrive, JavascriptParserPlugin, RequireReferencesState,
51    inner_graph::state::InnerGraphState,
52  },
53  utils::eval::{self, BasicEvaluatedExpression},
54  visitors::{
55    ParsedJavaScriptAst, ScanDependenciesResult,
56    dependency::parser::{ast::ExprRef, location_advancer::DependencyLocationAdvancer},
57    scope_info::{
58      ScopeInfoDB, ScopeInfoId, TagInfo, TagInfoId, VariableInfo, VariableInfoFlags, VariableInfoId,
59    },
60  },
61};
62
63pub trait TagInfoData: Clone + Sized + 'static {
64  fn into_any(data: Self) -> Box<dyn anymap::CloneAny>;
65
66  fn downcast(any: Box<dyn anymap::CloneAny>) -> Self;
67
68  fn downcast_ref(any: &dyn anymap::CloneAny) -> &Self;
69
70  fn downcast_mut(any: &mut dyn anymap::CloneAny) -> &mut Self;
71}
72
73fn atom_from_wtf8(value: swc_experimental_allocator::atom::Wtf8Atom<'_>) -> Atom {
74  Atom::from(value.as_wtf8().to_string_lossy().as_ref())
75}
76
77pub(crate) fn member_property_to_atom(expr: &Expr) -> Option<Atom> {
78  match expr {
79    Expr::Lit(lit) => Some(match &**lit {
80      Lit::Str(s) => atom_from_wtf8(s.value),
81      Lit::Bool(b) => Atom::from(if b.value { "true" } else { "false" }),
82      Lit::Null(_) => Atom::from("null"),
83      Lit::Num(n) => Atom::from(rspack_util::ryu_js::Buffer::new().format(n.value)),
84      Lit::BigInt(i) => Atom::from(i.value.as_str()),
85      Lit::Regex(r) => {
86        let mut flags = r.flags.as_str().chars().collect::<Vec<_>>();
87        flags.sort_unstable();
88        let mut property = String::with_capacity(r.exp.len() + flags.len() + 2);
89        property.push('/');
90        property.push_str(r.exp.as_str());
91        property.push('/');
92        property.extend(flags);
93        Atom::from(property)
94      }
95    }),
96    Expr::Tpl(tpl) if tpl.exprs.is_empty() && tpl.quasis.len() == 1 => {
97      let quasi = tpl.quasis.first()?;
98      Some(
99        quasi
100          .cooked
101          .map_or_else(|| Atom::from(quasi.raw.as_str()), atom_from_wtf8),
102      )
103    }
104    _ => None,
105  }
106}
107
108impl GetSpan for estree::Statement<'_> {
109  fn span(&self) -> Span {
110    self.span()
111  }
112}
113
114impl<T> TagInfoData for T
115where
116  T: Clone + Sized + 'static,
117{
118  fn into_any(data: Self) -> Box<dyn anymap::CloneAny> {
119    Box::new(data)
120  }
121
122  fn downcast(any: Box<dyn anymap::CloneAny>) -> Self {
123    *(any as Box<dyn std::any::Any>)
124      .downcast()
125      .expect("TagInfoData should be downcasted from correct tag info")
126  }
127
128  fn downcast_ref(any: &dyn anymap::CloneAny) -> &Self {
129    let any = any as &dyn std::any::Any;
130    any
131      .downcast_ref()
132      .expect("TagInfoData should be downcasted from correct tag info")
133  }
134
135  fn downcast_mut(any: &mut dyn anymap::CloneAny) -> &mut Self {
136    let any = any as &mut dyn std::any::Any;
137    any
138      .downcast_mut()
139      .expect("TagInfoData should be downcasted from correct tag info")
140  }
141}
142
143// Most parsed member chains are one or two segments long, so keep them inline.
144pub type AtomMembers = SmallVec<[Atom; 2]>;
145pub type OptionalMembers = SmallVec<[bool; 2]>;
146pub type MemberRanges = SmallVec<[Span; 2]>;
147
148#[derive(Debug)]
149pub struct ExtractedMemberExpressionChainData<'ast> {
150  pub object: ExprRef<'ast>,
151  pub members: AtomMembers,
152  pub members_optionals: OptionalMembers,
153  pub member_ranges: MemberRanges,
154}
155
156bitflags! {
157  #[derive(Clone, Copy)]
158  pub struct AllowedMemberTypes: u8 {
159    const CallExpression = 1 << 0;
160    const Expression = 1 << 1;
161  }
162}
163
164#[derive(Debug)]
165pub enum MemberExpressionInfo<'ast> {
166  Call(CallExpressionInfo<'ast>),
167  Expression(ExpressionExpressionInfo),
168}
169
170#[derive(Debug)]
171pub struct CallExpressionInfo<'ast> {
172  pub call: &'ast CallExpr<'ast>,
173  pub root_info: ExportedVariableInfo,
174  pub callee_members: AtomMembers,
175  pub members: AtomMembers,
176  pub members_optionals: OptionalMembers,
177  pub member_ranges: MemberRanges,
178}
179
180#[derive(Debug)]
181pub struct ExpressionExpressionInfo {
182  pub name: String,
183  pub root_info: ExportedVariableInfo,
184  pub members: AtomMembers,
185  pub members_optionals: OptionalMembers,
186  pub member_ranges: MemberRanges,
187}
188
189#[derive(Debug, Clone)]
190pub enum ExportedVariableInfo {
191  Name(Atom),
192  VariableInfo(VariableInfoId),
193}
194
195fn object_and_members_to_name(object: &str, members_reversed: &[impl AsRef<str>]) -> String {
196  let total_len = object.len()
197    + members_reversed.len()
198    + members_reversed
199      .iter()
200      .map(|m| m.as_ref().len())
201      .sum::<usize>();
202
203  let mut name = String::with_capacity(total_len);
204  name.push_str(object);
205  let iter = members_reversed.iter();
206  for member in iter.rev() {
207    name.push('.');
208    name.push_str(member.as_ref());
209  }
210  name
211}
212
213pub trait RootName {
214  fn get_root_name(&self) -> Option<Atom> {
215    None
216  }
217}
218
219impl RootName for Expr<'_> {
220  fn get_root_name(&self) -> Option<Atom> {
221    match self {
222      Expr::Ident(ident) => ident.get_root_name(),
223      Expr::This(this) => this.get_root_name(),
224      Expr::MetaProp(meta) => meta.get_root_name(),
225      _ => None,
226    }
227  }
228}
229
230impl RootName for ExprRef<'_> {
231  fn get_root_name(&self) -> Option<Atom> {
232    match self {
233      ExprRef::Ident(ident) => ident.get_root_name(),
234      ExprRef::This(this) => this.get_root_name(),
235      ExprRef::MetaProp(meta) => meta.get_root_name(),
236      _ => None,
237    }
238  }
239}
240
241impl RootName for ThisExpr {
242  fn get_root_name(&self) -> Option<Atom> {
243    Some("this".into())
244  }
245}
246
247impl RootName for Ident<'_> {
248  fn get_root_name(&self) -> Option<Atom> {
249    Some(Atom::from(&self.sym))
250  }
251}
252
253impl RootName for MetaPropExpr {
254  fn get_root_name(&self) -> Option<Atom> {
255    match self.kind {
256      MetaPropKind::NewTarget => Some("new.target".into()),
257      MetaPropKind::ImportMeta => Some("import.meta".into()),
258    }
259  }
260}
261
262pub struct NameInfo<'a> {
263  pub name: &'a str,
264  pub info: Option<&'a VariableInfo>,
265}
266
267pub enum PatRef<'a> {
268  Borrowed(&'a Pat<'a>),
269  Owned(Pat<'a>),
270}
271
272impl<'a> PatRef<'a> {
273  pub(crate) fn as_pat(&self) -> &Pat<'a> {
274    match self {
275      PatRef::Borrowed(pat) => pat,
276      PatRef::Owned(pat) => pat,
277    }
278  }
279}
280
281#[derive(Debug, Clone, Copy, PartialEq, Eq)]
282pub enum ScopeTerminated {
283  Return,
284  Throw,
285}
286
287#[derive(Clone, Copy, Debug)]
288pub enum TopLevelScope {
289  Top,
290  ArrowFunction,
291  False,
292}
293
294#[derive(Debug, Clone, Copy)]
295pub struct StatementPath {
296  span: Span,
297}
298
299impl StatementPath {
300  fn span(&self) -> Span {
301    self.span
302  }
303}
304
305impl StatementPath {
306  fn from_span(span: Span) -> Self {
307    Self { span }
308  }
309}
310
311impl From<Span> for StatementPath {
312  fn from(value: Span) -> Self {
313    Self::from_span(value)
314  }
315}
316
317#[cacheable]
318#[derive(Debug, Clone, Hash, PartialEq, Eq)]
319pub struct DestructuringAssignmentProperty {
320  pub range: DependencyRange,
321  #[cacheable(with=AsPreset)]
322  pub id: Atom,
323  #[cacheable(omit_bounds, with=AsOption<AsCacheable>)]
324  pub pattern: Option<DestructuringAssignmentProperties>,
325  pub shorthand: bool,
326}
327
328#[cacheable]
329#[derive(Debug, Default, Clone, PartialEq, Eq)]
330pub struct DestructuringAssignmentProperties {
331  #[cacheable(with=AsVec<AsCacheable>)]
332  inner: FxIndexSet<DestructuringAssignmentProperty>,
333}
334
335impl Hash for DestructuringAssignmentProperties {
336  fn hash<H: Hasher>(&self, state: &mut H) {
337    for prop in &self.inner {
338      prop.hash(state);
339    }
340  }
341}
342
343impl DestructuringAssignmentProperties {
344  pub fn insert(&mut self, prop: DestructuringAssignmentProperty) -> bool {
345    self.inner.insert(prop)
346  }
347
348  pub fn extend(&mut self, other: Self) {
349    self.inner.extend(other.inner);
350  }
351
352  pub fn iter(&self) -> impl Iterator<Item = &DestructuringAssignmentProperty> {
353    self.inner.iter()
354  }
355
356  pub fn traverse_on_leaf<'a, F>(&'a self, on_leaf_node: &mut F)
357  where
358    F: FnMut(&mut Vec<&'a DestructuringAssignmentProperty>),
359  {
360    self.traverse_impl(on_leaf_node, &mut |_| {}, &mut Vec::new());
361  }
362
363  pub fn traverse_on_enter<'a, F>(&'a self, on_enter_node: &mut F)
364  where
365    F: FnMut(&mut Vec<&'a DestructuringAssignmentProperty>),
366  {
367    self.traverse_impl(&mut |_| {}, on_enter_node, &mut Vec::new());
368  }
369
370  fn traverse_impl<'a, L, E>(
371    &'a self,
372    on_leaf_node: &mut L,
373    on_enter_node: &mut E,
374    stack: &mut Vec<&'a DestructuringAssignmentProperty>,
375  ) where
376    L: FnMut(&mut Vec<&'a DestructuringAssignmentProperty>),
377    E: FnMut(&mut Vec<&'a DestructuringAssignmentProperty>),
378  {
379    for prop in &self.inner {
380      stack.push(prop);
381      on_enter_node(stack);
382      // Empty nested patterns still access and coerce their parent value, so
383      // the parent property is a referenced leaf in that case.
384      if let Some(pattern) = &prop.pattern
385        && !pattern.inner.is_empty()
386      {
387        pattern.traverse_impl(on_leaf_node, on_enter_node, stack);
388      } else {
389        on_leaf_node(stack);
390      }
391      stack.pop();
392    }
393  }
394}
395
396#[derive(Debug, Default)]
397pub struct DestructuringAssignmentPropertiesMap {
398  inner: FxHashMap<Span, DestructuringAssignmentProperties>,
399}
400
401impl DestructuringAssignmentPropertiesMap {
402  pub fn add(&mut self, span: Span, props: DestructuringAssignmentProperties) {
403    self.inner.entry(span).or_default().extend(props)
404  }
405
406  pub fn get(&self, span: &Span) -> Option<&DestructuringAssignmentProperties> {
407    self.inner.get(span)
408  }
409}
410
411pub struct JavascriptParser<'parser> {
412  // ===== results =======
413  errors: Vec<Diagnostic>,
414  warning_diagnostics: Vec<Diagnostic>,
415  dependencies: Vec<BoxDependency>,
416  presentational_dependencies: Vec<DependencyCodeGenerationRef>,
417  // Vec<Box<T: Sized>> makes sense if T is a large type (see #3530, 1st comment).
418  // #3530: https://github.com/rust-lang/rust-clippy/issues/3530
419  #[allow(clippy::vec_box)]
420  blocks: Vec<Option<Box<AsyncDependenciesBlock>>>,
421  block_parents: Vec<Option<usize>>,
422  // ===== inputs =======
423  pub(crate) source: &'parser str,
424  pub ast: &'parser ParsedJavaScriptAst<'parser>,
425  pub parse_meta: ParseMeta,
426  pub factory_meta: Option<&'parser FactoryMeta>,
427  pub build_meta: &'parser mut BuildMeta,
428  pub build_info: &'parser mut BuildInfo,
429  pub resource_data: &'parser ResourceData,
430  pub(crate) compiler_options: &'parser CompilerOptions,
431  pub(crate) javascript_options: &'parser JavascriptParserOptions,
432  pub parser_runtime_requirements: &'parser ParserRuntimeRequirementsData,
433  pub module_type: &'parser ModuleType,
434  pub(crate) module_layer: Option<&'parser ModuleLayer>,
435  pub module_identifier: &'parser ModuleIdentifier,
436  pub(crate) plugin_drive: Rc<JavaScriptParserPluginDrive>,
437  // ===== states =======
438  pub(crate) definitions_db: ScopeInfoDB,
439  pub(crate) definitions: ScopeInfoId,
440  pub(crate) top_level_scope: TopLevelScope,
441  pub(crate) current_tag_info: Option<TagInfoId>,
442  pub in_try: bool,
443  pub(crate) terminated: Option<ScopeTerminated>,
444  pub(crate) in_short_hand: bool,
445  pub(crate) in_assignment_pattern: bool,
446  pub(crate) in_tagged_template_tag: bool,
447  pub(crate) member_expr_in_optional_chain: bool,
448  pub(crate) semicolons: &'parser mut FxHashSet<u32>,
449  pub(crate) statement_path: Vec<StatementPath>,
450  pub(crate) prev_statement: Option<StatementPath>,
451  pub is_esm: bool,
452  pub(crate) destructuring_assignment_properties: DestructuringAssignmentPropertiesMap,
453  pub(crate) dynamic_import_references: ImportsReferencesState,
454  pub(crate) common_js_require_references: RequireReferencesState,
455  pub(crate) created_require_references: CreatedRequireReferencesState<'parser>,
456  pub(crate) worker_index: u32,
457  pub(crate) parser_exports_state: Option<bool>,
458  pub(crate) local_modules: Vec<LocalModule>,
459  pub(crate) last_esm_import_order: i32,
460  pub(crate) inner_graph: InnerGraphState,
461  pub(crate) side_effects_item: Option<SideEffectsBailoutItemWithSpan>,
462  pub(crate) is_renaming: Option<Atom>,
463  pub(crate) location_advancer: DependencyLocationAdvancer,
464  pub(crate) collecting_dependencies_for_block: Option<usize>,
465  pub(crate) dependencies_in_branch_guard: Option<FxHashMap<DependencyRange, DependencyId>>,
466  pub(crate) current_branch_guard: Option<DependencyBranchGuard>,
467}
468
469impl<'parser> JavascriptParser<'parser> {
470  #[allow(clippy::too_many_arguments)]
471  pub fn new(
472    source: &'parser str,
473    ast: &'parser ParsedJavaScriptAst<'parser>,
474    compiler_options: &'parser CompilerOptions,
475    javascript_options: &'parser JavascriptParserOptions,
476    import_meta: ArcComputed<ResolvedModuleOptions, ImportMeta>,
477    module_identifier: &'parser ModuleIdentifier,
478    module_type: &'parser ModuleType,
479    module_layer: Option<&'parser ModuleLayer>,
480    resource_data: &'parser ResourceData,
481    factory_meta: Option<&'parser FactoryMeta>,
482    build_meta: &'parser mut BuildMeta,
483    build_info: &'parser mut BuildInfo,
484    semicolons: &'parser mut FxHashSet<u32>,
485    parser_plugins: &'parser mut Vec<BoxJavascriptParserPlugin>,
486    parse_meta: ParseMeta,
487    parser_runtime_requirements: &'parser ParserRuntimeRequirementsData,
488  ) -> Self {
489    let warning_diagnostics: Vec<Diagnostic> = Vec::new();
490    let errors = Vec::new();
491    // These three vectors are moved into the scan result and stay alive for the
492    // whole build, so their capacity is a per-module constant cost. Most modules
493    // have a handful of dependencies and no async blocks, while presentational
494    // dependencies average around seven in a large app, so start small without
495    // going all the way down to one element and let the rare large module grow.
496    let dependencies = Vec::with_capacity(16);
497    let blocks = Vec::with_capacity(4);
498    let presentational_dependencies = Vec::with_capacity(8);
499    let parser_exports_state: Option<bool> = None;
500
501    let mut plugins: Vec<BoxJavascriptParserPlugin> = Vec::with_capacity(32 + parser_plugins.len());
502
503    plugins.append(parser_plugins);
504
505    plugins.push(Box::new(parser_plugin::InitializeEvaluating));
506    plugins.push(Box::new(parser_plugin::JavascriptMetaInfoPlugin));
507    plugins.push(Box::new(parser_plugin::ConstPlugin));
508    plugins.push(Box::new(parser_plugin::UseStrictPlugin));
509
510    if matches!(module_type, ModuleType::JsAuto | ModuleType::JsDynamic) {
511      plugins.push(Box::new(
512        parser_plugin::RequireContextDependencyParserPlugin,
513      ));
514      plugins.push(Box::new(
515        parser_plugin::RequireEnsureDependenciesBlockParserPlugin,
516      ));
517    }
518    plugins.push(Box::new(parser_plugin::CompatibilityPlugin));
519
520    if module_type.is_js_auto() || module_type.is_js_esm() {
521      plugins.push(Box::new(parser_plugin::ESMTopLevelThisParserPlugin));
522      plugins.push(Box::<parser_plugin::ESMDetectionParserPlugin>::default());
523      plugins.push(Box::new(
524        parser_plugin::ImportMetaContextDependencyParserPlugin {
525          webpack_context: import_meta
526            .is_known_property_enabled(ImportMetaKnownProperties::WEBPACK_CONTEXT),
527          glob: import_meta.is_known_property_enabled(ImportMetaKnownProperties::GLOB),
528        },
529      ));
530      if import_meta.is_enabled() {
531        plugins.push(Box::new(parser_plugin::ImportMetaPlugin(import_meta)));
532      } else {
533        plugins.push(Box::new(parser_plugin::ImportMetaDisabledPlugin));
534      }
535
536      plugins.push(Box::new(parser_plugin::ESMImportDependencyParserPlugin));
537      plugins.push(Box::new(parser_plugin::ESMExportDependencyParserPlugin));
538    }
539
540    if compiler_options.amd.is_some() && (module_type.is_js_auto() || module_type.is_js_dynamic()) {
541      plugins.push(Box::new(
542        parser_plugin::AMDRequireDependenciesBlockParserPlugin,
543      ));
544      plugins.push(Box::new(parser_plugin::AMDDefineDependencyParserPlugin));
545      plugins.push(Box::new(parser_plugin::AMDParserPlugin));
546    }
547
548    if module_type.is_js_auto() || module_type.is_js_dynamic() || module_type.is_js_esm() {
549      plugins.push(Box::new(parser_plugin::CommonJsImportsParserPlugin));
550    }
551
552    if module_type.is_js_auto() || module_type.is_js_dynamic() {
553      plugins.push(Box::new(parser_plugin::CommonJsPlugin));
554      let commonjs_exports = javascript_options
555        .commonjs
556        .as_ref()
557        .map_or(JavascriptParserCommonjsExportsOption::Enable, |commonjs| {
558          commonjs.exports
559        });
560      if commonjs_exports != JavascriptParserCommonjsExportsOption::Disable {
561        plugins.push(Box::new(parser_plugin::CommonJsExportsParserPlugin::new(
562          commonjs_exports == JavascriptParserCommonjsExportsOption::SkipInEsm,
563        )));
564      }
565    }
566
567    // NodeStuffPlugin handles __dirname/__filename/global (CJS).
568    let handle_cjs =
569      (module_type.is_js_auto() || module_type.is_js_dynamic()) && compiler_options.node.is_some();
570    if handle_cjs {
571      plugins.push(Box::new(parser_plugin::NodeStuffPlugin::new(handle_cjs)));
572    }
573
574    if module_type.is_js_auto() || module_type.is_js_dynamic() || module_type.is_js_esm() {
575      plugins.push(Box::new(parser_plugin::APIPlugin::new(
576        compiler_options.output.module,
577      )));
578      plugins.push(Box::new(parser_plugin::ImportParserPlugin));
579      plugins.push(Box::new(parser_plugin::WorkerPlugin::new(
580        javascript_options
581          .worker
582          .as_ref()
583          .expect("should have worker"),
584      )));
585      plugins.push(Box::new(parser_plugin::OverrideStrictPlugin));
586    }
587
588    let inline_exports = compiler_options.optimization.inline_exports;
589    if inline_exports {
590      build_info.inline_exports = true;
591    }
592    plugins.push(Box::new(parser_plugin::ConstValuePlugin::new(
593      inline_exports,
594    )));
595    if compiler_options.optimization.inner_graph {
596      plugins.push(Box::new(parser_plugin::InnerGraphParserPlugin::new(
597        compiler_options.experiments.pure_functions,
598      )));
599    }
600
601    if compiler_options.optimization.side_effects.is_true() {
602      plugins.push(Box::new(parser_plugin::SideEffectsParserPlugin::new(
603        compiler_options.experiments.pure_functions,
604      )));
605    }
606
607    let plugin_drive = Rc::new(JavaScriptParserPluginDrive::new(plugins));
608    let mut db = ScopeInfoDB::new();
609
610    Self {
611      last_esm_import_order: 0,
612      ast,
613      javascript_options,
614      source,
615      errors,
616      warning_diagnostics,
617      dependencies,
618      presentational_dependencies,
619      blocks,
620      in_try: false,
621      terminated: None,
622      in_short_hand: false,
623      in_assignment_pattern: false,
624      top_level_scope: TopLevelScope::Top,
625      is_esm: matches!(module_type, ModuleType::JsEsm),
626      in_tagged_template_tag: false,
627      definitions: db.create(),
628      definitions_db: db,
629      plugin_drive,
630      resource_data,
631      factory_meta,
632      build_meta,
633      build_info,
634      compiler_options,
635      module_type,
636      module_layer,
637      parser_exports_state,
638      worker_index: 0,
639      module_identifier,
640      member_expr_in_optional_chain: false,
641      destructuring_assignment_properties: Default::default(),
642      dynamic_import_references: Default::default(),
643      common_js_require_references: Default::default(),
644      created_require_references: Default::default(),
645      semicolons,
646      statement_path: Default::default(),
647      current_tag_info: None,
648      prev_statement: None,
649      inner_graph: InnerGraphState::new(),
650      parse_meta,
651      local_modules: Default::default(),
652      side_effects_item: None,
653      parser_runtime_requirements,
654      is_renaming: None,
655      location_advancer: DependencyLocationAdvancer::new(),
656      collecting_dependencies_for_block: None,
657      block_parents: Vec::new(),
658      dependencies_in_branch_guard: None,
659      current_branch_guard: None,
660    }
661  }
662
663  pub fn into_results(mut self) -> Result<ScanDependenciesResult, Vec<Diagnostic>> {
664    if self.errors.is_empty() {
665      InnerGraphParserPlugin::finalize_dependency_usage(
666        &mut self.inner_graph,
667        &mut self.dependencies,
668      );
669      // Keep flat indices stable for dependency analysis, then assemble the tree
670      // from children to parents while preserving each block's insertion order.
671      let mut nested_blocks = vec![Vec::new(); self.blocks.len()];
672      let mut blocks = Vec::new();
673      for (index, (block, parent)) in self
674        .blocks
675        .into_iter()
676        .zip(self.block_parents)
677        .enumerate()
678        .rev()
679      {
680        let mut block = block.expect("reserved parser block should be initialized");
681        for child in nested_blocks[index].drain(..).rev() {
682          block.add_block(child);
683        }
684        if let Some(parent) = parent {
685          nested_blocks[parent].push(block.into());
686        } else {
687          blocks.push(block);
688        }
689      }
690      blocks.reverse();
691      Ok(ScanDependenciesResult {
692        dependencies: self.dependencies,
693        blocks,
694        presentational_dependencies: self.presentational_dependencies,
695        warning_diagnostics: self.warning_diagnostics,
696        side_effects_item: self.side_effects_item,
697      })
698    } else {
699      Err(self.errors)
700    }
701  }
702
703  pub fn add_dependency(&mut self, mut dep: BoxDependency) {
704    if let Some(guard) = &self.current_branch_guard {
705      guard.bind_dependency(dep.as_mut());
706    }
707    self.dependencies.push(dep);
708  }
709
710  pub fn add_dependencies(&mut self, deps: impl IntoIterator<Item = BoxDependency>) {
711    if let Some(guard) = &self.current_branch_guard {
712      self.dependencies.extend(deps.into_iter().map(|mut dep| {
713        guard.bind_dependency(dep.as_mut());
714        dep
715      }));
716    } else {
717      self.dependencies.extend(deps);
718    }
719  }
720
721  pub fn pop_dependency(&mut self) -> Option<BoxDependency> {
722    self.dependencies.pop()
723  }
724
725  pub fn next_dependency_idx(&self) -> usize {
726    self.dependencies.len()
727  }
728
729  pub fn get_dependencies(&self) -> &[BoxDependency] {
730    &self.dependencies
731  }
732
733  pub fn get_dependency_mut(&mut self, idx: usize) -> Option<&mut (dyn Dependency + 'static)> {
734    self.dependencies.get_mut(idx).map(AsMut::as_mut)
735  }
736
737  pub fn collect_dependencies_for_block(
738    &mut self,
739    block_idx: usize,
740    deps: Vec<BoxDependency>,
741    f: impl FnOnce(&mut JavascriptParser),
742  ) -> Vec<BoxDependency> {
743    let old_deps = std::mem::replace(&mut self.dependencies, deps);
744    let old_block_idx = self.collecting_dependencies_for_block.replace(block_idx);
745    f(self);
746    self.collecting_dependencies_for_block = old_block_idx;
747    std::mem::replace(&mut self.dependencies, old_deps)
748  }
749
750  pub fn collect_dependencies_in_branch_guard<T>(
751    &mut self,
752    f: impl FnOnce(&mut JavascriptParser) -> T,
753  ) -> T {
754    let old_deps = self
755      .dependencies_in_branch_guard
756      .replace(Default::default());
757    let result = f(self);
758    self.dependencies_in_branch_guard = old_deps;
759    result
760  }
761
762  pub fn with_branch_guard(&mut self, guard: DependencyBranchGuard, f: impl FnOnce(&mut Self)) {
763    let guard = if let Some(old_guard) = &self.current_branch_guard {
764      // handle for: if (A) { if (B) { import("./x") } }
765      old_guard.clone().and(guard)
766    } else {
767      guard
768    };
769    let old_guard = self.current_branch_guard.replace(guard);
770    f(self);
771    self.current_branch_guard = old_guard;
772  }
773
774  pub fn add_presentational_dependency(&mut self, dep: DependencyCodeGenerationRef) {
775    self.presentational_dependencies.push(dep);
776  }
777
778  pub fn add_presentational_dependencies(
779    &mut self,
780    deps: impl IntoIterator<Item = DependencyCodeGenerationRef>,
781  ) {
782    self.presentational_dependencies.extend(deps);
783  }
784
785  pub fn next_presentational_dependency_idx(&self) -> usize {
786    self.presentational_dependencies.len()
787  }
788
789  pub fn get_presentational_dependency_mut(
790    &mut self,
791    idx: usize,
792  ) -> Option<&mut (dyn DependencyCodeGeneration + 'static)> {
793    Arc::get_mut(self.presentational_dependencies.get_mut(idx)?)
794  }
795
796  pub fn reserve_block(&mut self) -> usize {
797    let index = self.blocks.len();
798    self.blocks.push(None);
799    self
800      .block_parents
801      .push(self.collecting_dependencies_for_block);
802    index
803  }
804
805  pub fn set_block(&mut self, index: usize, mut block: Box<AsyncDependenciesBlock>) {
806    if let Some(guard) = &self.current_branch_guard {
807      for dep in block.dependencies_mut() {
808        guard.bind_dependency(dep);
809      }
810    }
811    debug_assert!(self.blocks[index].is_none());
812    self.blocks[index] = Some(block);
813  }
814
815  pub fn add_block(&mut self, block: Box<AsyncDependenciesBlock>) {
816    let index = self.reserve_block();
817    self.set_block(index, block);
818  }
819
820  pub fn next_block_idx(&self) -> usize {
821    self.blocks.len()
822  }
823
824  pub fn get_block_mut(&mut self, idx: usize) -> Option<&mut Box<AsyncDependenciesBlock>> {
825    self.blocks.get_mut(idx).and_then(Option::as_mut)
826  }
827
828  pub fn add_error(&mut self, error: Diagnostic) {
829    self.errors.push(error);
830  }
831
832  pub fn add_warning(&mut self, warning: Diagnostic) {
833    self.warning_diagnostics.push(warning);
834  }
835
836  pub fn add_warnings(&mut self, warnings: impl IntoIterator<Item = Diagnostic>) {
837    self.warning_diagnostics.extend(warnings);
838  }
839
840  pub fn source(&self) -> &str {
841    self.source
842  }
843
844  pub fn is_top_level_scope(&self) -> bool {
845    matches!(self.top_level_scope, TopLevelScope::Top)
846  }
847
848  pub fn is_top_level_this(&self) -> bool {
849    !matches!(self.top_level_scope, TopLevelScope::False)
850  }
851
852  pub fn add_local_module(&mut self, name: &Atom, dep_idx: usize) {
853    self.local_modules.push(LocalModule::new(
854      name.clone(),
855      self.local_modules.len(),
856      dep_idx,
857    ));
858  }
859
860  pub fn get_local_module_mut(&mut self, name: &str) -> Option<&mut LocalModule> {
861    self.local_modules.iter_mut().find(|m| m.get_name() == name)
862  }
863
864  pub fn is_asi_position(&self, pos: u32) -> bool {
865    let curr_path = self.statement_path.last().expect("Should in statement");
866    if curr_path.span().end == pos && self.semicolons.contains(&pos) {
867      true
868    } else if curr_path.span().start == pos
869      && let Some(prev) = &self.prev_statement
870      && self.semicolons.contains(&prev.span().end)
871    {
872      true
873    } else {
874      false
875    }
876  }
877
878  pub fn set_asi_position(&mut self, pos: u32) -> bool {
879    self.semicolons.insert(pos)
880  }
881
882  pub fn unset_asi_position(&mut self, pos: u32) -> bool {
883    self.semicolons.remove(&pos)
884  }
885
886  pub fn is_statement_level_expression(&self, expr_span: Span) -> bool {
887    let Some(curr_path) = self.statement_path.last() else {
888      return false;
889    };
890    curr_path.span() == expr_span
891  }
892
893  pub fn get_module_layer(&self) -> Option<&ModuleLayer> {
894    self.module_layer
895  }
896
897  /// The source order assigned to the import declaration currently being
898  /// visited by `import_specifier` parser hooks.
899  pub fn current_esm_import_order(&self) -> i32 {
900    self.last_esm_import_order
901  }
902
903  pub fn get_variable_info<'key>(
904    &mut self,
905    name: impl Into<AtomRef<'key>>,
906  ) -> Option<&VariableInfo> {
907    let id = self.definitions_db.get(self.definitions, name)?;
908    Some(self.definitions_db.expect_get_variable(id))
909  }
910
911  fn get_tag_data_by_id<Data: TagInfoData>(
912    &self,
913    tag_info_id: TagInfoId,
914    tag: &'static str,
915  ) -> Option<&Data> {
916    let mut cur = Some(tag_info_id);
917
918    while let Some(cur_id) = cur {
919      let cur_tag_info = self.definitions_db.expect_get_tag_info(cur_id);
920      if cur_tag_info.tag == tag {
921        return cur_tag_info
922          .data
923          .as_deref()
924          .map(|data| TagInfoData::downcast_ref(data));
925      }
926      cur = cur_tag_info.next;
927    }
928
929    None
930  }
931
932  fn get_tag_data_mut_by_id<Data: TagInfoData>(
933    &mut self,
934    tag_info_id: TagInfoId,
935    tag: &'static str,
936  ) -> Option<&mut Data> {
937    let mut cur = Some(tag_info_id);
938
939    while let Some(cur_id) = cur {
940      let cur_tag_info = self.definitions_db.expect_get_tag_info(cur_id);
941      if cur_tag_info.tag == tag {
942        return self
943          .definitions_db
944          .expect_get_mut_tag_info(cur_id)
945          .data
946          .as_deref_mut()
947          .map(|data| TagInfoData::downcast_mut(data));
948      }
949      cur = cur_tag_info.next;
950    }
951
952    None
953  }
954
955  pub fn get_tag_data<'key, Data: TagInfoData>(
956    &mut self,
957    name: impl Into<AtomRef<'key>>,
958    tag: &'static str,
959  ) -> Option<&Data> {
960    self
961      .get_variable_info(name)
962      .and_then(|variable_info| variable_info.tag_info)
963      .and_then(|tag_info_id| self.get_tag_data_by_id(tag_info_id, tag))
964  }
965
966  pub fn get_tag_data_mut<'key, Data: TagInfoData>(
967    &mut self,
968    name: impl Into<AtomRef<'key>>,
969    tag: &'static str,
970  ) -> Option<&mut Data> {
971    self
972      .get_variable_info(name)
973      .and_then(|variable_info| variable_info.tag_info)
974      .and_then(|tag_info_id| self.get_tag_data_mut_by_id(tag_info_id, tag))
975  }
976
977  pub fn get_variable_tag_data<Data: TagInfoData>(
978    &self,
979    id: VariableInfoId,
980    tag: &'static str,
981  ) -> Option<&Data> {
982    self
983      .definitions_db
984      .expect_get_variable(id)
985      .tag_info
986      .and_then(|tag_info_id| self.get_tag_data_by_id(tag_info_id, tag))
987  }
988
989  pub fn get_free_info_from_variable<'a>(
990    &'a mut self,
991    name: impl Into<AtomRef<'a>>,
992  ) -> Option<NameInfo<'a>> {
993    let name = name.into();
994    let Some(info) = self.get_variable_info(name) else {
995      return Some(NameInfo {
996        name: name.as_str(),
997        info: None,
998      });
999    };
1000    let Some(name) = &info.name else {
1001      return None;
1002    };
1003    if !info.is_free() {
1004      return None;
1005    }
1006    Some(NameInfo {
1007      name: name.as_str(),
1008      info: Some(info),
1009    })
1010  }
1011
1012  pub fn get_name_info_from_variable<'a>(
1013    &'a mut self,
1014    name: impl Into<AtomRef<'a>>,
1015  ) -> Option<NameInfo<'a>> {
1016    let name = name.into();
1017    let Some(info) = self.get_variable_info(name) else {
1018      return Some(NameInfo {
1019        name: name.as_str(),
1020        info: None,
1021      });
1022    };
1023    let Some(name) = &info.name else {
1024      return None;
1025    };
1026    if !info.is_free() && !info.is_tagged() {
1027      return None;
1028    }
1029    Some(NameInfo {
1030      name: name.as_str(),
1031      info: Some(info),
1032    })
1033  }
1034
1035  pub fn get_all_variables_from_current_scope(
1036    &self,
1037  ) -> impl Iterator<Item = (&str, VariableInfoId)> {
1038    self.definitions_db.scope_variables(self.definitions)
1039  }
1040
1041  pub fn define_variable(&mut self, name: Atom) {
1042    let definitions = self.definitions;
1043    if let Some(variable_info) = self.get_variable_info(&name)
1044      && variable_info.tag_info.is_some()
1045      && definitions == variable_info.declared_scope
1046    {
1047      return;
1048    }
1049    let info = VariableInfo::create(
1050      &mut self.definitions_db,
1051      definitions,
1052      None,
1053      VariableInfoFlags::NORMAL,
1054      None,
1055    );
1056    self.definitions_db.set(definitions, name, info);
1057  }
1058
1059  pub fn set_variable(&mut self, name: Atom, variable: ExportedVariableInfo) {
1060    let scope_id = self.definitions;
1061    match variable {
1062      ExportedVariableInfo::Name(variable) => {
1063        if name == variable {
1064          self.definitions_db.delete(scope_id, &name);
1065        } else {
1066          let variable = VariableInfo::create(
1067            &mut self.definitions_db,
1068            scope_id,
1069            Some(variable),
1070            VariableInfoFlags::FREE,
1071            None,
1072          );
1073          self.definitions_db.set(scope_id, name, variable);
1074        }
1075      }
1076      ExportedVariableInfo::VariableInfo(variable) => {
1077        self.definitions_db.set(scope_id, name, variable);
1078      }
1079    }
1080  }
1081
1082  fn undefined_variable(&mut self, name: &Atom) {
1083    self.definitions_db.delete(self.definitions, name)
1084  }
1085
1086  pub fn tag_variable<Data: TagInfoData>(
1087    &mut self,
1088    name: Atom,
1089    tag: &'static str,
1090    data: Option<Data>,
1091  ) {
1092    self.tag_variable_impl(name, tag, data.map(TagInfoData::into_any), None);
1093  }
1094
1095  pub fn tag_variable_with_flags<Data: TagInfoData>(
1096    &mut self,
1097    name: Atom,
1098    tag: &'static str,
1099    data: Option<Data>,
1100    flags: VariableInfoFlags,
1101  ) {
1102    self.tag_variable_impl(name, tag, data.map(TagInfoData::into_any), Some(flags));
1103  }
1104
1105  pub fn tag_variable_without_data(&mut self, name: Atom, tag: &'static str) {
1106    self.tag_variable_impl(name, tag, None, None);
1107  }
1108
1109  fn tag_variable_impl(
1110    &mut self,
1111    name: Atom,
1112    tag: &'static str,
1113    data: Option<Box<dyn anymap::CloneAny>>,
1114    flags: Option<VariableInfoFlags>,
1115  ) {
1116    let flags = flags.unwrap_or(VariableInfoFlags::TAGGED);
1117    let new_info = if let Some(old_info_id) = self.definitions_db.get(self.definitions, &name) {
1118      let old_info = self.definitions_db.expect_get_variable(old_info_id);
1119      if let Some(old_tag_info) = old_info.tag_info {
1120        let declared_scope = old_info.declared_scope;
1121        // FIXME: remove `.clone`
1122        let name = old_info.name.clone();
1123        let flags = old_info.flags | flags;
1124        let tag_info = Some(TagInfo::create(
1125          &mut self.definitions_db,
1126          tag,
1127          data,
1128          Some(old_tag_info),
1129        ));
1130        VariableInfo::create(
1131          &mut self.definitions_db,
1132          declared_scope,
1133          name,
1134          flags,
1135          tag_info,
1136        )
1137      } else {
1138        let declared_scope = old_info.declared_scope;
1139        let tag_info = Some(TagInfo::create(&mut self.definitions_db, tag, data, None));
1140        VariableInfo::create(
1141          &mut self.definitions_db,
1142          declared_scope,
1143          Some(name.clone()),
1144          flags,
1145          tag_info,
1146        )
1147      }
1148    } else {
1149      let tag_info = Some(TagInfo::create(&mut self.definitions_db, tag, data, None));
1150      VariableInfo::create(
1151        &mut self.definitions_db,
1152        self.definitions,
1153        Some(name.clone()),
1154        flags,
1155        tag_info,
1156      )
1157    };
1158    self.definitions_db.set(self.definitions, name, new_info);
1159  }
1160
1161  fn _get_member_expression_info<'ast>(
1162    &mut self,
1163    object: ExprRef<'ast>,
1164    mut members: AtomMembers,
1165    mut members_optionals: OptionalMembers,
1166    mut member_ranges: MemberRanges,
1167    allowed_types: AllowedMemberTypes,
1168  ) -> Option<MemberExpressionInfo<'ast>> {
1169    match object {
1170      ExprRef::Call(expr) => {
1171        if !allowed_types.contains(AllowedMemberTypes::CallExpression) {
1172          return None;
1173        }
1174        let callee = expr.callee.as_expr()?;
1175        let (root_name, mut root_members) = if let Some(member) = callee.as_member() {
1176          let extracted = self.extract_member_expression_chain(ExprRef::Member(member));
1177          let root_name = extracted.object.get_root_name()?;
1178          (root_name, extracted.members)
1179        } else {
1180          (callee.get_root_name()?, AtomMembers::new())
1181        };
1182        let NameInfo {
1183          info: root_info, ..
1184        } = self.get_name_info_from_variable(&root_name)?;
1185
1186        root_members.reverse();
1187        members.reverse();
1188        members_optionals.reverse();
1189        member_ranges.reverse();
1190        let root_name_for_info = root_name.clone();
1191        Some(MemberExpressionInfo::Call(CallExpressionInfo {
1192          call: expr,
1193          root_info: root_info.map_or_else(
1194            || ExportedVariableInfo::Name(root_name_for_info),
1195            |i| ExportedVariableInfo::VariableInfo(i.id()),
1196          ),
1197          callee_members: root_members,
1198          members,
1199          members_optionals,
1200          member_ranges,
1201        }))
1202      }
1203      ExprRef::MetaProp(_) | ExprRef::Ident(_) | ExprRef::This(_) => {
1204        if !allowed_types.contains(AllowedMemberTypes::Expression) {
1205          return None;
1206        }
1207        let root_name = object.get_root_name()?;
1208
1209        let NameInfo {
1210          name: resolved_root,
1211          info: root_info,
1212        } = self.get_name_info_from_variable(&root_name)?;
1213
1214        let name = object_and_members_to_name(resolved_root, &members);
1215        members.reverse();
1216        members_optionals.reverse();
1217        member_ranges.reverse();
1218        let root_name_for_info = root_name.clone();
1219        Some(MemberExpressionInfo::Expression(ExpressionExpressionInfo {
1220          name,
1221          root_info: root_info.map_or_else(
1222            || ExportedVariableInfo::Name(root_name_for_info),
1223            |i| ExportedVariableInfo::VariableInfo(i.id()),
1224          ),
1225          members,
1226          members_optionals,
1227          member_ranges,
1228        }))
1229      }
1230      _ => None,
1231    }
1232  }
1233
1234  pub fn get_member_expression_info_from_expr<'ast>(
1235    &mut self,
1236    expr: &'ast Expr,
1237    allowed_types: AllowedMemberTypes,
1238  ) -> Option<MemberExpressionInfo<'ast>> {
1239    match expr {
1240      Expr::Member(_) | Expr::OptChain(_) => {
1241        self.get_member_expression_info(expr.into(), allowed_types)
1242      }
1243      _ => self._get_member_expression_info(
1244        expr.into(),
1245        AtomMembers::new(),
1246        OptionalMembers::new(),
1247        MemberRanges::new(),
1248        allowed_types,
1249      ),
1250    }
1251  }
1252
1253  pub fn get_member_expression_info<'ast>(
1254    &mut self,
1255    expr: ExprRef<'ast>,
1256    allowed_types: AllowedMemberTypes,
1257  ) -> Option<MemberExpressionInfo<'ast>> {
1258    let ExtractedMemberExpressionChainData {
1259      object,
1260      members,
1261      members_optionals,
1262      member_ranges,
1263    } = self.extract_member_expression_chain(expr);
1264    self._get_member_expression_info(
1265      object,
1266      members,
1267      members_optionals,
1268      member_ranges,
1269      allowed_types,
1270    )
1271  }
1272
1273  pub fn extract_member_expression_chain<'ast>(
1274    &self,
1275    expr: ExprRef<'ast>,
1276  ) -> ExtractedMemberExpressionChainData<'ast> {
1277    let mut object = expr;
1278    let mut members = AtomMembers::new();
1279    let mut members_optionals = OptionalMembers::new();
1280    let mut member_ranges = MemberRanges::new();
1281    let mut in_optional_chain = self.member_expr_in_optional_chain;
1282    loop {
1283      match object {
1284        ExprRef::Member(expr) => {
1285          if let Some(computed) = expr.prop.as_computed() {
1286            let Some(value) = member_property_to_atom(&computed.expr) else {
1287              break;
1288            };
1289            // Since members are not used across rspack javascript parser plugin,
1290            // we directly makes it atom here
1291            members.push(value);
1292            member_ranges.push(expr.obj.span());
1293          } else if let Some(ident) = expr.prop.as_ident() {
1294            members.push(Atom::from(&ident.sym));
1295            member_ranges.push(expr.obj.span());
1296          } else {
1297            break;
1298          }
1299          members_optionals.push(in_optional_chain);
1300          object = (&expr.obj).into();
1301          in_optional_chain = false;
1302        }
1303        ExprRef::OptChain(expr) => {
1304          in_optional_chain = expr.optional;
1305          if let OptChainBase::Member(member) = &expr.base {
1306            object = ExprRef::Member(member);
1307          } else {
1308            break;
1309          }
1310        }
1311        _ => break,
1312      }
1313    }
1314    ExtractedMemberExpressionChainData {
1315      object,
1316      members,
1317      members_optionals,
1318      member_ranges,
1319    }
1320  }
1321
1322  fn enter_ident<F>(&mut self, ident: &Ident, on_ident: F)
1323  where
1324    F: FnOnce(&mut Self, &Ident),
1325  {
1326    let drive = self.plugin_drive.clone();
1327    if !ident
1328      .sym
1329      .call_hooks_name(self, |parser, for_name| {
1330        drive.pattern(parser, ident, for_name)
1331      })
1332      .unwrap_or_default()
1333    {
1334      on_ident(self, ident);
1335    }
1336  }
1337
1338  fn enter_array_pattern<F>(&mut self, array_pat: &ArrayPat, on_ident: F)
1339  where
1340    F: FnOnce(&mut Self, &Ident) + Copy,
1341  {
1342    array_pat
1343      .elems
1344      .iter()
1345      .flatten()
1346      .for_each(|ele| self.enter_pattern(PatRef::Borrowed(ele), on_ident));
1347  }
1348
1349  fn enter_assignment_pattern<F>(&mut self, assign: &AssignPat, on_ident: F)
1350  where
1351    F: FnOnce(&mut Self, &Ident) + Copy,
1352  {
1353    self.enter_pattern(PatRef::Borrowed(&assign.left), on_ident);
1354  }
1355
1356  fn enter_object_pattern<F>(&mut self, obj: &ObjectPat, on_ident: F)
1357  where
1358    F: FnOnce(&mut Self, &Ident) + Copy,
1359  {
1360    for prop in &obj.props {
1361      match prop {
1362        ObjectPatProp::KeyValue(kv) => self.enter_pattern(PatRef::Borrowed(&kv.value), on_ident),
1363        ObjectPatProp::Assign(assign) => {
1364          let old = self.in_short_hand;
1365          // Defaulted shorthand bindings must also preserve their property key.
1366          self.in_short_hand = true;
1367          self.enter_ident(&assign.key.id, on_ident);
1368          self.in_short_hand = old;
1369        }
1370        ObjectPatProp::Rest(rest) => self.enter_rest_pattern(rest, on_ident),
1371      }
1372    }
1373  }
1374
1375  fn enter_rest_pattern<F>(&mut self, rest: &RestPat, on_ident: F)
1376  where
1377    F: FnOnce(&mut Self, &Ident) + Copy,
1378  {
1379    self.enter_pattern(PatRef::Borrowed(&rest.arg), on_ident)
1380  }
1381
1382  pub(crate) fn enter_pattern<F>(&mut self, pattern: PatRef<'_>, on_ident: F)
1383  where
1384    F: FnOnce(&mut Self, &Ident) + Copy,
1385  {
1386    match pattern.as_pat() {
1387      Pat::Ident(ident) => self.enter_ident(&ident.id, on_ident),
1388      Pat::Array(array) => self.enter_array_pattern(array, on_ident),
1389      Pat::Assign(assign) => self.enter_assignment_pattern(assign, on_ident),
1390      Pat::Object(obj) => self.enter_object_pattern(obj, on_ident),
1391      Pat::Rest(rest) => self.enter_rest_pattern(rest, on_ident),
1392      Pat::Invalid(_) => (),
1393      Pat::Expr(_) => (),
1394    }
1395  }
1396
1397  fn enter_assignment_target<F>(&mut self, target: &AssignTarget, on_ident: F)
1398  where
1399    F: FnOnce(&mut Self, &Ident) + Copy,
1400  {
1401    let old = self.in_assignment_pattern;
1402    self.in_assignment_pattern = true;
1403    match target {
1404      AssignTarget::Simple(simple) => {
1405        if let Some(ident) = simple.as_ident() {
1406          self.enter_ident(&ident.id, on_ident);
1407        }
1408      }
1409      AssignTarget::Pat(pattern) => match &**pattern {
1410        AssignTargetPat::Array(array) => self.enter_array_pattern(array, on_ident),
1411        AssignTargetPat::Object(obj) => self.enter_object_pattern(obj, on_ident),
1412        AssignTargetPat::Invalid(_) => (),
1413      },
1414    }
1415    self.in_assignment_pattern = old;
1416  }
1417
1418  fn enter_patterns<'a, I, F>(&mut self, patterns: I, on_ident: F)
1419  where
1420    F: FnOnce(&mut Self, &Ident) + Copy,
1421    I: Iterator<Item = PatRef<'a>>,
1422  {
1423    for pattern in patterns {
1424      self.enter_pattern(pattern, on_ident);
1425    }
1426  }
1427
1428  fn enter_optional_chain<'a, C, M, R>(
1429    &mut self,
1430    expr: &'a OptChainExpr<'a>,
1431    on_call: C,
1432    on_member: M,
1433  ) -> R
1434  where
1435    C: FnOnce(&mut Self, &'a OptCall<'a>) -> R,
1436    M: FnOnce(&mut Self, &'a MemberExpr<'a>) -> R,
1437  {
1438    let member_expr_in_optional_chain = self.member_expr_in_optional_chain;
1439    let ret = match &expr.base {
1440      OptChainBase::Call(call) => {
1441        if call.callee.is_member() {
1442          self.member_expr_in_optional_chain = expr.optional;
1443        }
1444        on_call(self, call)
1445      }
1446      OptChainBase::Member(member) => {
1447        self.member_expr_in_optional_chain = expr.optional;
1448        on_member(self, member)
1449      }
1450    };
1451    self.member_expr_in_optional_chain = member_expr_in_optional_chain;
1452    ret
1453  }
1454
1455  fn enter_declaration<F>(&mut self, decl: &Decl, on_ident: F)
1456  where
1457    F: FnOnce(&mut Self, &Ident) + Copy,
1458  {
1459    match decl {
1460      Decl::Class(c) => {
1461        self.enter_ident(&c.ident, on_ident);
1462      }
1463      Decl::Fn(f) => {
1464        self.enter_ident(&f.ident, on_ident);
1465      }
1466      Decl::Var(var) => {
1467        for decl in &var.decls {
1468          self.enter_pattern(PatRef::Borrowed(&decl.name), on_ident);
1469        }
1470      }
1471      Decl::Using(_) => (),
1472    }
1473  }
1474
1475  fn enter_statement<S, H, F>(&mut self, statement: &S, call_hook: H, on_statement: F)
1476  where
1477    S: GetSpan,
1478    H: FnOnce(&mut Self, &S) -> bool,
1479    F: FnOnce(&mut Self, &S),
1480  {
1481    self.statement_path.push(statement.span().into());
1482    if call_hook(self, statement) {
1483      self.prev_statement = self.statement_path.pop();
1484      return;
1485    }
1486    on_statement(self, statement);
1487    self.prev_statement = self.statement_path.pop();
1488  }
1489
1490  pub fn enter_destructuring_assignment<'a>(
1491    &mut self,
1492    pattern: &ObjectPat<'a>,
1493    expr: &'a Expr<'a>,
1494  ) -> Option<&'a Expr<'a>> {
1495    let drive = self.plugin_drive.clone();
1496    let expr = if let Expr::Await(await_expr) = expr {
1497      &await_expr.arg
1498    } else {
1499      expr
1500    };
1501    let destructuring = if let Some(assign) = expr.as_assign()
1502      && let Some(pat) = assign.left.as_pat()
1503      && let Some(obj_pat) = pat.as_object()
1504    {
1505      self.enter_destructuring_assignment(obj_pat, &assign.right)
1506    } else {
1507      let can_collect = drive
1508        .can_collect_destructuring_assignment_properties(self, expr)
1509        .unwrap_or_default();
1510      can_collect.then_some(expr)
1511    };
1512    let destructuring_span = destructuring.map(|destructuring| destructuring.span());
1513    if let Some(destructuring_span) = destructuring_span
1514      && let Some(keys) =
1515        self.collect_destructuring_assignment_properties_from_object_pattern(pattern)
1516    {
1517      self
1518        .destructuring_assignment_properties
1519        .add(destructuring_span, keys);
1520    }
1521    destructuring
1522  }
1523
1524  pub fn walk_program(&mut self, ast: &Program) {
1525    let drive = self.plugin_drive.clone();
1526    if drive.program(self, ast).is_none() {
1527      match ast {
1528        Program::Module(m) => {
1529          self.set_strict(true);
1530          self.prev_statement = None;
1531          self.module_pre_walk_module_items(&m.body);
1532          self.prev_statement = None;
1533          self.pre_walk_module_items(&m.body);
1534          self.prev_statement = None;
1535          self.block_pre_walk_module_items(&m.body);
1536          self.prev_statement = None;
1537          self.walk_module_items(&m.body);
1538        }
1539        Program::Script(s) => {
1540          self.detect_mode(&s.body);
1541          self.prev_statement = None;
1542          self.pre_walk_statements(&s.body);
1543          self.prev_statement = None;
1544          self.block_pre_walk_statements(&s.body);
1545          self.prev_statement = None;
1546          self.walk_statements(&s.body);
1547        }
1548      }
1549    }
1550    drive.finish(self);
1551  }
1552
1553  fn set_strict(&mut self, value: bool) {
1554    let current_scope = self.definitions_db.expect_get_mut_scope(self.definitions);
1555    current_scope.is_strict = value;
1556  }
1557
1558  pub fn detect_mode(&mut self, stmts: &[Stmt]) {
1559    let Some(Lit::Str(str)) = stmts
1560      .first()
1561      .and_then(|stmt| stmt.as_expr())
1562      .and_then(|expr_stmt| expr_stmt.expr.as_lit())
1563    else {
1564      return;
1565    };
1566
1567    if str.value.as_wtf8().to_string_lossy().as_ref() == "use strict" {
1568      self.set_strict(true);
1569    }
1570  }
1571
1572  pub fn is_strict(&mut self) -> bool {
1573    let scope = self.definitions_db.expect_get_scope(self.definitions);
1574    scope.is_strict
1575  }
1576
1577  pub fn is_variable_defined<'key>(&mut self, name: impl Into<AtomRef<'key>>) -> bool {
1578    let Some(info) = self.get_variable_info(name) else {
1579      return false;
1580    };
1581    !info.is_free()
1582  }
1583}
1584
1585impl<'parser> JavascriptParser<'parser> {
1586  pub fn evaluate_expression<'a>(&mut self, expr: &'a Expr<'a>) -> BasicEvaluatedExpression<'a>
1587  where
1588    'parser: 'a,
1589  {
1590    match self.evaluating(expr) {
1591      Some(evaluated) => evaluated.with_expression(Some(expr)),
1592      None => BasicEvaluatedExpression::with_range(expr.span().real_lo(), expr.span().real_hi())
1593        .with_expression(Some(expr)),
1594    }
1595  }
1596
1597  pub fn evaluate<T: Display>(
1598    &mut self,
1599    source: String,
1600    error_title: T,
1601  ) -> Option<BasicEvaluatedExpression<'parser>> {
1602    eval::eval_source(self, source, error_title.to_string())
1603  }
1604
1605  // same as `JavascriptParser._initializeEvaluating` in webpack
1606  // FIXME: should mv it to plugin(for example `parse.hooks.evaluate for`)
1607  fn evaluating<'a>(&mut self, expr: &'a Expr<'a>) -> Option<BasicEvaluatedExpression<'a>>
1608  where
1609    'parser: 'a,
1610  {
1611    match expr {
1612      Expr::Tpl(tpl) => eval::eval_tpl_expression(self, tpl),
1613      Expr::TaggedTpl(tagged_tpl) => eval::eval_tagged_tpl_expression(self, tagged_tpl),
1614      Expr::Lit(lit) => eval::eval_lit_expr(lit),
1615      Expr::Cond(cond) => eval::eval_cond_expression(self, cond),
1616      Expr::Unary(unary) => eval::eval_unary_expression(self, unary),
1617      Expr::Bin(binary) => eval::eval_binary_expression(self, binary),
1618      Expr::Array(array) => eval::eval_array_expression(self, array),
1619      Expr::New(new) => eval::eval_new_expression(self, new),
1620      Expr::Call(call) => eval::eval_call_expression(self, call),
1621      Expr::OptChain(opt_chain) => self.enter_optional_chain(
1622        opt_chain,
1623        |parser, call| {
1624          let allocator: &'a Allocator = parser.ast.allocator;
1625          let call_expr = allocator.alloc(CallExpr {
1626            span: call.span,
1627            callee: Callee::Expr(allocator.boxed(call.callee.clone_in(allocator))),
1628            args: call.args.clone_in(allocator),
1629          });
1630          eval::eval_call_expression(parser, call_expr)
1631        },
1632        |parser, member| eval::eval_member_expression(parser, member, expr),
1633      ),
1634      Expr::Member(member) => eval::eval_member_expression(self, member, expr),
1635      Expr::Ident(ident) => {
1636        let name = Atom::from(&ident.sym);
1637        if name == "undefined" {
1638          let mut eval =
1639            BasicEvaluatedExpression::with_range(ident.span.real_lo(), ident.span.real_hi());
1640          eval.set_undefined();
1641          return Some(eval);
1642        }
1643        let drive = self.plugin_drive.clone();
1644        name
1645          .call_hooks_name(self, |parser, name| {
1646            drive.evaluate_identifier(
1647              parser,
1648              name,
1649              None,
1650              ident.span.real_lo(),
1651              ident.span.real_hi(),
1652            )
1653          })
1654          .or_else(|| {
1655            let info = self.get_variable_info(&name);
1656            if let Some(info) = info {
1657              if let Some(name) = &info.name
1658                && (info.is_free() || info.is_tagged())
1659              {
1660                let mut eval =
1661                  BasicEvaluatedExpression::with_range(ident.span.real_lo(), ident.span.real_hi());
1662                eval.set_identifier(
1663                  name.to_owned(),
1664                  ExportedVariableInfo::VariableInfo(info.id()),
1665                  None,
1666                  None,
1667                  None,
1668                );
1669                Some(eval)
1670              } else {
1671                None
1672              }
1673            } else {
1674              let mut eval =
1675                BasicEvaluatedExpression::with_range(ident.span.real_lo(), ident.span.real_hi());
1676              eval.set_identifier(
1677                Atom::from(&ident.sym),
1678                ExportedVariableInfo::Name(name.clone()),
1679                None,
1680                None,
1681                None,
1682              );
1683              Some(eval)
1684            }
1685          })
1686      }
1687      Expr::This(this) => {
1688        let drive = self.plugin_drive.clone();
1689        let default_eval = || {
1690          let mut eval =
1691            BasicEvaluatedExpression::with_range(this.span.real_lo(), this.span.real_hi());
1692          eval.set_identifier(
1693            "this".into(),
1694            ExportedVariableInfo::Name("this".into()),
1695            None,
1696            None,
1697            None,
1698          );
1699          Some(eval)
1700        };
1701        let Some(info) = self.get_variable_info("this") else {
1702          // use `ident.sym` as fallback for global variable(or maybe just a undefined variable)
1703          return drive
1704            .evaluate_identifier(self, "this", None, this.span.real_lo(), this.span.real_hi())
1705            .or_else(default_eval);
1706        };
1707        if let Some(name) = &info.name
1708          && (info.is_free() || info.is_tagged())
1709        {
1710          let name = name.clone();
1711          return drive
1712            .evaluate_identifier(self, &name, None, this.span.real_lo(), this.span.real_hi())
1713            .or_else(default_eval);
1714        }
1715        None
1716      }
1717      _ => None,
1718    }
1719  }
1720
1721  pub fn to_dependency_location(&mut self, range: DependencyRange) -> Option<DependencyLocation> {
1722    self
1723      .location_advancer
1724      .compute_dependency_location(self.source, range)
1725  }
1726}