perf(transformer): React display name transform reduce Atom allocations (#3616)

Re-use existing `Atom`s in React Display Name transform rather than writing duplicate strings into the arena.

Also add comments for where our implementation diverges from Babel's (in my opinion, ours is an improvement).
This commit is contained in:
overlookmotel 2024-06-11 06:41:19 +00:00
parent 8d237c49a9
commit 3a59294c72
2 changed files with 31 additions and 21 deletions

View file

@ -810,17 +810,9 @@ impl<'a> MemberExpression<'a> {
pub fn static_property_name(&self) -> Option<&str> {
match self {
MemberExpression::ComputedMemberExpression(expr) => match &expr.expression {
Expression::StringLiteral(lit) => Some(&lit.value),
Expression::TemplateLiteral(lit) => {
if lit.expressions.is_empty() && lit.quasis.len() == 1 {
Some(&lit.quasis[0].value.raw)
} else {
None
}
}
_ => None,
},
MemberExpression::ComputedMemberExpression(expr) => {
expr.static_property_name().map(|name| name.as_str())
}
MemberExpression::StaticMemberExpression(expr) => Some(expr.property.name.as_str()),
MemberExpression::PrivateFieldExpression(_) => None,
}
@ -883,6 +875,20 @@ pub struct ComputedMemberExpression<'a> {
pub optional: bool, // for optional chaining
}
impl<'a> ComputedMemberExpression<'a> {
pub fn static_property_name(&self) -> Option<Atom<'a>> {
match &self.expression {
Expression::StringLiteral(lit) => Some(lit.value.clone()),
Expression::TemplateLiteral(lit)
if lit.expressions.is_empty() && lit.quasis.len() == 1 =>
{
Some(lit.quasis[0].value.raw.clone())
}
_ => None,
}
}
}
/// `MemberExpression[?Yield, ?Await] . IdentifierName`
#[visited_node]
#[derive(Debug, Hash)]

View file

@ -37,21 +37,22 @@ impl<'a> ReactDisplayName<'a> {
let name = ctx.find_ancestor(|ancestor| {
match ancestor {
// `foo = React.createClass({})`
Ancestor::AssignmentExpressionRight(assign_expr) => match &assign_expr.left() {
Ancestor::AssignmentExpressionRight(assign_expr) => match assign_expr.left() {
AssignmentTarget::AssignmentTargetIdentifier(ident) => {
FinderRet::Found(ident.name.clone())
}
target => {
if let Some(target) = target.as_member_expression() {
if let Some(name) = target.static_property_name() {
FinderRet::Found(ctx.ast.new_atom(name))
} else {
FinderRet::Stop
}
} else {
FinderRet::Stop
AssignmentTarget::StaticMemberExpression(expr) => {
FinderRet::Found(expr.property.name.clone())
}
// Babel does not handle computed member expressions e.g. `foo["bar"]`,
// so we diverge from Babel here, but that's probably an improvement
AssignmentTarget::ComputedMemberExpression(expr) => {
match expr.static_property_name() {
Some(name) => FinderRet::Found(name),
None => FinderRet::Stop,
}
}
_ => FinderRet::Stop,
},
// `let foo = React.createClass({})`
Ancestor::VariableDeclaratorInit(declarator) => match &declarator.id().kind {
@ -62,6 +63,9 @@ impl<'a> ReactDisplayName<'a> {
},
// `{foo: React.createClass({})}`
Ancestor::ObjectPropertyValue(prop) => {
// Babel only handles static identifiers e.g. `{foo: React.createClass({})}`,
// whereas we also handle e.g. `{"foo-bar": React.createClass({})}`,
// so we diverge from Babel here, but that's probably an improvement
if let Some(name) = prop.key().static_name() {
FinderRet::Found(ctx.ast.new_atom(&name))
} else {