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 std::{cell::RefCell, path::PathBuf, rc::Rc, sync::Arc};
|
||||||
|
|
||||||
use itertools::Itertools;
|
|
||||||
#[allow(clippy::wildcard_imports)]
|
#[allow(clippy::wildcard_imports)]
|
||||||
use oxc_ast::{ast::*, AstKind, Trivias, TriviasMap, Visit};
|
use oxc_ast::{ast::*, AstKind, Trivias, TriviasMap, Visit};
|
||||||
use oxc_diagnostics::Error;
|
use oxc_diagnostics::Error;
|
||||||
|
|
@ -62,6 +61,7 @@ pub struct SemanticBuilder<'a> {
|
||||||
pub namespace_stack: Vec<SymbolId>,
|
pub namespace_stack: Vec<SymbolId>,
|
||||||
/// If true, the current node is in the type definition
|
/// If true, the current node is in the type definition
|
||||||
in_type_definition: bool,
|
in_type_definition: bool,
|
||||||
|
current_reference_flag: ReferenceFlag,
|
||||||
|
|
||||||
// builders
|
// builders
|
||||||
pub nodes: AstNodes<'a>,
|
pub nodes: AstNodes<'a>,
|
||||||
|
|
@ -103,6 +103,7 @@ impl<'a> SemanticBuilder<'a> {
|
||||||
current_node_flags: NodeFlags::empty(),
|
current_node_flags: NodeFlags::empty(),
|
||||||
current_symbol_flags: SymbolFlags::empty(),
|
current_symbol_flags: SymbolFlags::empty(),
|
||||||
in_type_definition: false,
|
in_type_definition: false,
|
||||||
|
current_reference_flag: ReferenceFlag::empty(),
|
||||||
current_scope_id,
|
current_scope_id,
|
||||||
function_stack: vec![],
|
function_stack: vec![],
|
||||||
namespace_stack: vec![],
|
namespace_stack: vec![],
|
||||||
|
|
@ -1723,6 +1724,22 @@ impl<'a> SemanticBuilder<'a> {
|
||||||
AstKind::IdentifierReference(ident) => {
|
AstKind::IdentifierReference(ident) => {
|
||||||
self.reference_identifier(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) => {
|
AstKind::JSXElementName(elem) => {
|
||||||
self.reference_jsx_element_name(elem);
|
self.reference_jsx_element_name(elem);
|
||||||
}
|
}
|
||||||
|
|
@ -1774,6 +1791,20 @@ impl<'a> SemanticBuilder<'a> {
|
||||||
| AstKind::TSTypeAnnotation(_) => {
|
| AstKind::TSTypeAnnotation(_) => {
|
||||||
self.in_type_definition = false;
|
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.
|
/// Resolve reference flags for the current ast node.
|
||||||
fn resolve_reference_usages(&self) -> ReferenceFlag {
|
fn resolve_reference_usages(&self) -> ReferenceFlag {
|
||||||
if self.in_type_definition {
|
if self.in_type_definition {
|
||||||
return ReferenceFlag::Type;
|
ReferenceFlag::Type
|
||||||
}
|
} else if self.current_reference_flag.is_write()
|
||||||
|
&& !matches!(
|
||||||
let mut flags = ReferenceFlag::None;
|
self.nodes.parent_kind(self.current_node_id),
|
||||||
|
Some(AstKind::MemberExpression(_))
|
||||||
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>)>()
|
|
||||||
{
|
{
|
||||||
match (curr.kind(), parent.kind()) {
|
self.current_reference_flag
|
||||||
// lhs of assignment expression
|
} else {
|
||||||
(AstKind::SimpleAssignmentTarget(_), AstKind::AssignmentExpression(_)) => {
|
ReferenceFlag::Read
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_assert!(flags != ReferenceFlag::None);
|
|
||||||
|
|
||||||
flags
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reference_jsx_element_name(&mut self, elem: &JSXElementName) {
|
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
|
// simple cases
|
||||||
(SourceType::default(), "let a = 1; a = 2", ReferenceFlag::write()),
|
(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; b[a]", ReferenceFlag::read()),
|
||||||
(SourceType::default(), "let a = 1, b = 1, c; c = a + b", 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(), "function a() { return }; a()", ReferenceFlag::read()),
|
||||||
(SourceType::default(), "class a {}; new 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, b; b = a += 5", ReferenceFlag::read_write()),
|
||||||
(SourceType::default(), "let a = 1; 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
|
// 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::read_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, c; b = c = a", ReferenceFlag::read()),
|
(SourceType::default(), "let a, b, c; b = c = a", ReferenceFlag::read()),
|
||||||
// sequences return last value in sequence
|
// sequences return last value in sequence
|
||||||
(SourceType::default(), "let a, b; b = (0, a++)", ReferenceFlag::read_write()),
|
(SourceType::default(), "let a, b; b = (0, a++)", ReferenceFlag::read_write()),
|
||||||
|
|
@ -324,7 +325,7 @@ mod tests {
|
||||||
(
|
(
|
||||||
SourceType::default(),
|
SourceType::default(),
|
||||||
"let a; function foo(a) { return a }; foo(a = 1)",
|
"let a; function foo(a) { return a }; foo(a = 1)",
|
||||||
ReferenceFlag::write(),
|
ReferenceFlag::read_write(),
|
||||||
),
|
),
|
||||||
// typescript
|
// typescript
|
||||||
(typescript, "let a: number = 1; (a as any) = true", ReferenceFlag::write()),
|
(typescript, "let a: number = 1; (a as any) = true", ReferenceFlag::write()),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue