From 8110288bf4c319854563d2d822e02a9afed1b1b8 Mon Sep 17 00:00:00 2001 From: Dunqing Date: Fri, 16 Feb 2024 20:52:35 +0800 Subject: [PATCH] perf(semantic): reduce visit parent nodes in resolve_reference_usages (#2419) --- crates/oxc_semantic/src/builder.rs | 116 +++++++++++++---------------- crates/oxc_semantic/src/lib.rs | 7 +- 2 files changed, 56 insertions(+), 67 deletions(-) diff --git a/crates/oxc_semantic/src/builder.rs b/crates/oxc_semantic/src/builder.rs index 96d8b6810..20306ee20 100644 --- a/crates/oxc_semantic/src/builder.rs +++ b/crates/oxc_semantic/src/builder.rs @@ -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, /// 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 + } } diff --git a/crates/oxc_semantic/src/lib.rs b/crates/oxc_semantic/src/lib.rs index 28f325ac4..0ad0784aa 100644 --- a/crates/oxc_semantic/src/lib.rs +++ b/crates/oxc_semantic/src/lib.rs @@ -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()),