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
143pub 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 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 errors: Vec<Diagnostic>,
414 warning_diagnostics: Vec<Diagnostic>,
415 dependencies: Vec<BoxDependency>,
416 presentational_dependencies: Vec<DependencyCodeGenerationRef>,
417 #[allow(clippy::vec_box)]
420 blocks: Vec<Option<Box<AsyncDependenciesBlock>>>,
421 block_parents: Vec<Option<usize>>,
422 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 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 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 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 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 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 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 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 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 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 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 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}