mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 20:32:10 +00:00
perf(semantic): reduce visit parent nodes in resolve_reference_usages (#2419)
This commit is contained in:
parent
cc2ddbee77
commit
8110288bf4
2 changed files with 56 additions and 67 deletions
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
use std::{cell::RefCell, path::PathBuf, rc::Rc, sync::Arc};
|
||||
|
||||
use itertools::Itertools;
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use oxc_ast::{ast::*, AstKind, Trivias, TriviasMap, Visit};
|
||||
use oxc_diagnostics::Error;
|
||||
|
|
@ -62,6 +61,7 @@ pub struct SemanticBuilder<'a> {
|
|||
pub namespace_stack: Vec<SymbolId>,
|
||||
/// If true, the current node is in the type definition
|
||||
in_type_definition: bool,
|
||||
current_reference_flag: ReferenceFlag,
|
||||
|
||||
// builders
|
||||
pub nodes: AstNodes<'a>,
|
||||
|
|
@ -103,6 +103,7 @@ impl<'a> SemanticBuilder<'a> {
|
|||
current_node_flags: NodeFlags::empty(),
|
||||
current_symbol_flags: SymbolFlags::empty(),
|
||||
in_type_definition: false,
|
||||
current_reference_flag: ReferenceFlag::empty(),
|
||||
current_scope_id,
|
||||
function_stack: vec![],
|
||||
namespace_stack: vec![],
|
||||
|
|
@ -1723,6 +1724,22 @@ impl<'a> SemanticBuilder<'a> {
|
|||
AstKind::IdentifierReference(ident) => {
|
||||
self.reference_identifier(ident);
|
||||
}
|
||||
AstKind::UpdateExpression(_) => {
|
||||
if self.is_not_expression_statement_parent() {
|
||||
self.current_reference_flag |= ReferenceFlag::Read;
|
||||
}
|
||||
self.current_reference_flag |= ReferenceFlag::Write;
|
||||
}
|
||||
AstKind::AssignmentExpression(expr) => {
|
||||
if self.is_not_expression_statement_parent()
|
||||
|| expr.operator != AssignmentOperator::Assign
|
||||
{
|
||||
self.current_reference_flag |= ReferenceFlag::Read;
|
||||
}
|
||||
}
|
||||
AstKind::AssignmentTarget(_) => {
|
||||
self.current_reference_flag |= ReferenceFlag::Write;
|
||||
}
|
||||
AstKind::JSXElementName(elem) => {
|
||||
self.reference_jsx_element_name(elem);
|
||||
}
|
||||
|
|
@ -1774,6 +1791,20 @@ impl<'a> SemanticBuilder<'a> {
|
|||
| AstKind::TSTypeAnnotation(_) => {
|
||||
self.in_type_definition = false;
|
||||
}
|
||||
AstKind::UpdateExpression(_) => {
|
||||
if self.is_not_expression_statement_parent() {
|
||||
self.current_reference_flag -= ReferenceFlag::Read;
|
||||
}
|
||||
self.current_reference_flag -= ReferenceFlag::Write;
|
||||
}
|
||||
AstKind::AssignmentExpression(expr) => {
|
||||
if self.is_not_expression_statement_parent()
|
||||
|| expr.operator != AssignmentOperator::Assign
|
||||
{
|
||||
self.current_reference_flag -= ReferenceFlag::Read;
|
||||
}
|
||||
}
|
||||
AstKind::AssignmentTarget(_) => self.current_reference_flag -= ReferenceFlag::Write,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
@ -1802,71 +1833,17 @@ impl<'a> SemanticBuilder<'a> {
|
|||
/// Resolve reference flags for the current ast node.
|
||||
fn resolve_reference_usages(&self) -> ReferenceFlag {
|
||||
if self.in_type_definition {
|
||||
return ReferenceFlag::Type;
|
||||
}
|
||||
|
||||
let mut flags = ReferenceFlag::None;
|
||||
|
||||
if self.nodes.parent_id(self.current_node_id).is_none() {
|
||||
return ReferenceFlag::Read;
|
||||
}
|
||||
|
||||
// This func should only get called when an IdentifierReference is
|
||||
// reached
|
||||
debug_assert!(matches!(
|
||||
self.nodes.get_node(self.current_node_id).kind(),
|
||||
AstKind::IdentifierReference(_)
|
||||
));
|
||||
|
||||
for (curr, parent) in self
|
||||
.nodes
|
||||
.iter_parents(self.current_node_id)
|
||||
.tuple_windows::<(&AstNode<'a>, &AstNode<'a>)>()
|
||||
ReferenceFlag::Type
|
||||
} else if self.current_reference_flag.is_write()
|
||||
&& !matches!(
|
||||
self.nodes.parent_kind(self.current_node_id),
|
||||
Some(AstKind::MemberExpression(_))
|
||||
)
|
||||
{
|
||||
match (curr.kind(), parent.kind()) {
|
||||
// lhs of assignment expression
|
||||
(AstKind::SimpleAssignmentTarget(_), AstKind::AssignmentExpression(_)) => {
|
||||
debug_assert!(!flags.is_read());
|
||||
flags = ReferenceFlag::write();
|
||||
// a lhs expr will not propagate upwards into a rhs
|
||||
// expression, sow e can safely break
|
||||
break;
|
||||
}
|
||||
(AstKind::AssignmentTarget(_), AstKind::AssignmentExpression(expr)) => {
|
||||
flags |= if expr.operator == AssignmentOperator::Assign {
|
||||
ReferenceFlag::write()
|
||||
} else {
|
||||
ReferenceFlag::read_write()
|
||||
};
|
||||
break;
|
||||
}
|
||||
(_, AstKind::SimpleAssignmentTarget(_) | AstKind::AssignmentTarget(_)) => {
|
||||
flags |= ReferenceFlag::write();
|
||||
// continue up tree
|
||||
}
|
||||
(_, AstKind::UpdateExpression(_)) => {
|
||||
flags |= ReferenceFlag::Write;
|
||||
// continue up tree
|
||||
}
|
||||
(
|
||||
AstKind::AssignmentTarget(_),
|
||||
AstKind::ForInStatement(_) | AstKind::ForOfStatement(_),
|
||||
) => {
|
||||
break;
|
||||
}
|
||||
(_, AstKind::ParenthesizedExpression(_)) => {
|
||||
// continue up tree
|
||||
}
|
||||
_ => {
|
||||
flags |= ReferenceFlag::Read;
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.current_reference_flag
|
||||
} else {
|
||||
ReferenceFlag::Read
|
||||
}
|
||||
|
||||
debug_assert!(flags != ReferenceFlag::None);
|
||||
|
||||
flags
|
||||
}
|
||||
|
||||
fn reference_jsx_element_name(&mut self, elem: &JSXElementName) {
|
||||
|
|
@ -1893,4 +1870,15 @@ impl<'a> SemanticBuilder<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_not_expression_statement_parent(&self) -> bool {
|
||||
for node in self.nodes.iter_parents(self.current_node_id).skip(1) {
|
||||
return match node.kind() {
|
||||
AstKind::ParenthesizedExpression(_) => continue,
|
||||
AstKind::ExpressionStatement(_) => false,
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -254,6 +254,7 @@ mod tests {
|
|||
// simple cases
|
||||
(SourceType::default(), "let a = 1; a = 2", ReferenceFlag::write()),
|
||||
(SourceType::default(), "let a = 1, b; b = a", ReferenceFlag::read()),
|
||||
(SourceType::default(), "let a = 1, b; b[a]", ReferenceFlag::read()),
|
||||
(SourceType::default(), "let a = 1, b = 1, c; c = a + b", ReferenceFlag::read()),
|
||||
(SourceType::default(), "function a() { return }; a()", ReferenceFlag::read()),
|
||||
(SourceType::default(), "class a {}; new a()", ReferenceFlag::read()),
|
||||
|
|
@ -285,8 +286,8 @@ mod tests {
|
|||
(SourceType::default(), "let a = 1, b; b = a += 5", ReferenceFlag::read_write()),
|
||||
(SourceType::default(), "let a = 1; a += 5", ReferenceFlag::read_write()),
|
||||
// note: we consider a to be written, and the read of `1` propagates upwards
|
||||
(SourceType::default(), "let a, b; b = a = 1", ReferenceFlag::write()),
|
||||
(SourceType::default(), "let a, b; b = (a = 1)", ReferenceFlag::write()),
|
||||
(SourceType::default(), "let a, b; b = a = 1", ReferenceFlag::read_write()),
|
||||
(SourceType::default(), "let a, b; b = (a = 1)", ReferenceFlag::read_write()),
|
||||
(SourceType::default(), "let a, b, c; b = c = a", ReferenceFlag::read()),
|
||||
// sequences return last value in sequence
|
||||
(SourceType::default(), "let a, b; b = (0, a++)", ReferenceFlag::read_write()),
|
||||
|
|
@ -324,7 +325,7 @@ mod tests {
|
|||
(
|
||||
SourceType::default(),
|
||||
"let a; function foo(a) { return a }; foo(a = 1)",
|
||||
ReferenceFlag::write(),
|
||||
ReferenceFlag::read_write(),
|
||||
),
|
||||
// typescript
|
||||
(typescript, "let a: number = 1; (a as any) = true", ReferenceFlag::write()),
|
||||
|
|
|
|||
Loading…
Reference in a new issue