Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 68 additions & 61 deletions crates/swc_ecma_minifier/src/compress/optimize/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -912,7 +912,6 @@ impl Optimizer<'_> {
}
}

/// Actually inlines variables.
pub(super) fn inline(&mut self, e: &mut Expr) {
if self.ctx.bit_ctx.contains(BitCtx::IsExactLhsOfAssign) {
return;
Expand All @@ -936,81 +935,89 @@ impl Optimizer<'_> {
}
}
Expr::Ident(i) => {
let id = i.to_id();
if let Some(value) = self.vars.lits.get(&id).or_else(|| {
if self.ctx.bit_ctx.contains(BitCtx::IsCallee) {
self.vars.simple_functions.get(&id)
} else {
None
}
}) {
if !matches!(**value, Expr::Ident(..) | Expr::Member(..))
&& self.ctx.bit_ctx.contains(BitCtx::IsUpdateArg)
{
return;
}

// currently renamer relies on the fact no distinct var has same ctxt, we need
// to remap all new bindings.
let bindings: FxHashSet<Id> = collect_decls(value);
let new_mark = Mark::new();
let mut cache = FxHashMap::default();
let mut remap = FxHashMap::default();

for id in bindings {
let new_ctxt = cache
.entry(id.1)
.or_insert_with(|| id.1.apply_mark(new_mark));
if let Some(value) = self.inline_ident(i) {
*e = *value
}
}
_ => (),
}
}

let new_ctxt = *new_ctxt;
/// Actually inlines variables.
pub(super) fn inline_ident(&mut self, ident: &Ident) -> Option<Box<Expr>> {
let id = ident.to_id();

if let Some(usage) = self.data.vars.get(&id).cloned() {
let new_id = (id.0.clone(), new_ctxt);
self.data.vars.insert(new_id, usage);
}
if let Some(value) = self.vars.lits.get(&id).or_else(|| {
if self.ctx.bit_ctx.contains(BitCtx::IsCallee) {
self.vars.simple_functions.get(&id)
} else {
None
}
}) {
if !matches!(**value, Expr::Ident(..) | Expr::Member(..))
&& self.ctx.bit_ctx.contains(BitCtx::IsUpdateArg)
{
return None;
}

remap.insert(id, new_ctxt);
}
// currently renamer relies on the fact no distinct var has same ctxt, we need
// to remap all new bindings.
let bindings: FxHashSet<Id> = collect_decls(value);
let new_mark = Mark::new();
let mut cache = FxHashMap::default();
let mut remap = FxHashMap::default();

let mut value = value.clone();
if !remap.is_empty() {
let mut remapper = Remapper::new(&remap);
value.visit_mut_with(&mut remapper);
}
for id in bindings {
let new_ctxt = cache
.entry(id.1)
.or_insert_with(|| id.1.apply_mark(new_mark));

self.changed = true;
report_change!("inline: Replacing a variable `{}` with cheap expression", i);
let new_ctxt = *new_ctxt;

*e = *value;
return;
if let Some(usage) = self.data.vars.get(&id).cloned() {
let new_id = (id.0.clone(), new_ctxt);
self.data.vars.insert(new_id, usage);
}

// Check without cloning
if let Some(value) = self.vars.vars_for_inlining.get(&id) {
if self.ctx.bit_ctx.contains(BitCtx::IsExactLhsOfAssign)
&& !is_valid_for_lhs(value)
{
return;
}
remap.insert(id, new_ctxt);
}

if let Expr::Member(..) = &**value {
if self.ctx.bit_ctx.contains(BitCtx::ExecutedMultipleTime) {
return;
}
}
}
let mut value = value.clone();
if !remap.is_empty() {
let mut remapper = Remapper::new(&remap);
value.visit_mut_with(&mut remapper);
}

if let Some(value) = self.vars.vars_for_inlining.remove(&id) {
self.changed = true;
report_change!("inline: Replacing '{}' with an expression", i);
self.changed = true;
report_change!(
"inline: Replacing a variable `{}` with cheap expression",
ident
);

*e = *value;
return Some(value);
}

// Check without cloning
if let Some(value) = self.vars.vars_for_inlining.get(&id) {
if self.ctx.bit_ctx.contains(BitCtx::IsExactLhsOfAssign) && !is_valid_for_lhs(value) {
return None;
}

log_abort!("inline: [Change] {}", crate::debug::dump(&*e, false))
if let Expr::Member(..) = &**value {
if self.ctx.bit_ctx.contains(BitCtx::ExecutedMultipleTime) {
return None;
}
}
_ => (),
}

if let Some(value) = self.vars.vars_for_inlining.remove(&id) {
self.changed = true;
report_change!("inline: Replacing '{}' with an expression", ident);

return Some(value);
}

None
}
}

Expand Down
17 changes: 16 additions & 1 deletion crates/swc_ecma_minifier/src/compress/optimize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ use super::util::{drop_invalid_stmts, is_fine_for_if_cons};
#[cfg(feature = "debug")]
use crate::debug::dump;
use crate::{
compress::{optimize::util::get_ids_of_pat, util::is_pure_undefined},
compress::{
optimize::util::{get_ids_of_pat, prop_name_from_ident},
util::is_pure_undefined,
},
debug::AssertValid,
maybe_par,
mode::Mode,
Expand Down Expand Up @@ -2489,6 +2492,18 @@ impl VisitMut for Optimizer<'_> {
n.retain(|p| !p.pat.is_invalid());
}

fn visit_mut_prop(&mut self, n: &mut Prop) {
n.visit_mut_children_with(self);

if let Prop::Shorthand(i) = n {
if let Some(expr) = self.inline_ident(i) {
let key = prop_name_from_ident(i.take());
*n = Prop::KeyValue(KeyValueProp { key, value: expr });
self.changed = true;
}
}
}

#[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
fn visit_mut_return_stmt(&mut self, n: &mut ReturnStmt) {
n.visit_mut_children_with(self);
Expand Down
2 changes: 1 addition & 1 deletion crates/swc_ecma_minifier/src/compress/optimize/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,7 @@ pub fn get_ids_of_pat(pat: &Pat) -> Vec<Id> {
/// Creates a PropName for a shorthand property, handling the special case of
/// `__proto__`. When the property name is `__proto__`, it must be converted to
/// a computed property to preserve JavaScript semantics.
fn prop_name_from_ident(ident: Ident) -> PropName {
pub(crate) fn prop_name_from_ident(ident: Ident) -> PropName {
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prop_name_from_ident is only referenced within the compress::optimize module (currently optimize/mod.rs and this file). Exposing it as pub(crate) increases the crate-wide API surface unnecessarily; consider reducing visibility to pub(super) (or pub(in crate::compress::optimize)) to keep the helper scoped to this module.

Suggested change
pub(crate) fn prop_name_from_ident(ident: Ident) -> PropName {
pub(super) fn prop_name_from_ident(ident: Ident) -> PropName {

Copilot uses AI. Check for mistakes.
if ident.sym == "__proto__" {
PropName::Computed(ComputedPropName {
span: ident.span,
Expand Down
Loading