perf(visit): Add linear AST traversal (#9452)

**Description:**

Babel decorator pass uses `fastTraverse`, and I think we can have one.

**Related issue:**

 - Closes https://github.com/swc-project/swc/issues/9451
This commit is contained in:
Donny/강동윤 2024-08-20 15:50:11 +09:00 committed by GitHub
parent 6187fcb134
commit 911d4eaa14
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 6926 additions and 18 deletions

View File

@ -0,0 +1,9 @@
---
swc_css_visit: patch
swc_ecma_visit: patch
swc_html_visit: patch
swc_xml_visit: patch
swc_core: patch
---
perf(visit): Add linear AST traversal

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -11092,7 +11092,6 @@ pub mod fields {
#[doc = "Represents [`TokenAndSpan::token`]"]
Token,
}
#[cfg(any(docsrs, feature = "path"))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum AstParentKind {
Attribute(AttributeField),
@ -11131,7 +11130,6 @@ pub mod fields {
}
}
}
#[cfg(any(docsrs, feature = "path"))]
#[derive(Debug, Clone, Copy)]
pub enum AstParentNodeRef<'ast> {
Attribute(&'ast Attribute, AttributeField),
@ -11176,6 +11174,7 @@ pub mod fields {
}
}
}
#[cfg(any(docsrs, feature = "path"))]
impl<'ast> AstParentNodeRef<'ast> {
#[inline]
pub fn kind(&self) -> AstParentKind {
@ -11202,5 +11201,185 @@ pub mod fields {
}
}
}
impl<'ast> From<&'ast Attribute> for NodeRef<'ast> {
fn from(node: &'ast Attribute) -> Self {
NodeRef::Attribute(node)
}
}
impl<'ast> From<&'ast AttributeToken> for NodeRef<'ast> {
fn from(node: &'ast AttributeToken) -> Self {
NodeRef::AttributeToken(node)
}
}
impl<'ast> From<&'ast Child> for NodeRef<'ast> {
fn from(node: &'ast Child) -> Self {
NodeRef::Child(node)
}
}
impl<'ast> From<&'ast Comment> for NodeRef<'ast> {
fn from(node: &'ast Comment) -> Self {
NodeRef::Comment(node)
}
}
impl<'ast> From<&'ast Document> for NodeRef<'ast> {
fn from(node: &'ast Document) -> Self {
NodeRef::Document(node)
}
}
impl<'ast> From<&'ast DocumentFragment> for NodeRef<'ast> {
fn from(node: &'ast DocumentFragment) -> Self {
NodeRef::DocumentFragment(node)
}
}
impl<'ast> From<&'ast DocumentMode> for NodeRef<'ast> {
fn from(node: &'ast DocumentMode) -> Self {
NodeRef::DocumentMode(node)
}
}
impl<'ast> From<&'ast DocumentType> for NodeRef<'ast> {
fn from(node: &'ast DocumentType) -> Self {
NodeRef::DocumentType(node)
}
}
impl<'ast> From<&'ast Element> for NodeRef<'ast> {
fn from(node: &'ast Element) -> Self {
NodeRef::Element(node)
}
}
impl<'ast> From<&'ast Namespace> for NodeRef<'ast> {
fn from(node: &'ast Namespace) -> Self {
NodeRef::Namespace(node)
}
}
impl<'ast> From<&'ast Raw> for NodeRef<'ast> {
fn from(node: &'ast Raw) -> Self {
NodeRef::Raw(node)
}
}
impl<'ast> From<&'ast Text> for NodeRef<'ast> {
fn from(node: &'ast Text) -> Self {
NodeRef::Text(node)
}
}
impl<'ast> From<&'ast Token> for NodeRef<'ast> {
fn from(node: &'ast Token) -> Self {
NodeRef::Token(node)
}
}
impl<'ast> From<&'ast TokenAndSpan> for NodeRef<'ast> {
fn from(node: &'ast TokenAndSpan) -> Self {
NodeRef::TokenAndSpan(node)
}
}
#[derive(Debug, Clone, Copy)]
pub enum NodeRef<'ast> {
Attribute(&'ast Attribute),
AttributeToken(&'ast AttributeToken),
Child(&'ast Child),
Comment(&'ast Comment),
Document(&'ast Document),
DocumentFragment(&'ast DocumentFragment),
DocumentMode(&'ast DocumentMode),
DocumentType(&'ast DocumentType),
Element(&'ast Element),
Namespace(&'ast Namespace),
Raw(&'ast Raw),
Text(&'ast Text),
Token(&'ast Token),
TokenAndSpan(&'ast TokenAndSpan),
}
impl<'ast> NodeRef<'ast> {
#[allow(unreachable_patterns)]
pub fn raw_children(&'ast self) -> Box<dyn 'ast + Iterator<Item = NodeRef<'ast>>> {
match self {
NodeRef::Attribute(node) => {
let iterator = ::std::iter::empty::<NodeRef<'ast>>().chain(
node.namespace
.iter()
.flat_map(|item| ::std::iter::once(NodeRef::Namespace(&item))),
);
Box::new(iterator)
}
NodeRef::AttributeToken(node) => {
let iterator = ::std::iter::empty::<NodeRef<'ast>>();
Box::new(iterator)
}
NodeRef::Child(node) => match node {
Child::DocumentType(v0) => Box::new(::std::iter::once(NodeRef::DocumentType(v0))),
Child::Element(v0) => Box::new(::std::iter::once(NodeRef::Element(v0))),
Child::Text(v0) => Box::new(::std::iter::once(NodeRef::Text(v0))),
Child::Comment(v0) => Box::new(::std::iter::once(NodeRef::Comment(v0))),
_ => Box::new(::std::iter::empty::<NodeRef<'ast>>()),
},
NodeRef::Comment(node) => {
let iterator = ::std::iter::empty::<NodeRef<'ast>>();
Box::new(iterator)
}
NodeRef::Document(node) => {
let iterator = ::std::iter::empty::<NodeRef<'ast>>()
.chain(::std::iter::once(NodeRef::DocumentMode(&node.mode)))
.chain(
node.children
.iter()
.flat_map(|item| ::std::iter::once(NodeRef::Child(&item))),
);
Box::new(iterator)
}
NodeRef::DocumentFragment(node) => {
let iterator = ::std::iter::empty::<NodeRef<'ast>>().chain(
node.children
.iter()
.flat_map(|item| ::std::iter::once(NodeRef::Child(&item))),
);
Box::new(iterator)
}
NodeRef::DocumentMode(node) => match node {
_ => Box::new(::std::iter::empty::<NodeRef<'ast>>()),
},
NodeRef::DocumentType(node) => {
let iterator = ::std::iter::empty::<NodeRef<'ast>>();
Box::new(iterator)
}
NodeRef::Element(node) => {
let iterator = ::std::iter::empty::<NodeRef<'ast>>()
.chain(::std::iter::once(NodeRef::Namespace(&node.namespace)))
.chain(
node.attributes
.iter()
.flat_map(|item| ::std::iter::once(NodeRef::Attribute(&item))),
)
.chain(
node.children
.iter()
.flat_map(|item| ::std::iter::once(NodeRef::Child(&item))),
)
.chain(
node.content
.iter()
.flat_map(|item| ::std::iter::once(NodeRef::DocumentFragment(&item))),
);
Box::new(iterator)
}
NodeRef::Namespace(node) => match node {
_ => Box::new(::std::iter::empty::<NodeRef<'ast>>()),
},
NodeRef::Raw(node) => match node {
_ => Box::new(::std::iter::empty::<NodeRef<'ast>>()),
},
NodeRef::Text(node) => {
let iterator = ::std::iter::empty::<NodeRef<'ast>>();
Box::new(iterator)
}
NodeRef::Token(node) => match node {
_ => Box::new(::std::iter::empty::<NodeRef<'ast>>()),
},
NodeRef::TokenAndSpan(node) => {
let iterator = ::std::iter::empty::<NodeRef<'ast>>()
.chain(::std::iter::once(NodeRef::Token(&node.token)));
Box::new(iterator)
}
}
}
}
#[cfg(any(docsrs, feature = "path"))]
pub use self::fields::{AstParentKind, AstParentNodeRef};

View File

@ -10660,7 +10660,6 @@ pub mod fields {
#[doc = "Represents [`TokenAndSpan::token`]"]
Token,
}
#[cfg(any(docsrs, feature = "path"))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum AstParentKind {
Attribute(AttributeField),
@ -10699,7 +10698,6 @@ pub mod fields {
}
}
}
#[cfg(any(docsrs, feature = "path"))]
#[derive(Debug, Clone, Copy)]
pub enum AstParentNodeRef<'ast> {
Attribute(&'ast Attribute, AttributeField),
@ -10744,6 +10742,7 @@ pub mod fields {
}
}
}
#[cfg(any(docsrs, feature = "path"))]
impl<'ast> AstParentNodeRef<'ast> {
#[inline]
pub fn kind(&self) -> AstParentKind {
@ -10770,5 +10769,178 @@ pub mod fields {
}
}
}
impl<'ast> From<&'ast Attribute> for NodeRef<'ast> {
fn from(node: &'ast Attribute) -> Self {
NodeRef::Attribute(node)
}
}
impl<'ast> From<&'ast AttributeToken> for NodeRef<'ast> {
fn from(node: &'ast AttributeToken) -> Self {
NodeRef::AttributeToken(node)
}
}
impl<'ast> From<&'ast CdataSection> for NodeRef<'ast> {
fn from(node: &'ast CdataSection) -> Self {
NodeRef::CdataSection(node)
}
}
impl<'ast> From<&'ast Child> for NodeRef<'ast> {
fn from(node: &'ast Child) -> Self {
NodeRef::Child(node)
}
}
impl<'ast> From<&'ast Comment> for NodeRef<'ast> {
fn from(node: &'ast Comment) -> Self {
NodeRef::Comment(node)
}
}
impl<'ast> From<&'ast Document> for NodeRef<'ast> {
fn from(node: &'ast Document) -> Self {
NodeRef::Document(node)
}
}
impl<'ast> From<&'ast DocumentMode> for NodeRef<'ast> {
fn from(node: &'ast DocumentMode) -> Self {
NodeRef::DocumentMode(node)
}
}
impl<'ast> From<&'ast DocumentType> for NodeRef<'ast> {
fn from(node: &'ast DocumentType) -> Self {
NodeRef::DocumentType(node)
}
}
impl<'ast> From<&'ast Element> for NodeRef<'ast> {
fn from(node: &'ast Element) -> Self {
NodeRef::Element(node)
}
}
impl<'ast> From<&'ast Namespace> for NodeRef<'ast> {
fn from(node: &'ast Namespace) -> Self {
NodeRef::Namespace(node)
}
}
impl<'ast> From<&'ast ProcessingInstruction> for NodeRef<'ast> {
fn from(node: &'ast ProcessingInstruction) -> Self {
NodeRef::ProcessingInstruction(node)
}
}
impl<'ast> From<&'ast Text> for NodeRef<'ast> {
fn from(node: &'ast Text) -> Self {
NodeRef::Text(node)
}
}
impl<'ast> From<&'ast Token> for NodeRef<'ast> {
fn from(node: &'ast Token) -> Self {
NodeRef::Token(node)
}
}
impl<'ast> From<&'ast TokenAndSpan> for NodeRef<'ast> {
fn from(node: &'ast TokenAndSpan) -> Self {
NodeRef::TokenAndSpan(node)
}
}
#[derive(Debug, Clone, Copy)]
pub enum NodeRef<'ast> {
Attribute(&'ast Attribute),
AttributeToken(&'ast AttributeToken),
CdataSection(&'ast CdataSection),
Child(&'ast Child),
Comment(&'ast Comment),
Document(&'ast Document),
DocumentMode(&'ast DocumentMode),
DocumentType(&'ast DocumentType),
Element(&'ast Element),
Namespace(&'ast Namespace),
ProcessingInstruction(&'ast ProcessingInstruction),
Text(&'ast Text),
Token(&'ast Token),
TokenAndSpan(&'ast TokenAndSpan),
}
impl<'ast> NodeRef<'ast> {
#[allow(unreachable_patterns)]
pub fn raw_children(&'ast self) -> Box<dyn 'ast + Iterator<Item = NodeRef<'ast>>> {
match self {
NodeRef::Attribute(node) => {
let iterator = ::std::iter::empty::<NodeRef<'ast>>().chain(
node.namespace
.iter()
.flat_map(|item| ::std::iter::once(NodeRef::Namespace(&item))),
);
Box::new(iterator)
}
NodeRef::AttributeToken(node) => {
let iterator = ::std::iter::empty::<NodeRef<'ast>>();
Box::new(iterator)
}
NodeRef::CdataSection(node) => {
let iterator = ::std::iter::empty::<NodeRef<'ast>>();
Box::new(iterator)
}
NodeRef::Child(node) => match node {
Child::DocumentType(v0) => Box::new(::std::iter::once(NodeRef::DocumentType(v0))),
Child::Element(v0) => Box::new(::std::iter::once(NodeRef::Element(v0))),
Child::Text(v0) => Box::new(::std::iter::once(NodeRef::Text(v0))),
Child::CdataSection(v0) => Box::new(::std::iter::once(NodeRef::CdataSection(v0))),
Child::Comment(v0) => Box::new(::std::iter::once(NodeRef::Comment(v0))),
Child::ProcessingInstruction(v0) => {
Box::new(::std::iter::once(NodeRef::ProcessingInstruction(v0)))
}
_ => Box::new(::std::iter::empty::<NodeRef<'ast>>()),
},
NodeRef::Comment(node) => {
let iterator = ::std::iter::empty::<NodeRef<'ast>>();
Box::new(iterator)
}
NodeRef::Document(node) => {
let iterator = ::std::iter::empty::<NodeRef<'ast>>().chain(
node.children
.iter()
.flat_map(|item| ::std::iter::once(NodeRef::Child(&item))),
);
Box::new(iterator)
}
NodeRef::DocumentMode(node) => match node {
_ => Box::new(::std::iter::empty::<NodeRef<'ast>>()),
},
NodeRef::DocumentType(node) => {
let iterator = ::std::iter::empty::<NodeRef<'ast>>();
Box::new(iterator)
}
NodeRef::Element(node) => {
let iterator = ::std::iter::empty::<NodeRef<'ast>>()
.chain(
node.attributes
.iter()
.flat_map(|item| ::std::iter::once(NodeRef::Attribute(&item))),
)
.chain(
node.children
.iter()
.flat_map(|item| ::std::iter::once(NodeRef::Child(&item))),
);
Box::new(iterator)
}
NodeRef::Namespace(node) => match node {
_ => Box::new(::std::iter::empty::<NodeRef<'ast>>()),
},
NodeRef::ProcessingInstruction(node) => {
let iterator = ::std::iter::empty::<NodeRef<'ast>>();
Box::new(iterator)
}
NodeRef::Text(node) => {
let iterator = ::std::iter::empty::<NodeRef<'ast>>();
Box::new(iterator)
}
NodeRef::Token(node) => match node {
_ => Box::new(::std::iter::empty::<NodeRef<'ast>>()),
},
NodeRef::TokenAndSpan(node) => {
let iterator = ::std::iter::empty::<NodeRef<'ast>>()
.chain(::std::iter::once(NodeRef::Token(&node.token)));
Box::new(iterator)
}
}
}
}
#[cfg(any(docsrs, feature = "path"))]
pub use self::fields::{AstParentKind, AstParentNodeRef};

View File

@ -1261,14 +1261,71 @@ fn extract_generic<'a>(name: &str, ty: &'a Type) -> Option<&'a Type> {
None
}
fn to_iter(e: TokenStream, ty: &Type, node_names: &[Ident]) -> Option<Expr> {
if let Some(ty) = extract_vec(ty) {
let inner_expr = to_iter(quote!(item), ty, node_names)?;
return Some(parse_quote!(#e.iter().flat_map(|item| #inner_expr)));
}
if let Some(ty) = extract_generic("Option", ty) {
let inner_expr = to_iter(quote!(item), ty, node_names)?;
return Some(parse_quote!(#e.iter().flat_map(|item| #inner_expr)));
}
if let Some(ty) = extract_generic("Box", ty) {
let inner_expr = to_iter(quote!(item), ty, node_names)?;
return Some(parse_quote!({
let item = &*#e;
#inner_expr
}));
}
if let Type::Path(p) = ty {
let ty = &p.path.segments.last().unwrap().ident;
if node_names.contains(ty) {
return Some(parse_quote!(::std::iter::once(NodeRef::#ty(&#e))));
}
None
} else {
todo!("to_iter for {:?}", ty);
}
}
fn define_fields(crate_name: &Ident, node_types: &[&Item]) -> Vec<Item> {
let mut items = Vec::<Item>::new();
let mut kind_enum_members = Vec::new();
let mut parent_enum_members = Vec::new();
let mut node_ref_enum_members = Vec::new();
let mut kind_set_index_arms = Vec::<Arm>::new();
let mut node_ref_set_index_arms = Vec::<Arm>::new();
let mut node_ref_kind_arms = Vec::<Arm>::new();
let mut node_ref_iter_next_arms = Vec::<Arm>::new();
let node_names = node_types
.iter()
.filter_map(|ty| match ty {
Item::Enum(data) => Some(data.ident.clone()),
Item::Struct(data) => Some(data.ident.clone()),
_ => None,
})
.collect::<Vec<_>>();
let is_node_ref_raw = |ty: &Type| match ty {
Type::Path(p) => node_names.contains(&p.path.segments.last().unwrap().ident),
_ => false,
};
let is_node_ref = |ty: &Type| {
if let Some(ty) = extract_generic("Box", ty) {
return is_node_ref_raw(ty);
}
is_node_ref_raw(ty)
};
{
let mut defs = Vec::<Item>::new();
@ -1330,10 +1387,58 @@ fn define_fields(crate_name: &Ident, node_types: &[&Item]) -> Vec<Item> {
#type_name(#fields_enum_name)
));
node_ref_enum_members.push(quote!(
parent_enum_members.push(quote!(
#type_name(&'ast #type_name, #fields_enum_name)
));
node_ref_enum_members.push(quote!(
#type_name(&'ast #type_name)
));
items.push(parse_quote!(
impl<'ast> From<&'ast #type_name> for NodeRef<'ast> {
fn from(node: &'ast #type_name) -> Self {
NodeRef::#type_name(node)
}
}
));
{
let mut arms = Vec::<Arm>::new();
for variant in &data.variants {
let variant_name = &variant.ident;
// TODO: Support all kinds of fields
if variant.fields.len() != 1 {
continue;
}
for f in variant.fields.iter().filter(|f| is_node_ref(&f.ty)) {
let mut ty = &f.ty;
if let Some(inner) = extract_generic("Box", ty) {
ty = inner;
}
arms.push(parse_quote!(
#type_name::#variant_name(v0) => {
Box::new(::std::iter::once(NodeRef::#ty(v0)))
},
));
}
}
node_ref_iter_next_arms.push(parse_quote!(
NodeRef::#type_name(node) => {
match node {
#(#arms)*
_ => Box::new(::std::iter::empty::<NodeRef<'ast>>())
}
}
));
}
defs.push(parse_quote!(
impl #fields_enum_name {
#[inline(always)]
@ -1361,10 +1466,51 @@ fn define_fields(crate_name: &Ident, node_types: &[&Item]) -> Vec<Item> {
#type_name(#fields_enum_name)
));
node_ref_enum_members.push(quote!(
parent_enum_members.push(quote!(
#type_name(&'ast #type_name, #fields_enum_name)
));
node_ref_enum_members.push(quote!(
#type_name(&'ast #type_name)
));
items.push(parse_quote!(
impl<'ast> From<&'ast #type_name> for NodeRef<'ast> {
fn from(node: &'ast #type_name) -> Self {
NodeRef::#type_name(node)
}
}
));
{
let mut iter: Expr = parse_quote!(::std::iter::empty::<NodeRef<'ast>>());
match &data.fields {
Fields::Named(fields) => {
for f in fields.named.iter() {
let ident = &f.ident;
let iter_expr =
to_iter(quote!(node.#ident), &f.ty, &node_names);
if let Some(iter_expr) = iter_expr {
iter = parse_quote!(#iter.chain(#iter_expr));
}
}
}
Fields::Unnamed(_fields) => {
// TODO: Support unnamed fields
}
Fields::Unit => {}
}
node_ref_iter_next_arms.push(parse_quote!(
NodeRef::#type_name(node) => {
let iterator = #iter;
Box::new(iterator)
}
));
}
defs.push(parse_quote!(
impl #fields_enum_name {
pub(crate) fn set_index(&mut self, index: usize) {
@ -1392,7 +1538,6 @@ fn define_fields(crate_name: &Ident, node_types: &[&Item]) -> Vec<Item> {
{
defs.push(parse_quote!(
#[cfg(any(docsrs, feature = "path"))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum AstParentKind {
#(#kind_enum_members),*
@ -1413,9 +1558,14 @@ fn define_fields(crate_name: &Ident, node_types: &[&Item]) -> Vec<Item> {
{
defs.push(parse_quote!(
#[cfg(any(docsrs, feature = "path"))]
#[derive(Debug, Clone, Copy)]
pub enum AstParentNodeRef<'ast> {
#(#parent_enum_members),*
}
));
items.push(parse_quote!(
#[derive(Debug, Clone, Copy)]
pub enum NodeRef<'ast> {
#(#node_ref_enum_members),*
}
));
@ -1437,6 +1587,7 @@ fn define_fields(crate_name: &Ident, node_types: &[&Item]) -> Vec<Item> {
}
));
defs.push(parse_quote!(
#[cfg(any(docsrs, feature = "path"))]
impl<'ast> AstParentNodeRef<'ast> {
#[inline]
pub fn kind(&self) -> AstParentKind {
@ -1446,14 +1597,27 @@ fn define_fields(crate_name: &Ident, node_types: &[&Item]) -> Vec<Item> {
}
}
));
items.push(parse_quote!(
impl<'ast> NodeRef<'ast> {
#[allow(unreachable_patterns)]
pub fn raw_children(&'ast self) -> Box<dyn 'ast + Iterator<Item = NodeRef<'ast>>> {
match self {
#(#node_ref_iter_next_arms)*
}
}
}
));
}
items.push(parse_quote!(
items.insert(
0,
parse_quote!(
#[cfg(any(docsrs, feature = "path"))]
pub mod fields {
#(#defs)*
}
));
),
);
items.push(parse_quote!(
#[cfg(any(docsrs, feature = "path"))]