1mod call_hooks_name;
2pub mod estree;
3mod walk;
4mod walk_block_pre;
5mod walk_module_pre;
6mod walk_pre;
7
8use std::{
9 borrow::Cow,
10 fmt::Display,
11 hash::{Hash, Hasher},
12 rc::Rc,
13 sync::Arc,
14};
15
16use bitflags::bitflags;
17pub use call_hooks_name::CallHooksName;
18use rspack_cacheable::{
19 cacheable,
20 with::{AsCacheable, AsOption, AsPreset, AsVec},
21};
22use rspack_core::{
23 AsyncDependenciesBlock, BoxDependency, BoxDependencyTemplate, BuildInfo, BuildMeta,
24 CompilerOptions, DependencyRange, FactoryMeta, JavascriptParserCommonjsExportsOption,
25 JavascriptParserOptions, ModuleIdentifier, ModuleLayer, ModuleType, ParseMeta, ResourceData,
26 TypeReexportPresenceMode,
27};
28use rspack_error::{Diagnostic, Result};
29use rspack_util::SpanExt;
30use rustc_hash::{FxHashMap, FxHashSet};
31use swc_core::{
32 atoms::Atom,
33 common::{
34 BytePos, Mark, SourceFile, SourceMap, Span, Spanned, comments::Comments, util::take::Take,
35 },
36 ecma::{
37 ast::{
38 ArrayPat, AssignPat, AssignTargetPat, CallExpr, Decl, Expr, Ident, Lit, MemberExpr,
39 MetaPropExpr, MetaPropKind, ObjectPat, ObjectPatProp, OptCall, OptChainBase, OptChainExpr,
40 Pat, Program, RestPat, Stmt, ThisExpr,
41 },
42 utils::ExprFactory,
43 },
44};
45
46use crate::{
47 BoxJavascriptParserPlugin,
48 dependency::local_module::LocalModule,
49 parser_plugin::{
50 self, ImportsReferencesState, InnerGraphState, JavaScriptParserPluginDrive,
51 JavascriptParserPlugin,
52 },
53 utils::eval::{self, BasicEvaluatedExpression},
54 visitors::{
55 ScanDependenciesResult,
56 scope_info::{
57 ScopeInfoDB, ScopeInfoId, TagInfo, TagInfoId, VariableInfo, VariableInfoFlags, VariableInfoId,
58 },
59 },
60};
61
62pub trait TagInfoData: Clone + Sized + 'static {
63 fn into_any(data: Self) -> Box<dyn anymap::CloneAny>;
64
65 fn downcast(any: Box<dyn anymap::CloneAny>) -> Self;
66}
67
68impl<T> TagInfoData for T
69where
70 T: Clone + Sized + 'static,
71{
72 fn into_any(data: Self) -> Box<dyn anymap::CloneAny> {
73 Box::new(data)
74 }
75
76 fn downcast(any: Box<dyn anymap::CloneAny>) -> Self {
77 *(any as Box<dyn std::any::Any>)
78 .downcast()
79 .expect("TagInfoData should be downcasted from correct tag info")
80 }
81}
82
83#[derive(Debug)]
84pub struct ExtractedMemberExpressionChainData {
85 pub object: Expr,
86 pub members: Vec<Atom>,
87 pub members_optionals: Vec<bool>,
88 pub member_ranges: Vec<Span>,
89}
90
91bitflags! {
92 #[derive(Clone, Copy)]
93 pub struct AllowedMemberTypes: u8 {
94 const CallExpression = 1 << 0;
95 const Expression = 1 << 1;
96 }
97}
98
99#[derive(Debug)]
100pub enum MemberExpressionInfo {
101 Call(CallExpressionInfo),
102 Expression(ExpressionExpressionInfo),
103}
104
105#[derive(Debug)]
106pub struct CallExpressionInfo {
107 pub call: CallExpr,
108 pub callee_name: String,
109 pub root_info: ExportedVariableInfo,
110 pub callee_members: Vec<Atom>,
111 pub members: Vec<Atom>,
112 pub members_optionals: Vec<bool>,
113 pub member_ranges: Vec<Span>,
114}
115
116#[derive(Debug)]
117pub struct ExpressionExpressionInfo {
118 pub name: String,
119 pub root_info: ExportedVariableInfo,
120 pub members: Vec<Atom>,
121 pub members_optionals: Vec<bool>,
122 pub member_ranges: Vec<Span>,
123}
124
125#[derive(Debug, Clone)]
126pub enum ExportedVariableInfo {
127 Name(Atom),
128 VariableInfo(VariableInfoId),
129}
130
131fn object_and_members_to_name(object: String, members_reversed: &[impl AsRef<str>]) -> String {
132 let mut name = object;
133 let iter = members_reversed.iter();
134 for member in iter.rev() {
135 name.push('.');
136 name.push_str(member.as_ref());
137 }
138 name
139}
140
141pub trait RootName {
142 fn get_root_name(&self) -> Option<Atom> {
143 None
144 }
145}
146
147impl RootName for Expr {
148 fn get_root_name(&self) -> Option<Atom> {
149 match self {
150 Expr::Ident(ident) => ident.get_root_name(),
151 Expr::This(this) => this.get_root_name(),
152 Expr::MetaProp(meta) => meta.get_root_name(),
153 _ => None,
154 }
155 }
156}
157
158impl RootName for ThisExpr {
159 fn get_root_name(&self) -> Option<Atom> {
160 Some("this".into())
161 }
162}
163
164impl RootName for Ident {
165 fn get_root_name(&self) -> Option<Atom> {
166 Some(self.sym.clone())
167 }
168}
169
170impl RootName for MetaPropExpr {
171 fn get_root_name(&self) -> Option<Atom> {
172 match self.kind {
173 MetaPropKind::NewTarget => Some("new.target".into()),
174 MetaPropKind::ImportMeta => Some("import.meta".into()),
175 }
176 }
177}
178
179pub struct NameInfo<'a> {
180 pub name: &'a Atom,
181 pub info: Option<&'a VariableInfo>,
182}
183
184#[derive(Clone, Copy, Debug)]
185pub enum TopLevelScope {
186 Top,
187 ArrowFunction,
188 False,
189}
190
191#[derive(Debug, Clone, Copy)]
192pub struct StatementPath {
193 span: Span,
194}
195
196impl Spanned for StatementPath {
197 fn span(&self) -> Span {
198 self.span
199 }
200}
201
202impl StatementPath {
203 fn from_span(span: Span) -> Self {
204 Self { span }
205 }
206}
207
208impl From<Span> for StatementPath {
209 fn from(value: Span) -> Self {
210 Self::from_span(value)
211 }
212}
213
214#[cacheable]
215#[derive(Debug, Clone, Hash, PartialEq, Eq)]
216pub struct DestructuringAssignmentProperty {
217 pub range: DependencyRange,
218 #[cacheable(with=AsPreset)]
219 pub id: Atom,
220 #[cacheable(omit_bounds, with=AsOption<AsCacheable>)]
221 pub pattern: Option<DestructuringAssignmentProperties>,
222 pub shorthand: bool,
223}
224
225#[cacheable]
226#[derive(Debug, Default, Clone, PartialEq, Eq)]
227pub struct DestructuringAssignmentProperties {
228 #[cacheable(with=AsVec<AsCacheable>)]
229 inner: FxHashSet<DestructuringAssignmentProperty>,
230}
231
232impl Hash for DestructuringAssignmentProperties {
233 fn hash<H: Hasher>(&self, state: &mut H) {
234 for prop in &self.inner {
235 prop.hash(state);
236 }
237 }
238}
239
240impl DestructuringAssignmentProperties {
241 pub fn new(properties: FxHashSet<DestructuringAssignmentProperty>) -> Self {
242 Self { inner: properties }
243 }
244
245 pub fn insert(&mut self, prop: DestructuringAssignmentProperty) -> bool {
246 self.inner.insert(prop)
247 }
248
249 pub fn extend(&mut self, other: Self) {
250 self.inner.extend(other.inner);
251 }
252
253 pub fn iter(&self) -> impl Iterator<Item = &DestructuringAssignmentProperty> {
254 self.inner.iter()
255 }
256
257 pub fn traverse_on_left<'a, F>(&'a self, on_left_node: &mut F)
258 where
259 F: FnMut(&mut Vec<&'a DestructuringAssignmentProperty>),
260 {
261 self.traverse_impl(on_left_node, &mut |_| {}, &mut Vec::new());
262 }
263
264 pub fn traverse_on_enter<'a, F>(&'a self, on_enter_node: &mut F)
265 where
266 F: FnMut(&mut Vec<&'a DestructuringAssignmentProperty>),
267 {
268 self.traverse_impl(&mut |_| {}, on_enter_node, &mut Vec::new());
269 }
270
271 fn traverse_impl<'a, L, E>(
272 &'a self,
273 on_left_node: &mut L,
274 on_enter_node: &mut E,
275 stack: &mut Vec<&'a DestructuringAssignmentProperty>,
276 ) where
277 L: FnMut(&mut Vec<&'a DestructuringAssignmentProperty>),
278 E: FnMut(&mut Vec<&'a DestructuringAssignmentProperty>),
279 {
280 for prop in &self.inner {
281 stack.push(prop);
282 on_enter_node(stack);
283 if let Some(pattern) = &prop.pattern {
284 pattern.traverse_impl(on_left_node, on_enter_node, stack);
285 } else {
286 on_left_node(stack);
287 }
288 stack.pop();
289 }
290 }
291}
292
293#[derive(Debug, Default)]
294pub struct DestructuringAssignmentPropertiesMap {
295 inner: FxHashMap<Span, DestructuringAssignmentProperties>,
296}
297
298impl DestructuringAssignmentPropertiesMap {
299 pub fn add(&mut self, span: Span, props: DestructuringAssignmentProperties) {
300 self.inner.entry(span).or_default().extend(props)
301 }
302
303 pub fn get(&self, span: &Span) -> Option<&DestructuringAssignmentProperties> {
304 self.inner.get(span)
305 }
306}
307
308pub struct JavascriptParser<'parser> {
309 errors: Vec<Diagnostic>,
311 warning_diagnostics: Vec<Diagnostic>,
312 dependencies: Vec<BoxDependency>,
313 presentational_dependencies: Vec<BoxDependencyTemplate>,
314 #[allow(clippy::vec_box)]
317 blocks: Vec<Box<AsyncDependenciesBlock>>,
318 pub source_map: Arc<SourceMap>,
320 pub(crate) source_file: &'parser SourceFile,
321 pub parse_meta: ParseMeta,
322 pub comments: Option<&'parser dyn Comments>,
323 pub factory_meta: Option<&'parser FactoryMeta>,
324 pub build_meta: &'parser mut BuildMeta,
325 pub build_info: &'parser mut BuildInfo,
326 pub resource_data: &'parser ResourceData,
327 pub(crate) compiler_options: &'parser CompilerOptions,
328 pub(crate) javascript_options: &'parser JavascriptParserOptions,
329 pub(crate) module_type: &'parser ModuleType,
330 pub(crate) module_layer: Option<&'parser ModuleLayer>,
331 pub module_identifier: &'parser ModuleIdentifier,
332 pub(crate) plugin_drive: Rc<JavaScriptParserPluginDrive>,
333 pub(crate) definitions_db: ScopeInfoDB,
335 pub(super) definitions: ScopeInfoId,
336 pub(crate) top_level_scope: TopLevelScope,
337 pub(crate) current_tag_info: Option<TagInfoId>,
338 pub in_try: bool,
339 pub(crate) in_short_hand: bool,
340 pub(crate) in_tagged_template_tag: bool,
341 pub(crate) member_expr_in_optional_chain: bool,
342 pub(crate) semicolons: &'parser mut FxHashSet<BytePos>,
343 pub(crate) statement_path: Vec<StatementPath>,
344 pub(crate) prev_statement: Option<StatementPath>,
345 pub is_esm: bool,
346 pub(crate) destructuring_assignment_properties: DestructuringAssignmentPropertiesMap,
347 pub(crate) dynamic_import_references: ImportsReferencesState,
348 pub(crate) worker_index: u32,
349 pub(crate) parser_exports_state: Option<bool>,
350 pub(crate) local_modules: Vec<LocalModule>,
351 pub(crate) last_esm_import_order: i32,
352 pub(crate) inner_graph: InnerGraphState,
353 pub(crate) has_inlinable_const_decls: bool,
354}
355
356impl<'parser> JavascriptParser<'parser> {
357 #[allow(clippy::too_many_arguments)]
358 pub fn new(
359 source_map: Arc<SourceMap>,
360 source_file: &'parser SourceFile,
361 compiler_options: &'parser CompilerOptions,
362 javascript_options: &'parser JavascriptParserOptions,
363 comments: Option<&'parser dyn Comments>,
364 module_identifier: &'parser ModuleIdentifier,
365 module_type: &'parser ModuleType,
366 module_layer: Option<&'parser ModuleLayer>,
367 resource_data: &'parser ResourceData,
368 factory_meta: Option<&'parser FactoryMeta>,
369 build_meta: &'parser mut BuildMeta,
370 build_info: &'parser mut BuildInfo,
371 semicolons: &'parser mut FxHashSet<BytePos>,
372 unresolved_mark: Mark,
373 parser_plugins: &'parser mut Vec<BoxJavascriptParserPlugin>,
374 parse_meta: ParseMeta,
375 ) -> Self {
376 let warning_diagnostics: Vec<Diagnostic> = Vec::with_capacity(4);
377 let mut errors = Vec::with_capacity(4);
378 let dependencies = Vec::with_capacity(64);
379 let blocks = Vec::with_capacity(64);
380 let presentational_dependencies = Vec::with_capacity(64);
381 let parser_exports_state: Option<bool> = None;
382
383 let mut plugins: Vec<BoxJavascriptParserPlugin> = Vec::with_capacity(32);
384
385 plugins.append(parser_plugins);
386
387 plugins.push(Box::new(parser_plugin::InitializeEvaluating));
388 plugins.push(Box::new(parser_plugin::JavascriptMetaInfoPlugin));
389 plugins.push(Box::new(parser_plugin::CheckVarDeclaratorIdent));
390 plugins.push(Box::new(parser_plugin::ConstPlugin));
391 plugins.push(Box::new(parser_plugin::UseStrictPlugin));
392 plugins.push(Box::new(
393 parser_plugin::RequireContextDependencyParserPlugin,
394 ));
395 plugins.push(Box::new(
396 parser_plugin::RequireEnsureDependenciesBlockParserPlugin,
397 ));
398 plugins.push(Box::new(parser_plugin::CompatibilityPlugin));
399
400 if module_type.is_js_auto() || module_type.is_js_esm() {
401 plugins.push(Box::new(parser_plugin::ESMTopLevelThisParserPlugin));
402 plugins.push(Box::new(parser_plugin::ESMDetectionParserPlugin::new(
403 compiler_options.experiments.top_level_await,
404 )));
405 plugins.push(Box::new(
406 parser_plugin::ImportMetaContextDependencyParserPlugin,
407 ));
408 if let Some(true) = javascript_options.import_meta {
409 plugins.push(Box::new(parser_plugin::ImportMetaPlugin));
410 } else {
411 plugins.push(Box::new(parser_plugin::ImportMetaDisabledPlugin));
412 }
413
414 plugins.push(Box::new(parser_plugin::ESMImportDependencyParserPlugin));
415 plugins.push(Box::new(parser_plugin::ESMExportDependencyParserPlugin));
416 }
417
418 if compiler_options.amd.is_some() && (module_type.is_js_auto() || module_type.is_js_dynamic()) {
419 plugins.push(Box::new(
420 parser_plugin::AMDRequireDependenciesBlockParserPlugin,
421 ));
422 plugins.push(Box::new(parser_plugin::AMDDefineDependencyParserPlugin));
423 plugins.push(Box::new(parser_plugin::AMDParserPlugin));
424 }
425
426 if module_type.is_js_auto() || module_type.is_js_dynamic() {
427 plugins.push(Box::new(parser_plugin::CommonJsImportsParserPlugin));
428 plugins.push(Box::new(parser_plugin::CommonJsPlugin));
429 let commonjs_exports = javascript_options
430 .commonjs
431 .as_ref()
432 .map_or(JavascriptParserCommonjsExportsOption::Enable, |commonjs| {
433 commonjs.exports
434 });
435 if commonjs_exports != JavascriptParserCommonjsExportsOption::Disable {
436 plugins.push(Box::new(parser_plugin::CommonJsExportsParserPlugin::new(
437 commonjs_exports == JavascriptParserCommonjsExportsOption::SkipInEsm,
438 )));
439 }
440 if compiler_options.node.is_some() {
441 plugins.push(Box::new(parser_plugin::NodeStuffPlugin));
442 }
443 }
444
445 if module_type.is_js_auto() || module_type.is_js_dynamic() || module_type.is_js_esm() {
446 plugins.push(Box::new(parser_plugin::WebpackIsIncludedPlugin));
447 plugins.push(Box::new(parser_plugin::ExportsInfoApiPlugin));
448 plugins.push(Box::new(parser_plugin::APIPlugin::new(
449 compiler_options.output.module,
450 )));
451 plugins.push(Box::new(parser_plugin::ImportParserPlugin));
452 plugins.push(Box::new(parser_plugin::WorkerPlugin::new(
453 javascript_options
454 .worker
455 .as_ref()
456 .expect("should have worker"),
457 )));
458 plugins.push(Box::new(parser_plugin::OverrideStrictPlugin));
459 }
460
461 if javascript_options.inline_const.unwrap_or_default() {
463 if !compiler_options.experiments.inline_const {
464 errors.push(rspack_error::error!("inlineConst is still an experimental feature. To continue using it, please enable 'experiments.inlineConst'.").into());
465 } else {
466 plugins.push(Box::new(parser_plugin::InlineConstPlugin));
467 }
468 }
469 if compiler_options.optimization.inner_graph {
470 plugins.push(Box::new(parser_plugin::InnerGraphPlugin::new(
471 unresolved_mark,
472 )));
473 }
474
475 if !matches!(
476 javascript_options
477 .type_reexports_presence
478 .unwrap_or_default(),
479 TypeReexportPresenceMode::NoTolerant
480 ) && !compiler_options.experiments.type_reexports_presence
481 {
482 errors.push(rspack_error::error!("typeReexportsPresence is still an experimental feature. To continue using it, please enable 'experiments.typeReexportsPresence'.").into());
483 }
484
485 let plugin_drive = Rc::new(JavaScriptParserPluginDrive::new(plugins));
486 let mut db = ScopeInfoDB::new();
487
488 Self {
489 last_esm_import_order: 0,
490 comments,
491 javascript_options,
492 source_map,
493 source_file,
494 errors,
495 warning_diagnostics,
496 dependencies,
497 presentational_dependencies,
498 blocks,
499 in_try: false,
500 in_short_hand: false,
501 top_level_scope: TopLevelScope::Top,
502 is_esm: matches!(module_type, ModuleType::JsEsm),
503 in_tagged_template_tag: false,
504 definitions: db.create(),
505 definitions_db: db,
506 plugin_drive,
507 resource_data,
508 factory_meta,
509 build_meta,
510 build_info,
511 compiler_options,
512 module_type,
513 module_layer,
514 parser_exports_state,
515 worker_index: 0,
516 module_identifier,
517 member_expr_in_optional_chain: false,
518 destructuring_assignment_properties: Default::default(),
519 dynamic_import_references: Default::default(),
520 semicolons,
521 statement_path: Default::default(),
522 current_tag_info: None,
523 prev_statement: None,
524 inner_graph: InnerGraphState::new(),
525 parse_meta,
526 local_modules: Default::default(),
527 has_inlinable_const_decls: true,
528 }
529 }
530
531 pub fn into_results(self) -> Result<ScanDependenciesResult, Vec<Diagnostic>> {
532 if self.errors.is_empty() {
533 Ok(ScanDependenciesResult {
534 dependencies: self.dependencies,
535 blocks: self.blocks,
536 presentational_dependencies: self.presentational_dependencies,
537 warning_diagnostics: self.warning_diagnostics,
538 })
539 } else {
540 Err(self.errors)
541 }
542 }
543
544 pub fn add_dependency(&mut self, dep: BoxDependency) {
545 self.dependencies.push(dep);
546 }
547
548 pub fn add_dependencies(&mut self, deps: impl IntoIterator<Item = BoxDependency>) {
549 self.dependencies.extend(deps);
550 }
551
552 pub fn pop_dependency(&mut self) -> Option<BoxDependency> {
553 self.dependencies.pop()
554 }
555
556 pub fn next_dependency_idx(&self) -> usize {
557 self.dependencies.len()
558 }
559
560 pub fn get_dependency_mut(&mut self, idx: usize) -> Option<&mut BoxDependency> {
561 self.dependencies.get_mut(idx)
562 }
563
564 pub fn collect_dependencies_for_block(
565 &mut self,
566 f: impl FnOnce(&mut JavascriptParser),
567 ) -> Vec<BoxDependency> {
568 let old_deps = std::mem::take(&mut self.dependencies);
569 f(self);
570 std::mem::replace(&mut self.dependencies, old_deps)
571 }
572
573 pub fn add_presentational_dependency(&mut self, dep: BoxDependencyTemplate) {
574 self.presentational_dependencies.push(dep);
575 }
576
577 pub fn add_presentational_dependencies(
578 &mut self,
579 deps: impl IntoIterator<Item = BoxDependencyTemplate>,
580 ) {
581 self.presentational_dependencies.extend(deps);
582 }
583
584 pub fn next_presentational_dependency_idx(&self) -> usize {
585 self.presentational_dependencies.len()
586 }
587
588 pub fn get_presentational_dependency_mut(
589 &mut self,
590 idx: usize,
591 ) -> Option<&mut BoxDependencyTemplate> {
592 self.presentational_dependencies.get_mut(idx)
593 }
594
595 pub fn add_block(&mut self, block: Box<AsyncDependenciesBlock>) {
596 self.blocks.push(block);
597 }
598
599 pub fn next_block_idx(&self) -> usize {
600 self.blocks.len()
601 }
602
603 pub fn get_block_mut(&mut self, idx: usize) -> Option<&mut Box<AsyncDependenciesBlock>> {
604 self.blocks.get_mut(idx)
605 }
606
607 pub fn add_error(&mut self, error: Diagnostic) {
608 self.errors.push(error);
609 }
610
611 pub fn add_errors(&mut self, errors: impl IntoIterator<Item = Diagnostic>) {
612 self.errors.extend(errors);
613 }
614
615 pub fn add_warning(&mut self, warning: Diagnostic) {
616 self.warning_diagnostics.push(warning);
617 }
618
619 pub fn add_warnings(&mut self, warnings: impl IntoIterator<Item = Diagnostic>) {
620 self.warning_diagnostics.extend(warnings);
621 }
622
623 pub fn is_top_level_scope(&self) -> bool {
624 matches!(self.top_level_scope, TopLevelScope::Top)
625 }
626
627 pub fn is_top_level_this(&self) -> bool {
628 !matches!(self.top_level_scope, TopLevelScope::False)
629 }
630
631 pub fn add_local_module(&mut self, name: &Atom, dep_idx: usize) {
632 self.local_modules.push(LocalModule::new(
633 name.clone(),
634 self.local_modules.len(),
635 dep_idx,
636 ));
637 }
638
639 pub fn get_local_module_mut(&mut self, name: &str) -> Option<&mut LocalModule> {
640 self.local_modules.iter_mut().find(|m| m.get_name() == name)
641 }
642
643 pub fn is_asi_position(&self, pos: BytePos) -> bool {
644 let curr_path = self.statement_path.last().expect("Should in statement");
645 if curr_path.span_hi() == pos && self.semicolons.contains(&pos) {
646 true
647 } else if curr_path.span_lo() == pos
648 && let Some(prev) = &self.prev_statement
649 && self.semicolons.contains(&prev.span_hi())
650 {
651 true
652 } else {
653 false
654 }
655 }
656
657 pub fn set_asi_position(&mut self, pos: BytePos) -> bool {
658 self.semicolons.insert(pos)
659 }
660
661 pub fn unset_asi_position(&mut self, pos: BytePos) -> bool {
662 self.semicolons.remove(&pos)
663 }
664
665 pub fn is_statement_level_expression(&self, expr_span: Span) -> bool {
666 let Some(curr_path) = self.statement_path.last() else {
667 return false;
668 };
669 curr_path.span() == expr_span
670 }
671
672 pub fn get_module_layer(&self) -> Option<&ModuleLayer> {
673 self.module_layer
674 }
675
676 pub fn get_variable_info(&mut self, name: &Atom) -> Option<&VariableInfo> {
677 let id = self.definitions_db.get(self.definitions, name)?;
678 Some(self.definitions_db.expect_get_variable(id))
679 }
680
681 pub fn get_tag_data(
682 &mut self,
683 name: &Atom,
684 tag: &'static str,
685 ) -> Option<Box<dyn anymap::CloneAny>> {
686 self
687 .get_variable_info(name)
688 .and_then(|variable_info| variable_info.tag_info)
689 .and_then(|tag_info_id| {
690 let mut tag_info = Some(self.definitions_db.expect_get_tag_info(tag_info_id));
691
692 while let Some(cur_tag_info) = tag_info {
693 if cur_tag_info.tag == tag {
694 return cur_tag_info.data.clone();
695 }
696 tag_info = cur_tag_info
697 .next
698 .map(|tag_info_id| self.definitions_db.expect_get_tag_info(tag_info_id))
699 }
700
701 None
702 })
703 }
704
705 pub fn get_free_info_from_variable<'a>(&'a mut self, name: &'a Atom) -> Option<NameInfo<'a>> {
706 let Some(info) = self.get_variable_info(name) else {
707 return Some(NameInfo { name, info: None });
708 };
709 let Some(name) = &info.name else {
710 return None;
711 };
712 if !info.is_free() {
713 return None;
714 }
715 Some(NameInfo {
716 name,
717 info: Some(info),
718 })
719 }
720
721 pub fn get_name_info_from_variable<'a>(&'a mut self, name: &'a Atom) -> Option<NameInfo<'a>> {
722 let Some(info) = self.get_variable_info(name) else {
723 return Some(NameInfo { name, info: None });
724 };
725 let Some(name) = &info.name else {
726 return None;
727 };
728 if !info.is_free() && !info.is_tagged() {
729 return None;
730 }
731 Some(NameInfo {
732 name,
733 info: Some(info),
734 })
735 }
736
737 pub fn get_all_variables_from_current_scope(
738 &self,
739 ) -> impl Iterator<Item = (&str, &VariableInfoId)> {
740 let scope = self.definitions_db.expect_get_scope(self.definitions);
741 scope.variables()
742 }
743
744 pub fn define_variable(&mut self, name: Atom) {
745 let definitions = self.definitions;
746 if let Some(variable_info) = self.get_variable_info(&name)
747 && variable_info.tag_info.is_some()
748 && definitions == variable_info.declared_scope
749 {
750 return;
751 }
752 let info = VariableInfo::create(
753 &mut self.definitions_db,
754 definitions,
755 None,
756 VariableInfoFlags::NORMAL,
757 None,
758 );
759 self.definitions_db.set(definitions, name, info);
760 }
761
762 pub fn set_variable(&mut self, name: Atom, variable: ExportedVariableInfo) {
763 let scope_id = self.definitions;
764 match variable {
765 ExportedVariableInfo::Name(variable) => {
766 if name == variable {
767 self.definitions_db.delete(scope_id, &name);
768 } else {
769 let variable = VariableInfo::create(
770 &mut self.definitions_db,
771 scope_id,
772 Some(variable),
773 VariableInfoFlags::FREE,
774 None,
775 );
776 self.definitions_db.set(scope_id, name, variable);
777 }
778 }
779 ExportedVariableInfo::VariableInfo(variable) => {
780 self.definitions_db.set(scope_id, name, variable);
781 }
782 }
783 }
784
785 fn undefined_variable(&mut self, name: &Atom) {
786 self.definitions_db.delete(self.definitions, name)
787 }
788
789 pub fn tag_variable<Data: TagInfoData>(
790 &mut self,
791 name: Atom,
792 tag: &'static str,
793 data: Option<Data>,
794 ) {
795 self.tag_variable_impl(name, tag, data, None);
796 }
797
798 pub fn tag_variable_with_flags<Data: TagInfoData>(
799 &mut self,
800 name: Atom,
801 tag: &'static str,
802 data: Option<Data>,
803 flags: VariableInfoFlags,
804 ) {
805 self.tag_variable_impl(name, tag, data, Some(flags));
806 }
807
808 fn tag_variable_impl<Data: TagInfoData>(
809 &mut self,
810 name: Atom,
811 tag: &'static str,
812 data: Option<Data>,
813 flags: Option<VariableInfoFlags>,
814 ) {
815 let flags = flags.unwrap_or(VariableInfoFlags::TAGGED);
816 let data = data.map(|data| TagInfoData::into_any(data));
817 let new_info = if let Some(old_info_id) = self.definitions_db.get(self.definitions, &name) {
818 let old_info = self.definitions_db.expect_get_variable(old_info_id);
819 if let Some(old_tag_info) = old_info.tag_info {
820 let declared_scope = old_info.declared_scope;
821 let name = old_info.name.clone();
823 let flags = old_info.flags | flags;
824 let tag_info = Some(TagInfo::create(
825 &mut self.definitions_db,
826 tag,
827 data,
828 Some(old_tag_info),
829 ));
830 VariableInfo::create(
831 &mut self.definitions_db,
832 declared_scope,
833 name,
834 flags,
835 tag_info,
836 )
837 } else {
838 let declared_scope = old_info.declared_scope;
839 let tag_info = Some(TagInfo::create(&mut self.definitions_db, tag, data, None));
840 VariableInfo::create(
841 &mut self.definitions_db,
842 declared_scope,
843 Some(name.clone()),
844 flags,
845 tag_info,
846 )
847 }
848 } else {
849 let tag_info = Some(TagInfo::create(&mut self.definitions_db, tag, data, None));
850 VariableInfo::create(
851 &mut self.definitions_db,
852 self.definitions,
853 Some(name.clone()),
854 flags,
855 tag_info,
856 )
857 };
858 self.definitions_db.set(self.definitions, name, new_info);
859 }
860
861 fn _get_member_expression_info(
862 &mut self,
863 object: Expr,
864 mut members: Vec<Atom>,
865 mut members_optionals: Vec<bool>,
866 mut member_ranges: Vec<Span>,
867 allowed_types: AllowedMemberTypes,
868 ) -> Option<MemberExpressionInfo> {
869 match object {
870 Expr::Call(expr) => {
871 if !allowed_types.contains(AllowedMemberTypes::CallExpression) {
872 return None;
873 }
874 let callee = expr.callee.as_expr()?;
875 let (root_name, mut root_members) = if let Some(member) = callee.as_member() {
876 let extracted = self.extract_member_expression_chain(member);
877 let root_name = extracted.object.get_root_name()?;
878 (root_name, extracted.members)
879 } else {
880 (callee.get_root_name()?, vec![])
881 };
882 let NameInfo {
883 name: resolved_root,
884 info: root_info,
885 } = self.get_name_info_from_variable(&root_name)?;
886
887 let callee_name = object_and_members_to_name(resolved_root.to_string(), &root_members);
888 root_members.reverse();
889 members.reverse();
890 members_optionals.reverse();
891 member_ranges.reverse();
892 Some(MemberExpressionInfo::Call(CallExpressionInfo {
893 call: expr,
894 callee_name,
895 root_info: root_info
896 .map(|i| ExportedVariableInfo::VariableInfo(i.id()))
897 .unwrap_or_else(|| ExportedVariableInfo::Name(root_name)),
898 callee_members: root_members,
899 members,
900 members_optionals,
901 member_ranges,
902 }))
903 }
904 Expr::MetaProp(_) | Expr::Ident(_) | Expr::This(_) => {
905 if !allowed_types.contains(AllowedMemberTypes::Expression) {
906 return None;
907 }
908 let root_name = object.get_root_name()?;
909
910 let NameInfo {
911 name: resolved_root,
912 info: root_info,
913 } = self.get_name_info_from_variable(&root_name)?;
914
915 let name = object_and_members_to_name(resolved_root.to_string(), &members);
916 members.reverse();
917 members_optionals.reverse();
918 member_ranges.reverse();
919 Some(MemberExpressionInfo::Expression(ExpressionExpressionInfo {
920 name,
921 root_info: root_info
922 .map(|i| ExportedVariableInfo::VariableInfo(i.id()))
923 .unwrap_or_else(|| ExportedVariableInfo::Name(root_name)),
924 members,
925 members_optionals,
926 member_ranges,
927 }))
928 }
929 _ => None,
930 }
931 }
932
933 pub fn get_member_expression_info_from_expr(
934 &mut self,
935 expr: &Expr,
936 allowed_types: AllowedMemberTypes,
937 ) -> Option<MemberExpressionInfo> {
938 expr
939 .as_member()
940 .and_then(|member| self.get_member_expression_info(member, allowed_types))
941 .or_else(|| {
942 self._get_member_expression_info(expr.clone(), vec![], vec![], vec![], allowed_types)
943 })
944 }
945
946 pub fn get_member_expression_info(
947 &mut self,
948 expr: &MemberExpr,
949 allowed_types: AllowedMemberTypes,
950 ) -> Option<MemberExpressionInfo> {
951 let ExtractedMemberExpressionChainData {
952 object,
953 members,
954 members_optionals,
955 member_ranges,
956 } = self.extract_member_expression_chain(expr);
957 self._get_member_expression_info(
958 object,
959 members,
960 members_optionals,
961 member_ranges,
962 allowed_types,
963 )
964 }
965
966 pub fn extract_member_expression_chain(
967 &self,
968 expr: &MemberExpr,
969 ) -> ExtractedMemberExpressionChainData {
970 let mut object = Expr::Member(expr.clone());
971 let mut members = Vec::new();
972 let mut members_optionals = Vec::new();
973 let mut member_ranges = Vec::new();
974 let mut in_optional_chain = self.member_expr_in_optional_chain;
975 loop {
976 match &mut object {
977 Expr::Member(expr) => {
978 if let Some(computed) = expr.prop.as_computed() {
979 let Expr::Lit(lit) = &*computed.expr else {
980 break;
981 };
982 let value = match lit {
983 Lit::Str(s) => s.value.clone(),
984 Lit::Bool(b) => if b.value { "true" } else { "false" }.into(),
985 Lit::Null(_) => "null".into(),
986 Lit::Num(n) => n.value.to_string().into(),
987 Lit::BigInt(i) => i.value.to_string().into(),
988 Lit::Regex(r) => r.exp.clone(),
989 Lit::JSXText(_) => unreachable!(),
990 };
991 members.push(value);
992 member_ranges.push(expr.obj.span());
993 } else if let Some(ident) = expr.prop.as_ident() {
994 members.push(ident.sym.clone());
995 member_ranges.push(expr.obj.span());
996 } else {
997 break;
998 }
999 members_optionals.push(in_optional_chain);
1000 object = *expr.obj.take();
1001 in_optional_chain = false;
1002 }
1003 Expr::OptChain(expr) => {
1004 in_optional_chain = expr.optional;
1005 if let OptChainBase::Member(member) = &mut *expr.base {
1006 object = Expr::Member(member.take());
1007 } else {
1008 break;
1009 }
1010 }
1011 _ => break,
1012 }
1013 }
1014 ExtractedMemberExpressionChainData {
1015 object,
1016 members,
1017 members_optionals,
1018 member_ranges,
1019 }
1020 }
1021
1022 fn enter_ident<F>(&mut self, ident: &Ident, on_ident: F)
1023 where
1024 F: FnOnce(&mut Self, &Ident),
1025 {
1026 if !ident
1027 .sym
1028 .call_hooks_name(self, |parser, for_name| {
1029 parser.plugin_drive.clone().pattern(parser, ident, for_name)
1030 })
1031 .unwrap_or_default()
1032 {
1033 on_ident(self, ident);
1034 }
1035 }
1036
1037 fn enter_array_pattern<F>(&mut self, array_pat: &ArrayPat, on_ident: F)
1038 where
1039 F: FnOnce(&mut Self, &Ident) + Copy,
1040 {
1041 array_pat
1042 .elems
1043 .iter()
1044 .flatten()
1045 .for_each(|ele| self.enter_pattern(Cow::Borrowed(ele), on_ident));
1046 }
1047
1048 fn enter_assignment_pattern<F>(&mut self, assign: &AssignPat, on_ident: F)
1049 where
1050 F: FnOnce(&mut Self, &Ident) + Copy,
1051 {
1052 self.enter_pattern(Cow::Borrowed(&assign.left), on_ident);
1053 }
1054
1055 fn enter_object_pattern<F>(&mut self, obj: &ObjectPat, on_ident: F)
1056 where
1057 F: FnOnce(&mut Self, &Ident) + Copy,
1058 {
1059 for prop in &obj.props {
1060 match prop {
1061 ObjectPatProp::KeyValue(kv) => self.enter_pattern(Cow::Borrowed(&kv.value), on_ident),
1062 ObjectPatProp::Assign(assign) => {
1063 let old = self.in_short_hand;
1064 if assign.value.is_none() {
1065 self.in_short_hand = true;
1066 }
1067 self.enter_ident(&assign.key, on_ident);
1068 self.in_short_hand = old;
1069 }
1070 ObjectPatProp::Rest(rest) => self.enter_rest_pattern(rest, on_ident),
1071 }
1072 }
1073 }
1074
1075 fn enter_rest_pattern<F>(&mut self, rest: &RestPat, on_ident: F)
1076 where
1077 F: FnOnce(&mut Self, &Ident) + Copy,
1078 {
1079 self.enter_pattern(Cow::Borrowed(&rest.arg), on_ident)
1080 }
1081
1082 fn enter_pattern<F>(&mut self, pattern: Cow<Pat>, on_ident: F)
1083 where
1084 F: FnOnce(&mut Self, &Ident) + Copy,
1085 {
1086 match &*pattern {
1087 Pat::Ident(ident) => self.enter_ident(&ident.id, on_ident),
1088 Pat::Array(array) => self.enter_array_pattern(array, on_ident),
1089 Pat::Assign(assign) => self.enter_assignment_pattern(assign, on_ident),
1090 Pat::Object(obj) => self.enter_object_pattern(obj, on_ident),
1091 Pat::Rest(rest) => self.enter_rest_pattern(rest, on_ident),
1092 Pat::Invalid(_) => (),
1093 Pat::Expr(_) => (),
1094 }
1095 }
1096
1097 fn enter_assign_target_pattern<F>(&mut self, pattern: Cow<AssignTargetPat>, on_ident: F)
1098 where
1099 F: FnOnce(&mut Self, &Ident) + Copy,
1100 {
1101 match &*pattern {
1102 AssignTargetPat::Array(array) => self.enter_array_pattern(array, on_ident),
1103 AssignTargetPat::Object(obj) => self.enter_object_pattern(obj, on_ident),
1104 AssignTargetPat::Invalid(_) => (),
1105 }
1106 }
1107
1108 fn enter_patterns<'a, I, F>(&mut self, patterns: I, on_ident: F)
1109 where
1110 F: FnOnce(&mut Self, &Ident) + Copy,
1111 I: Iterator<Item = Cow<'a, Pat>>,
1112 {
1113 for pattern in patterns {
1114 self.enter_pattern(pattern, on_ident);
1115 }
1116 }
1117
1118 fn enter_optional_chain<'a, C, M, R>(
1119 &mut self,
1120 expr: &'a OptChainExpr,
1121 on_call: C,
1122 on_member: M,
1123 ) -> R
1124 where
1125 C: FnOnce(&mut Self, &'a OptCall) -> R,
1126 M: FnOnce(&mut Self, &'a MemberExpr) -> R,
1127 {
1128 let member_expr_in_optional_chain = self.member_expr_in_optional_chain;
1129 let ret = match &*expr.base {
1130 OptChainBase::Call(call) => {
1131 if call.callee.is_member() {
1132 self.member_expr_in_optional_chain = expr.optional;
1133 }
1134 on_call(self, call)
1135 }
1136 OptChainBase::Member(member) => {
1137 self.member_expr_in_optional_chain = expr.optional;
1138 on_member(self, member)
1139 }
1140 };
1141 self.member_expr_in_optional_chain = member_expr_in_optional_chain;
1142 ret
1143 }
1144
1145 fn enter_declaration<F>(&mut self, decl: &Decl, on_ident: F)
1146 where
1147 F: FnOnce(&mut Self, &Ident) + Copy,
1148 {
1149 match decl {
1150 Decl::Class(c) => {
1151 self.enter_ident(&c.ident, on_ident);
1152 }
1153 Decl::Fn(f) => {
1154 self.enter_ident(&f.ident, on_ident);
1155 }
1156 Decl::Var(var) => {
1157 for decl in &var.decls {
1158 self.enter_pattern(Cow::Borrowed(&decl.name), on_ident);
1159 }
1160 }
1161 Decl::Using(_) => (),
1162 _ => unreachable!(),
1163 }
1164 }
1165
1166 fn enter_statement<S, H, F>(&mut self, statement: &S, call_hook: H, on_statement: F)
1167 where
1168 S: Spanned,
1169 H: FnOnce(&mut Self, &S) -> bool,
1170 F: FnOnce(&mut Self, &S),
1171 {
1172 self.statement_path.push(statement.span().into());
1173 if call_hook(self, statement) {
1174 self.prev_statement = self.statement_path.pop();
1175 return;
1176 }
1177 on_statement(self, statement);
1178 self.prev_statement = self.statement_path.pop();
1179 }
1180
1181 pub fn enter_destructuring_assignment<'a>(
1182 &mut self,
1183 pattern: &ObjectPat,
1184 expr: &'a Expr,
1185 ) -> Option<&'a Expr> {
1186 let expr = if let Some(await_expr) = expr.as_await_expr() {
1187 &await_expr.arg
1188 } else {
1189 expr
1190 };
1191 let destructuring = if let Some(assign) = expr.as_assign()
1192 && let Some(pat) = assign.left.as_pat()
1193 && let Some(obj_pat) = pat.as_object()
1194 {
1195 self.enter_destructuring_assignment(obj_pat, &assign.right)
1196 } else {
1197 let can_collect = self
1198 .plugin_drive
1199 .clone()
1200 .can_collect_destructuring_assignment_properties(self, expr)
1201 .unwrap_or_default();
1202 can_collect.then_some(expr)
1203 };
1204 if let Some(destructuring) = destructuring
1205 && let Some(keys) =
1206 self.collect_destructuring_assignment_properties_from_object_pattern(pattern)
1207 {
1208 self
1209 .destructuring_assignment_properties
1210 .add(destructuring.span(), keys);
1211 }
1212 destructuring
1213 }
1214
1215 pub fn walk_program(&mut self, ast: &Program) {
1216 if self.plugin_drive.clone().program(self, ast).is_none() {
1217 match ast {
1218 Program::Module(m) => {
1219 self.set_strict(true);
1220 self.prev_statement = None;
1221 self.module_pre_walk_module_items(&m.body);
1222 self.prev_statement = None;
1223 self.pre_walk_module_items(&m.body);
1224 self.prev_statement = None;
1225 self.block_pre_walk_module_items(&m.body);
1226 self.prev_statement = None;
1227 self.walk_module_items(&m.body);
1228 }
1229 Program::Script(s) => {
1230 self.detect_mode(&s.body);
1231 self.prev_statement = None;
1232 self.pre_walk_statements(&s.body);
1233 self.prev_statement = None;
1234 self.block_pre_walk_statements(&s.body);
1235 self.prev_statement = None;
1236 self.walk_statements(&s.body);
1237 }
1238 };
1239 }
1240 self.plugin_drive.clone().finish(self);
1241 }
1242
1243 fn set_strict(&mut self, value: bool) {
1244 let current_scope = self.definitions_db.expect_get_mut_scope(self.definitions);
1245 current_scope.is_strict = value;
1246 }
1247
1248 pub fn detect_mode(&mut self, stmts: &[Stmt]) {
1249 let Some(Lit::Str(str)) = stmts
1250 .first()
1251 .and_then(|stmt| stmt.as_expr())
1252 .and_then(|expr_stmt| expr_stmt.expr.as_lit())
1253 else {
1254 return;
1255 };
1256
1257 if str.value.as_str() == "use strict" {
1258 self.set_strict(true);
1259 }
1260 }
1261
1262 pub fn is_strict(&mut self) -> bool {
1263 let scope = self.definitions_db.expect_get_scope(self.definitions);
1264 scope.is_strict
1265 }
1266
1267 pub fn is_variable_defined(&mut self, name: &Atom) -> bool {
1268 let Some(info) = self.get_variable_info(name) else {
1269 return false;
1270 };
1271 !info.is_free()
1272 }
1273}
1274
1275impl JavascriptParser<'_> {
1276 pub fn evaluate_expression<'a>(&mut self, expr: &'a Expr) -> BasicEvaluatedExpression<'a> {
1277 match self.evaluating(expr) {
1278 Some(evaluated) => evaluated.with_expression(Some(expr)),
1279 None => BasicEvaluatedExpression::with_range(expr.span().real_lo(), expr.span().real_hi())
1280 .with_expression(Some(expr)),
1281 }
1282 }
1283
1284 pub fn evaluate<T: Display>(
1285 &mut self,
1286 source: String,
1287 error_title: T,
1288 ) -> Option<BasicEvaluatedExpression<'static>> {
1289 eval::eval_source(self, source, error_title)
1290 }
1291
1292 fn evaluating<'a>(&mut self, expr: &'a Expr) -> Option<BasicEvaluatedExpression<'a>> {
1295 match expr {
1296 Expr::Tpl(tpl) => eval::eval_tpl_expression(self, tpl),
1297 Expr::TaggedTpl(tagged_tpl) => eval::eval_tagged_tpl_expression(self, tagged_tpl),
1298 Expr::Lit(lit) => eval::eval_lit_expr(lit),
1299 Expr::Cond(cond) => eval::eval_cond_expression(self, cond),
1300 Expr::Unary(unary) => eval::eval_unary_expression(self, unary),
1301 Expr::Bin(binary) => eval::eval_binary_expression(self, binary),
1302 Expr::Array(array) => eval::eval_array_expression(self, array),
1303 Expr::New(new) => eval::eval_new_expression(self, new),
1304 Expr::Call(call) => eval::eval_call_expression(self, call),
1305 Expr::OptChain(opt_chain) => self.enter_optional_chain(
1306 opt_chain,
1307 |parser, call| {
1308 let expr = Expr::Call(CallExpr {
1309 ctxt: call.ctxt,
1310 span: call.span,
1311 callee: call.callee.clone().as_callee(),
1312 args: call.args.clone(),
1313 type_args: None,
1314 });
1315 BasicEvaluatedExpression::with_owned_expression(expr, |expr| {
1316 #[allow(clippy::unwrap_used)]
1317 let call_expr = expr.as_call().unwrap();
1318 eval::eval_call_expression(parser, call_expr)
1319 })
1320 },
1321 |parser, member| eval::eval_member_expression(parser, member, expr),
1322 ),
1323 Expr::Member(member) => eval::eval_member_expression(self, member, expr),
1324 Expr::Ident(ident) => {
1325 let name = &ident.sym;
1326 if name.eq("undefined") {
1327 let mut eval =
1328 BasicEvaluatedExpression::with_range(ident.span.real_lo(), ident.span.real_hi());
1329 eval.set_undefined();
1330 return Some(eval);
1331 }
1332 let drive = self.plugin_drive.clone();
1333 name
1334 .call_hooks_name(self, |parser, name| {
1335 drive.evaluate_identifier(parser, name, ident.span.real_lo(), ident.span.real_hi())
1336 })
1337 .or_else(|| {
1338 let info = self.get_variable_info(name);
1339 if let Some(info) = info {
1340 if let Some(name) = &info.name
1341 && (info.is_free() || info.is_tagged())
1342 {
1343 let mut eval =
1344 BasicEvaluatedExpression::with_range(ident.span.real_lo(), ident.span.real_hi());
1345 eval.set_identifier(
1346 name.to_owned(),
1347 ExportedVariableInfo::VariableInfo(info.id()),
1348 None,
1349 None,
1350 None,
1351 );
1352 Some(eval)
1353 } else {
1354 None
1355 }
1356 } else {
1357 let mut eval =
1358 BasicEvaluatedExpression::with_range(ident.span.real_lo(), ident.span.real_hi());
1359 eval.set_identifier(
1360 ident.sym.clone(),
1361 ExportedVariableInfo::Name(name.clone()),
1362 None,
1363 None,
1364 None,
1365 );
1366 Some(eval)
1367 }
1368 })
1369 }
1370 Expr::This(this) => {
1371 let drive = self.plugin_drive.clone();
1372 let default_eval = || {
1373 let mut eval =
1374 BasicEvaluatedExpression::with_range(this.span.real_lo(), this.span.real_hi());
1375 eval.set_identifier(
1376 "this".into(),
1377 ExportedVariableInfo::Name("this".into()),
1378 None,
1379 None,
1380 None,
1381 );
1382 Some(eval)
1383 };
1384 let Some(info) = self.get_variable_info(&"this".into()) else {
1385 return drive
1387 .evaluate_identifier(self, "this", this.span.real_lo(), this.span.real_hi())
1388 .or_else(default_eval);
1389 };
1390 if let Some(name) = &info.name
1391 && (info.is_free() || info.is_tagged())
1392 {
1393 let name = name.clone();
1394 return drive
1395 .evaluate_identifier(self, &name, this.span.real_lo(), this.span.real_hi())
1396 .or_else(default_eval);
1397 }
1398 None
1399 }
1400 _ => None,
1401 }
1402 }
1403}