diff --git a/crates/swc_ecma_minifier/src/compress/optimize/bools.rs b/crates/swc_ecma_minifier/src/compress/optimize/bools.rs index 5ef46f3fde6f..e1eb1dbb44ff 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/bools.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/bools.rs @@ -183,8 +183,14 @@ impl Optimizer<'_> { _ => return None, }; - let lt = left.get_type(self.ctx.expr_ctx); - let rt = right.get_type(self.ctx.expr_ctx); + let lt = left.get_type( + self.ctx.expr_ctx.unresolved_ctxt, + self.ctx.expr_ctx.remaining_depth, + ); + let rt = right.get_type( + self.ctx.expr_ctx.unresolved_ctxt, + self.ctx.expr_ctx.remaining_depth, + ); if let (Value::Known(Type::Undefined), Value::Known(Type::Null)) | (Value::Known(Type::Null), Value::Known(Type::Undefined)) = (lt, rt) { diff --git a/crates/swc_ecma_minifier/src/compress/optimize/mod.rs b/crates/swc_ecma_minifier/src/compress/optimize/mod.rs index 78be6ee3571f..6e822c2f5323 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/mod.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/mod.rs @@ -1562,11 +1562,17 @@ impl VisitMut for Optimizer<'_> { self.optimize_bin_and_or(n); if n.op == op!(bin, "+") { - if let Known(Type::Str) = n.left.get_type(self.ctx.expr_ctx) { + if let Known(Type::Str) = n.left.get_type( + self.ctx.expr_ctx.unresolved_ctxt, + self.ctx.expr_ctx.remaining_depth, + ) { self.optimize_expr_in_str_ctx(&mut n.right); } - if let Known(Type::Str) = n.right.get_type(self.ctx.expr_ctx) { + if let Known(Type::Str) = n.right.get_type( + self.ctx.expr_ctx.unresolved_ctxt, + self.ctx.expr_ctx.remaining_depth, + ) { self.optimize_expr_in_str_ctx(&mut n.left); } } diff --git a/crates/swc_ecma_minifier/src/compress/optimize/ops.rs b/crates/swc_ecma_minifier/src/compress/optimize/ops.rs index 19897778fbdb..2e468a65d053 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/ops.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/ops.rs @@ -57,8 +57,14 @@ impl Optimizer<'_> { } if e.op == op!("===") { - if let Known(lt) = e.left.get_type(self.ctx.expr_ctx) { - if let Known(rt) = e.right.get_type(self.ctx.expr_ctx) { + if let Known(lt) = e.left.get_type( + self.ctx.expr_ctx.unresolved_ctxt, + self.ctx.expr_ctx.remaining_depth, + ) { + if let Known(rt) = e.right.get_type( + self.ctx.expr_ctx.unresolved_ctxt, + self.ctx.expr_ctx.remaining_depth, + ) { if lt == rt { e.op = op!("=="); self.changed = true; @@ -131,7 +137,10 @@ impl Optimizer<'_> { | Expr::Bin(BinExpr { op: op!("<"), .. }) | Expr::Bin(BinExpr { op: op!(">="), .. }) | Expr::Bin(BinExpr { op: op!(">"), .. }) => { - if let Known(Type::Bool) = arg.get_type(self.ctx.expr_ctx) { + if let Known(Type::Bool) = arg.get_type( + self.ctx.expr_ctx.unresolved_ctxt, + self.ctx.expr_ctx.remaining_depth, + ) { self.changed = true; report_change!("Optimizing: `!!expr` => `expr`"); *e = *arg.take(); @@ -187,14 +196,20 @@ impl Optimizer<'_> { _ => {} } - let lt = bin.left.get_type(self.ctx.expr_ctx); + let lt = bin.left.get_type( + self.ctx.expr_ctx.unresolved_ctxt, + self.ctx.expr_ctx.remaining_depth, + ); match lt { // Don't change type Known(Type::Bool) => {} _ => return, } - let rt = bin.right.get_type(self.ctx.expr_ctx); + let rt = bin.right.get_type( + self.ctx.expr_ctx.unresolved_ctxt, + self.ctx.expr_ctx.remaining_depth, + ); match rt { Known(Type::Bool) => {} _ => return, diff --git a/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs b/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs index 83a2ab135615..635d0a3323df 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs @@ -1340,7 +1340,7 @@ impl Optimizer<'_> { } if let Callee::Expr(callee) = &e.callee { - if callee.is_pure_callee(self.ctx.expr_ctx) { + if callee.is_pure_callee(self.ctx.expr_ctx.unresolved_ctxt) { if !self.is_skippable_for_seq(a, callee) { return false; } @@ -1403,7 +1403,7 @@ impl Optimizer<'_> { false } OptChainBase::Call(e) => { - if e.callee.is_pure_callee(self.ctx.expr_ctx) { + if e.callee.is_pure_callee(self.ctx.expr_ctx.unresolved_ctxt) { if !self.is_skippable_for_seq(a, &e.callee) { return false; } @@ -2371,7 +2371,12 @@ impl Optimizer<'_> { Mergable::Drop => return Ok(false), }; - let a_type = a_right.as_deref().map(|a| a.get_type(self.ctx.expr_ctx)); + let a_type = a_right.as_deref().map(|a| { + a.get_type( + self.ctx.expr_ctx.unresolved_ctxt, + self.ctx.expr_ctx.remaining_depth, + ) + }); if let Some(a_right) = a_right { if a_right.is_this() || a_right.is_ident_ref_to("arguments") { @@ -2487,7 +2492,10 @@ impl Optimizer<'_> { let Some(a_type) = a_type else { return Ok(false); }; - let b_type = b.right.get_type(self.ctx.expr_ctx); + let b_type = b.right.get_type( + self.ctx.expr_ctx.unresolved_ctxt, + self.ctx.expr_ctx.remaining_depth, + ); if let Some(a_op) = a_op { if can_drop_op_for(a_op, b.op, var_type, a_type, b_type) { diff --git a/crates/swc_ecma_minifier/src/compress/optimize/unused.rs b/crates/swc_ecma_minifier/src/compress/optimize/unused.rs index 3e4de5a3f7cd..e00576b74909 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/unused.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/unused.rs @@ -107,21 +107,19 @@ impl Optimizer<'_> { #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))] pub(super) fn drop_unused_vars(&mut self, name: &mut Pat, init: Option<&mut Expr>) { - if self - .ctx - .bit_ctx - .intersects(BitCtx::IsExported | BitCtx::InAsm) - { + if !self.options.unused && !self.options.side_effects { return; } - trace_op!("unused: drop_unused_vars({})", dump(&*name, false)); - - if !self.options.unused && !self.options.side_effects { + if self.ctx.bit_ctx.intersects( + BitCtx::IsExported + .union(BitCtx::InAsm) + .union(BitCtx::InVarDeclOfForInOrOfLoop), + ) { return; } - if self.ctx.bit_ctx.contains(BitCtx::InVarDeclOfForInOrOfLoop) { + if !name.is_ident() && init.is_none() { return; } @@ -135,9 +133,7 @@ impl Optimizer<'_> { } } - if !name.is_ident() && init.is_none() { - return; - } + trace_op!("unused: drop_unused_vars({})", dump(&*name, false)); self.take_pat_if_unused(name, init, true); } diff --git a/crates/swc_ecma_minifier/src/compress/pure/bools.rs b/crates/swc_ecma_minifier/src/compress/pure/bools.rs index 069b1830742d..428da43c8739 100644 --- a/crates/swc_ecma_minifier/src/compress/pure/bools.rs +++ b/crates/swc_ecma_minifier/src/compress/pure/bools.rs @@ -263,7 +263,7 @@ impl Pure<'_> { || is_typeof_unaray(&e.right, &e.left) || (self.options.comparisons && matches!( - (e.left.get_type(self.expr_ctx), e.right.get_type(self.expr_ctx)), + (e.left.get_type(self.expr_ctx.unresolved_ctxt, self.expr_ctx.remaining_depth), e.right.get_type(self.expr_ctx.unresolved_ctxt, self.expr_ctx.remaining_depth)), (Value::Known(l), Value::Known(r)) if l == r )); @@ -306,8 +306,10 @@ impl Pure<'_> { right, .. }) => { - let lt = left.get_type(self.expr_ctx); - let rt = right.get_type(self.expr_ctx); + let lt = + left.get_type(self.expr_ctx.unresolved_ctxt, self.expr_ctx.remaining_depth); + let rt = + right.get_type(self.expr_ctx.unresolved_ctxt, self.expr_ctx.remaining_depth); if let (Value::Known(Type::Bool), Value::Known(Type::Bool)) = (lt, rt) { let rb = right.as_pure_bool(self.expr_ctx); diff --git a/crates/swc_ecma_minifier/src/compress/pure/conds.rs b/crates/swc_ecma_minifier/src/compress/pure/conds.rs index 25f96f2582ce..c43df26f9c6f 100644 --- a/crates/swc_ecma_minifier/src/compress/pure/conds.rs +++ b/crates/swc_ecma_minifier/src/compress/pure/conds.rs @@ -107,7 +107,9 @@ impl Pure<'_> { let Expr::Cond(cond) = e else { return }; - let lt = cond.cons.get_type(self.expr_ctx); + let lt = cond + .cons + .get_type(self.expr_ctx.unresolved_ctxt, self.expr_ctx.remaining_depth); if let Value::Known(Type::Bool) = lt { let lb = cond.cons.as_pure_bool(self.expr_ctx); if let Value::Known(true) = lb { @@ -145,7 +147,9 @@ impl Pure<'_> { } } - let rt = cond.alt.get_type(self.expr_ctx); + let rt = cond + .alt + .get_type(self.expr_ctx.unresolved_ctxt, self.expr_ctx.remaining_depth); if let Value::Known(Type::Bool) = rt { let rb = cond.alt.as_pure_bool(self.expr_ctx); if let Value::Known(false) = rb { @@ -233,7 +237,10 @@ impl Pure<'_> { Expr::Lit(Lit::Num(Number { value: 0.0, .. })), ) if *value > 0.0 && (!cond.test.is_bin() - || cond.test.get_type(self.expr_ctx) == Value::Known(Type::Bool)) => + || cond.test.get_type( + self.expr_ctx.unresolved_ctxt, + self.expr_ctx.remaining_depth, + ) == Value::Known(Type::Bool)) => { report_change!("conditionals: `foo ? num : 0` => `num * !!foo`"); self.changed = true; @@ -292,8 +299,12 @@ impl Pure<'_> { return; } - let lt = bin.left.get_type(self.expr_ctx); - let rt = bin.right.get_type(self.expr_ctx); + let lt = bin + .left + .get_type(self.expr_ctx.unresolved_ctxt, self.expr_ctx.remaining_depth); + let rt = bin + .right + .get_type(self.expr_ctx.unresolved_ctxt, self.expr_ctx.remaining_depth); let _lb = bin.left.as_pure_bool(self.expr_ctx); let rb = bin.right.as_pure_bool(self.expr_ctx); diff --git a/crates/swc_ecma_minifier/src/compress/pure/evaluate.rs b/crates/swc_ecma_minifier/src/compress/pure/evaluate.rs index afb961fe2203..42a201794d86 100644 --- a/crates/swc_ecma_minifier/src/compress/pure/evaluate.rs +++ b/crates/swc_ecma_minifier/src/compress/pure/evaluate.rs @@ -35,8 +35,12 @@ impl Pure<'_> { ) }; match ( - n.left.get_type(self.expr_ctx).opt()?, - n.right.get_type(self.expr_ctx).opt()?, + n.left + .get_type(self.expr_ctx.unresolved_ctxt, self.expr_ctx.remaining_depth) + .opt()?, + n.right + .get_type(self.expr_ctx.unresolved_ctxt, self.expr_ctx.remaining_depth) + .opt()?, ) { // Abort if types differ, or one of them is unknown. (lt, rt) if lt != rt => {} diff --git a/crates/swc_ecma_minifier/src/compress/pure/misc.rs b/crates/swc_ecma_minifier/src/compress/pure/misc.rs index 7ca4e41256bc..cd5bb62762c9 100644 --- a/crates/swc_ecma_minifier/src/compress/pure/misc.rs +++ b/crates/swc_ecma_minifier/src/compress/pure/misc.rs @@ -1134,7 +1134,11 @@ impl Pure<'_> { callee: Callee::Expr(callee), args, .. - }) if callee.is_one_of_global_ref_to(self.expr_ctx, &["Array", "Object", "RegExp"]) => { + }) if callee.is_one_of_global_ref_to( + self.expr_ctx.unresolved_ctxt, + &["Array", "Object", "RegExp"], + ) => + { let new_expr = match &**callee { Expr::Ident(Ident { sym, .. }) if &**sym == "RegExp" => { self.optimize_regex(args, span) @@ -1163,7 +1167,7 @@ impl Pure<'_> { args, .. }) if callee.is_one_of_global_ref_to( - self.expr_ctx, + self.expr_ctx.unresolved_ctxt, &["Boolean", "Number", "String", "Symbol"], ) => { @@ -1273,7 +1277,7 @@ impl Pure<'_> { args, .. }) if callee.is_one_of_global_ref_to( - self.expr_ctx, + self.expr_ctx.unresolved_ctxt, &[ "Object", // https://262.ecma-international.org/12.0/#sec-array-constructor @@ -1292,7 +1296,7 @@ impl Pure<'_> { "TypeError", "URIError", ], - ) || (callee.is_global_ref_to(self.expr_ctx, "RegExp") + ) || (callee.is_global_ref_to(self.expr_ctx.unresolved_ctxt, "RegExp") && can_compress_new_regexp(args.as_deref())) => { self.changed = true; @@ -1615,7 +1619,7 @@ impl Pure<'_> { callee: Callee::Expr(callee), args, .. - }) if callee.is_pure_callee(self.expr_ctx) => { + }) if callee.is_pure_callee(self.expr_ctx.unresolved_ctxt) => { report_change!("ignore_return_value: Dropping a pure call (callee is pure)"); self.changed = true; @@ -1644,7 +1648,9 @@ impl Pure<'_> { ctxt, args, .. - }) if callee.is_pure_callee(self.expr_ctx) || ctxt.has_mark(self.marks.pure) => { + }) if callee.is_pure_callee(self.expr_ctx.unresolved_ctxt) + || ctxt.has_mark(self.marks.pure) => + { report_change!("ignore_return_value: Dropping a pure call"); self.changed = true; @@ -1667,7 +1673,7 @@ impl Pure<'_> { args, .. }) => { - if callee.is_pure_callee(self.expr_ctx) { + if callee.is_pure_callee(self.expr_ctx.unresolved_ctxt) { self.changed = true; report_change!("Dropping pure call as callee is pure"); *e = self @@ -1683,7 +1689,7 @@ impl Pure<'_> { tpl, .. }) => { - if callee.is_pure_callee(self.expr_ctx) { + if callee.is_pure_callee(self.expr_ctx.unresolved_ctxt) { self.changed = true; report_change!("Dropping pure tag tpl as callee is pure"); *e = self @@ -2157,7 +2163,7 @@ impl Pure<'_> { Expr::New(NewExpr { span, callee, args, .. }) if callee.is_one_of_global_ref_to( - self.expr_ctx, + self.expr_ctx.unresolved_ctxt, &[ "Map", "Set", "Array", "Object", "Boolean", "Number", "String", ], @@ -2181,7 +2187,7 @@ impl Pure<'_> { args, .. }) if callee.is_one_of_global_ref_to( - self.expr_ctx, + self.expr_ctx.unresolved_ctxt, &["Array", "Object", "Boolean", "Number"], ) => { @@ -2422,8 +2428,12 @@ impl Pure<'_> { _ => return, }; - let lt = cond.cons.get_type(self.expr_ctx); - let rt = cond.alt.get_type(self.expr_ctx); + let lt = cond + .cons + .get_type(self.expr_ctx.unresolved_ctxt, self.expr_ctx.remaining_depth); + let rt = cond + .alt + .get_type(self.expr_ctx.unresolved_ctxt, self.expr_ctx.remaining_depth); match (lt, rt) { (Known(Type::Bool), Known(Type::Bool)) => {} _ => return, diff --git a/crates/swc_ecma_minifier/src/compress/pure/strings.rs b/crates/swc_ecma_minifier/src/compress/pure/strings.rs index 9e3be0025c99..e7e231440ffc 100644 --- a/crates/swc_ecma_minifier/src/compress/pure/strings.rs +++ b/crates/swc_ecma_minifier/src/compress/pure/strings.rs @@ -28,11 +28,11 @@ impl Pure<'_> { _ => return, }; - match l_l.get_type(self.expr_ctx) { + match l_l.get_type(self.expr_ctx.unresolved_ctxt, self.expr_ctx.remaining_depth) { Known(Type::Str) => {} _ => return, } - match r_l.get_type(self.expr_ctx) { + match r_l.get_type(self.expr_ctx.unresolved_ctxt, self.expr_ctx.remaining_depth) { Known(Type::Str) => {} _ => return, } @@ -487,8 +487,12 @@ impl Pure<'_> { }, ) = &mut *bin.left { - let type_of_second = left.right.get_type(self.expr_ctx); - let type_of_third = bin.right.get_type(self.expr_ctx); + let type_of_second = left + .right + .get_type(self.expr_ctx.unresolved_ctxt, self.expr_ctx.remaining_depth); + let type_of_third = bin + .right + .get_type(self.expr_ctx.unresolved_ctxt, self.expr_ctx.remaining_depth); if let Value::Known(Type::Str) = type_of_second { if let Value::Known(Type::Str) = type_of_third { @@ -534,8 +538,8 @@ impl Pure<'_> { .. }) = e { - let lt = left.get_type(self.expr_ctx); - let rt = right.get_type(self.expr_ctx); + let lt = left.get_type(self.expr_ctx.unresolved_ctxt, self.expr_ctx.remaining_depth); + let rt = right.get_type(self.expr_ctx.unresolved_ctxt, self.expr_ctx.remaining_depth); if let Value::Known(Type::Str) = lt { if let Value::Known(Type::Str) = rt { match &**left { diff --git a/crates/swc_ecma_minifier/src/compress/util/mod.rs b/crates/swc_ecma_minifier/src/compress/util/mod.rs index 21ae0c15efa4..2ab6f07e9210 100644 --- a/crates/swc_ecma_minifier/src/compress/util/mod.rs +++ b/crates/swc_ecma_minifier/src/compress/util/mod.rs @@ -370,7 +370,7 @@ pub(crate) fn is_pure_undefined(expr_ctx: ExprCtx, e: &Expr) -> bool { .. }) if !arg.may_have_side_effects(expr_ctx) => true, - _ => e.is_undefined(expr_ctx), + _ => e.is_undefined(expr_ctx.unresolved_ctxt), } } @@ -419,7 +419,7 @@ pub(crate) fn eval_to_undefined(expr_ctx: ExprCtx, e: &Expr) -> bool { eval_to_undefined(expr_ctx, &c.cons) && eval_to_undefined(expr_ctx, &c.alt) } - _ => e.is_undefined(expr_ctx), + _ => e.is_undefined(expr_ctx.unresolved_ctxt), } } @@ -787,7 +787,10 @@ pub(crate) fn can_absorb_negate(e: &Expr, expr_ctx: ExprCtx) -> bool { Expr::Bin(BinExpr { op, .. }) if is_eq(*op) => true, Expr::Unary(UnaryExpr { op: op!("!"), arg, .. - }) => arg.get_type(expr_ctx) == Value::Known(Type::Bool), + }) => { + arg.get_type(expr_ctx.unresolved_ctxt, expr_ctx.remaining_depth) + == Value::Known(Type::Bool) + } _ => false, } } diff --git a/crates/swc_ecma_minifier/src/eval.rs b/crates/swc_ecma_minifier/src/eval.rs index ab89d366cd52..025c7f12abcf 100644 --- a/crates/swc_ecma_minifier/src/eval.rs +++ b/crates/swc_ecma_minifier/src/eval.rs @@ -125,7 +125,7 @@ impl Evaluator { obj: tag_obj, prop: MemberProp::Ident(prop), .. - }) if tag_obj.is_global_ref_to(self.expr_ctx, "String") + }) if tag_obj.is_global_ref_to(self.expr_ctx.unresolved_ctxt, "String") && prop.sym == *"raw" => { return self.eval_tpl(&t.tpl); diff --git a/crates/swc_ecma_transforms_optimization/src/simplify/branch/mod.rs b/crates/swc_ecma_transforms_optimization/src/simplify/branch/mod.rs index cfbd5697bf85..1729315db305 100644 --- a/crates/swc_ecma_transforms_optimization/src/simplify/branch/mod.rs +++ b/crates/swc_ecma_transforms_optimization/src/simplify/branch/mod.rs @@ -145,13 +145,13 @@ impl VisitMut for Remover { Expr::Cond(cond) if !cond.test.may_have_side_effects(self.expr_ctx) - && (cond.cons.is_undefined(self.expr_ctx) + && (cond.cons.is_undefined(self.expr_ctx.unresolved_ctxt) || matches!(*cond.cons, Expr::Unary(UnaryExpr { op: op!("void"), ref arg, .. }) if !arg.may_have_side_effects(self.expr_ctx))) - && (cond.alt.is_undefined(self.expr_ctx) + && (cond.alt.is_undefined(self.expr_ctx.unresolved_ctxt) || matches!(*cond.alt, Expr::Unary(UnaryExpr { op: op!("void"), ref arg, @@ -281,7 +281,7 @@ impl VisitMut for Remover { span, key, value: Some(expr), - }) if expr.is_undefined(self.expr_ctx) + }) if expr.is_undefined(self.expr_ctx.unresolved_ctxt) || match **expr { Expr::Unary(UnaryExpr { op: op!("void"), @@ -323,7 +323,7 @@ impl VisitMut for Remover { match p { Pat::Assign(assign) - if assign.right.is_undefined(self.expr_ctx) + if assign.right.is_undefined(self.expr_ctx.unresolved_ctxt) || match *assign.right { Expr::Unary(UnaryExpr { op: op!("void"), @@ -655,7 +655,13 @@ impl VisitMut for Remover { Expr::Lit(Lit::Str(..)) | Expr::Lit(Lit::Null(..)) | Expr::Lit(Lit::Num(..)) => true, - ref e if e.is_nan() || e.is_global_ref_to(self.expr_ctx, "undefined") => { + ref e + if e.is_nan() + || e.is_global_ref_to( + self.expr_ctx.unresolved_ctxt, + "undefined", + ) => + { true } _ => false, @@ -725,7 +731,10 @@ impl VisitMut for Remover { _ => { if !test.is_nan() - && !test.is_global_ref_to(self.expr_ctx, "undefined") + && !test.is_global_ref_to( + self.expr_ctx.unresolved_ctxt, + "undefined", + ) { non_constant_case_idx = Some(i); } @@ -1637,7 +1646,7 @@ fn ignore_result(e: Box, drop_str_lit: bool, ctx: ExprCtx) -> Option ignore_result( + }) if callee.is_pure_callee(ctx.unresolved_ctxt) => ignore_result( ArrayLit { span, elems: args @@ -1654,7 +1663,7 @@ fn ignore_result(e: Box, drop_str_lit: bool, ctx: ExprCtx) -> Option ignore_result( + }) if callee.is_pure_callee(ctx.unresolved_ctxt) => ignore_result( ArrayLit { span, elems: args.into_iter().map(Some).collect(), @@ -1670,7 +1679,9 @@ fn ignore_result(e: Box, drop_str_lit: bool, ctx: ExprCtx) -> Option { + Expr::TaggedTpl(TaggedTpl { span, tag, tpl, .. }) + if tag.is_pure_callee(ctx.unresolved_ctxt) => + { ignore_result( ctx.preserve_effects(span, Expr::undefined(span), tpl.exprs), true, diff --git a/crates/swc_ecma_transforms_optimization/src/simplify/expr/mod.rs b/crates/swc_ecma_transforms_optimization/src/simplify/expr/mod.rs index fff50a77de56..99eeb5f74afb 100644 --- a/crates/swc_ecma_transforms_optimization/src/simplify/expr/mod.rs +++ b/crates/swc_ecma_transforms_optimization/src/simplify/expr/mod.rs @@ -420,7 +420,7 @@ impl VisitMut for SimplifyExpr { self.in_callee = old_in_callee; if let Pat::Assign(a) = p { - if a.right.is_undefined(self.expr_ctx) + if a.right.is_undefined(self.expr_ctx.unresolved_ctxt) || match *a.right { Expr::Unary(UnaryExpr { op: op!("void"), @@ -1026,7 +1026,7 @@ pub fn optimize_bin_expr(expr_ctx: ExprCtx, expr: &mut Expr, changed: &mut bool) } } - match expr.get_type(expr_ctx) { + match expr.get_type(expr_ctx.unresolved_ctxt, expr_ctx.remaining_depth) { // String concatenation Known(StringType) => match expr { Expr::Bin(BinExpr { @@ -1185,7 +1185,7 @@ pub fn optimize_bin_expr(expr_ctx: ExprCtx, expr: &mut Expr, changed: &mut bool) return; } - if is_obj(left) && right.is_global_ref_to(expr_ctx, "Object") { + if is_obj(left) && right.is_global_ref_to(expr_ctx.unresolved_ctxt, "Object") { *changed = true; *expr = *make_bool_expr(expr_ctx, *span, true, iter::once(left.take())); @@ -1474,8 +1474,12 @@ fn perform_arithmetic_op(expr_ctx: ExprCtx, op: BinaryOp, left: &Expr, right: &E if (lv.is_unknown() && rv.is_unknown()) || op == op!(bin, "+") - && (!left.get_type(expr_ctx).casted_to_number_on_add() - || !right.get_type(expr_ctx).casted_to_number_on_add()) + && (!left + .get_type(expr_ctx.unresolved_ctxt, expr_ctx.remaining_depth) + .casted_to_number_on_add() + || !right + .get_type(expr_ctx.unresolved_ctxt, expr_ctx.remaining_depth) + .casted_to_number_on_add()) { return Unknown; } @@ -1630,7 +1634,10 @@ fn perform_abstract_rel_cmp( } // Try to evaluate based on the general type. - let (lt, rt) = (left.get_type(expr_ctx), right.get_type(expr_ctx)); + let (lt, rt) = ( + left.get_type(expr_ctx.unresolved_ctxt, expr_ctx.remaining_depth), + right.get_type(expr_ctx.unresolved_ctxt, expr_ctx.remaining_depth), + ); if let (Known(StringType), Known(StringType)) = (lt, rt) { if let (Known(lv), Known(rv)) = ( @@ -1668,8 +1675,8 @@ fn perform_abstract_eq_cmp( right: &Expr, ) -> Value { let (lt, rt) = ( - try_val!(left.get_type(expr_ctx)), - try_val!(right.get_type(expr_ctx)), + try_val!(left.get_type(expr_ctx.unresolved_ctxt, expr_ctx.remaining_depth)), + try_val!(right.get_type(expr_ctx.unresolved_ctxt, expr_ctx.remaining_depth)), ); if lt == rt { @@ -1745,8 +1752,8 @@ fn perform_strict_eq_cmp(expr_ctx: ExprCtx, left: &Expr, right: &Expr) -> Value< } let (lt, rt) = ( - try_val!(left.get_type(expr_ctx)), - try_val!(right.get_type(expr_ctx)), + try_val!(left.get_type(expr_ctx.unresolved_ctxt, expr_ctx.remaining_depth)), + try_val!(right.get_type(expr_ctx.unresolved_ctxt, expr_ctx.remaining_depth)), ); // Strict equality can only be true for values of the same type. if lt != rt { diff --git a/crates/swc_ecma_usage_analyzer/src/analyzer/mod.rs b/crates/swc_ecma_usage_analyzer/src/analyzer/mod.rs index ded8e1479ed0..d9b2427800d4 100644 --- a/crates/swc_ecma_usage_analyzer/src/analyzer/mod.rs +++ b/crates/swc_ecma_usage_analyzer/src/analyzer/mod.rs @@ -2,9 +2,7 @@ use ctx::BitContext; use rustc_hash::FxHashMap; use swc_common::SyntaxContext; use swc_ecma_ast::*; -use swc_ecma_utils::{ - find_pat_ids, ident::IdentLike, ExprCtx, ExprExt, IsEmpty, StmtExt, Type, Value, -}; +use swc_ecma_utils::{find_pat_ids, ExprCtx, ExprExt, IsEmpty, StmtExt, Type, Value}; use swc_ecma_visit::{noop_visit_type, Visit, VisitWith}; use swc_timer::timer; @@ -159,7 +157,7 @@ where self.data.report_usage(self.ctx, i.clone()); self.data.var_or_default(i.clone()).mark_used_above_decl() } - self.data.var_or_default(i.clone()).mark_used_recursively(); + self.data.var_or_default(i).mark_used_recursively(); return; } @@ -285,7 +283,8 @@ where self.ctx, id, is_op_assign, - n.right.get_type(self.expr_ctx), + n.right + .get_type(self.expr_ctx.unresolved_ctxt, self.expr_ctx.remaining_depth), ) } } @@ -293,7 +292,8 @@ where self.report_assign_expr_if_ident( e.as_ident().map(Ident::from).as_ref(), is_op_assign, - n.right.get_type(self.expr_ctx), + n.right + .get_type(self.expr_ctx.unresolved_ctxt, self.expr_ctx.remaining_depth), ); self.mark_mutation_if_member(e.as_member()) } @@ -301,7 +301,7 @@ where if n.op == op!("=") { let left = match &n.left { - AssignTarget::Simple(left) => left.leftmost().map(Ident::to_id), + AssignTarget::Simple(left) => left.leftmost(), AssignTarget::Pat(..) => None, }; @@ -319,7 +319,7 @@ where v = Some(self.data.var_or_default(left.to_id())); } - v.as_mut().unwrap().add_infects_to(id.clone()); + v.as_mut().unwrap().add_infects_to(id); } } } @@ -472,7 +472,7 @@ where n.args.visit_with(&mut *self.with_ctx(ctx)); let call_may_mutate = match &n.callee { - Callee::Expr(e) => call_may_mutate(e, self.expr_ctx), + Callee::Expr(e) => call_may_mutate(e, self.expr_ctx.unresolved_ctxt), _ => true, }; @@ -800,7 +800,7 @@ where v = Some(self.data.var_or_default(n.ident.to_id())); } - v.as_mut().unwrap().add_infects_to(id.clone()); + v.as_mut().unwrap().add_infects_to(id); } } } @@ -1098,7 +1098,7 @@ where let ctx = self.ctx.with(BitContext::IsIdRef, true); n.args.visit_with(&mut *self.with_ctx(ctx)); - if call_may_mutate(&n.callee, self.expr_ctx) { + if call_may_mutate(&n.callee, self.expr_ctx.unresolved_ctxt) { if let Some(args) = &n.args { for a in args { for_each_id_ref_in_expr(&a.expr, &mut |id| { @@ -1390,7 +1390,7 @@ where v = Some(self.data.var_or_default(var.to_id())); } - v.as_mut().unwrap().add_infects_to(id.clone()); + v.as_mut().unwrap().add_infects_to(id); } } } @@ -1422,10 +1422,9 @@ where .map(is_safe_to_access_prop) .unwrap_or(false), ), - in_pat_of_var_decl_with_init: e - .init - .as_ref() - .map(|init| init.get_type(self.expr_ctx)), + in_pat_of_var_decl_with_init: e.init.as_ref().map(|init| { + init.get_type(self.expr_ctx.unresolved_ctxt, self.expr_ctx.remaining_depth) + }), ..self.ctx }; e.name.visit_with(&mut *self.with_ctx(ctx)); @@ -1683,7 +1682,7 @@ fn is_safe_to_access_prop(e: &Expr) -> bool { } } -fn call_may_mutate(expr: &Expr, expr_ctx: ExprCtx) -> bool { +fn call_may_mutate(expr: &Expr, unresolved: SyntaxContext) -> bool { fn is_global_fn_wont_mutate(s: &Ident, unresolved: SyntaxContext) -> bool { s.ctxt == unresolved && matches!( @@ -1731,13 +1730,13 @@ fn call_may_mutate(expr: &Expr, expr_ctx: ExprCtx) -> bool { ) } - if expr.is_pure_callee(expr_ctx) { + if expr.is_pure_callee(unresolved) { false } else { match expr { - Expr::Ident(i) if is_global_fn_wont_mutate(i, expr_ctx.unresolved_ctxt) => false, + Expr::Ident(i) if is_global_fn_wont_mutate(i, unresolved) => false, Expr::Member(MemberExpr { obj, .. }) => { - !matches!(&**obj, Expr::Ident(i) if is_global_fn_wont_mutate(i, expr_ctx.unresolved_ctxt)) + !matches!(&**obj, Expr::Ident(i) if is_global_fn_wont_mutate(i, unresolved)) } _ => true, } diff --git a/crates/swc_ecma_utils/src/lib.rs b/crates/swc_ecma_utils/src/lib.rs index 2771b746eb82..2a5395ccb67e 100644 --- a/crates/swc_ecma_utils/src/lib.rs +++ b/crates/swc_ecma_utils/src/lib.rs @@ -565,63 +565,59 @@ pub trait StmtExt { } fn may_have_side_effects(&self, ctx: ExprCtx) -> bool { - fn may_have_side_effects(stmt: &Stmt, ctx: ExprCtx) -> bool { - match stmt { - Stmt::Block(block_stmt) => block_stmt + match self.as_stmt() { + Stmt::Block(block_stmt) => block_stmt + .stmts + .iter() + .any(|stmt| stmt.may_have_side_effects(ctx)), + Stmt::Empty(_) => false, + Stmt::Labeled(labeled_stmt) => labeled_stmt.body.may_have_side_effects(ctx), + Stmt::If(if_stmt) => { + if_stmt.test.may_have_side_effects(ctx) + || if_stmt.cons.may_have_side_effects(ctx) + || if_stmt + .alt + .as_ref() + .is_some_and(|stmt| stmt.may_have_side_effects(ctx)) + } + Stmt::Switch(switch_stmt) => { + switch_stmt.discriminant.may_have_side_effects(ctx) + || switch_stmt.cases.iter().any(|case| { + case.test + .as_ref() + .is_some_and(|expr| expr.may_have_side_effects(ctx)) + || case.cons.iter().any(|con| con.may_have_side_effects(ctx)) + }) + } + Stmt::Try(try_stmt) => { + try_stmt + .block .stmts .iter() - .any(|stmt| stmt.may_have_side_effects(ctx)), - Stmt::Empty(_) => false, - Stmt::Labeled(labeled_stmt) => labeled_stmt.body.may_have_side_effects(ctx), - Stmt::If(if_stmt) => { - if_stmt.test.may_have_side_effects(ctx) - || if_stmt.cons.may_have_side_effects(ctx) - || if_stmt - .alt - .as_ref() - .is_some_and(|stmt| stmt.may_have_side_effects(ctx)) - } - Stmt::Switch(switch_stmt) => { - switch_stmt.discriminant.may_have_side_effects(ctx) - || switch_stmt.cases.iter().any(|case| { - case.test - .as_ref() - .is_some_and(|expr| expr.may_have_side_effects(ctx)) - || case.cons.iter().any(|con| con.may_have_side_effects(ctx)) - }) - } - Stmt::Try(try_stmt) => { - try_stmt - .block - .stmts - .iter() - .any(|stmt| stmt.may_have_side_effects(ctx)) - || try_stmt.handler.as_ref().is_some_and(|handler| { - handler - .body - .stmts - .iter() - .any(|stmt| stmt.may_have_side_effects(ctx)) - }) - || try_stmt.finalizer.as_ref().is_some_and(|finalizer| { - finalizer - .stmts - .iter() - .any(|stmt| stmt.may_have_side_effects(ctx)) - }) - } - Stmt::Decl(decl) => match decl { - Decl::Class(class_decl) => class_has_side_effect(ctx, &class_decl.class), - Decl::Fn(_) => !ctx.in_strict, - Decl::Var(var_decl) => var_decl.kind == VarDeclKind::Var, - _ => false, - }, - Stmt::Expr(expr_stmt) => expr_stmt.expr.may_have_side_effects(ctx), - _ => true, + .any(|stmt| stmt.may_have_side_effects(ctx)) + || try_stmt.handler.as_ref().is_some_and(|handler| { + handler + .body + .stmts + .iter() + .any(|stmt| stmt.may_have_side_effects(ctx)) + }) + || try_stmt.finalizer.as_ref().is_some_and(|finalizer| { + finalizer + .stmts + .iter() + .any(|stmt| stmt.may_have_side_effects(ctx)) + }) } + Stmt::Decl(decl) => match decl { + Decl::Class(class_decl) => class_has_side_effect(ctx, &class_decl.class), + Decl::Fn(_) => !ctx.in_strict, + Decl::Var(var_decl) => var_decl.kind == VarDeclKind::Var, + _ => false, + }, + Stmt::Expr(expr_stmt) => expr_stmt.expr.may_have_side_effects(ctx), + _ => true, } - - may_have_side_effects(self.as_stmt(), ctx) } } @@ -742,8 +738,8 @@ pub trait ExprExt { } #[inline(always)] - fn is_undefined(&self, ctx: ExprCtx) -> bool { - is_undefined(self.as_expr(), ctx) + fn is_undefined(&self, unresolved_ctxt: SyntaxContext) -> bool { + is_undefined(self.as_expr(), unresolved_ctxt) } #[inline(always)] @@ -753,14 +749,14 @@ pub trait ExprExt { /// Returns `true` if `id` references a global object. #[inline(always)] - fn is_global_ref_to(&self, ctx: ExprCtx, id: &str) -> bool { - is_global_ref_to(self.as_expr(), ctx, id) + fn is_global_ref_to(&self, unresolved_ctxt: SyntaxContext, id: &str) -> bool { + is_global_ref_to(self.as_expr(), unresolved_ctxt, id) } /// Returns `true` if `id` references a global object. #[inline(always)] - fn is_one_of_global_ref_to(&self, ctx: ExprCtx, ids: &[&str]) -> bool { - is_one_of_global_ref_to(self.as_expr(), ctx, ids) + fn is_one_of_global_ref_to(&self, unresolved_ctxt: SyntaxContext, ids: &[&str]) -> bool { + is_one_of_global_ref_to(self.as_expr(), unresolved_ctxt, ids) } #[inline(always)] @@ -805,13 +801,13 @@ pub trait ExprExt { /// Apply the supplied predicate against all possible result Nodes of the /// expression. #[inline(always)] - fn get_type(&self, ctx: ExprCtx) -> Value { - get_type(self.as_expr(), ctx) + fn get_type(&self, unresolved_ctxt: SyntaxContext, remaining_depth: u8) -> Value { + get_type(self.as_expr(), unresolved_ctxt, remaining_depth) } #[inline(always)] - fn is_pure_callee(&self, ctx: ExprCtx) -> bool { - is_pure_callee(self.as_expr(), ctx) + fn is_pure_callee(&self, unresolved_ctxt: SyntaxContext) -> bool { + is_pure_callee(self.as_expr(), unresolved_ctxt) } #[inline(always)] @@ -1795,17 +1791,6 @@ impl<'a> IdentUsageFinder<'a> { } impl ExprCtx { - pub fn consume_depth(self) -> Option { - if self.remaining_depth == 0 { - return None; - } - - Some(Self { - remaining_depth: self.remaining_depth - 1, - ..self - }) - } - /// make a new expression which evaluates `val` preserving side effects, if /// any. pub fn preserve_effects(self, span: Span, val: Box, exprs: I) -> Box @@ -2702,8 +2687,8 @@ fn is_nan(expr: &Expr) -> bool { expr.is_ident_ref_to("NaN") } -fn is_undefined(expr: &Expr, ctx: ExprCtx) -> bool { - expr.is_global_ref_to(ctx, "undefined") +fn is_undefined(expr: &Expr, unresolved_ctxt: SyntaxContext) -> bool { + expr.is_global_ref_to(unresolved_ctxt, "undefined") } fn is_void(expr: &Expr) -> bool { @@ -2716,16 +2701,16 @@ fn is_void(expr: &Expr) -> bool { ) } -fn is_global_ref_to(expr: &Expr, ctx: ExprCtx, id: &str) -> bool { +fn is_global_ref_to(expr: &Expr, unresolved_ctxt: SyntaxContext, id: &str) -> bool { match expr { - Expr::Ident(i) => i.ctxt == ctx.unresolved_ctxt && &*i.sym == id, + Expr::Ident(i) => i.ctxt == unresolved_ctxt && &*i.sym == id, _ => false, } } -fn is_one_of_global_ref_to(expr: &Expr, ctx: ExprCtx, ids: &[&str]) -> bool { +fn is_one_of_global_ref_to(expr: &Expr, unresolved_ctxt: SyntaxContext, ids: &[&str]) -> bool { match expr { - Expr::Ident(i) => i.ctxt == ctx.unresolved_ctxt && ids.contains(&&*i.sym), + Expr::Ident(i) => i.ctxt == unresolved_ctxt && ids.contains(&&*i.sym), _ => false, } } @@ -2737,18 +2722,20 @@ fn as_pure_bool(expr: &Expr, ctx: ExprCtx) -> BoolValue { } } -fn cast_to_bool(expr: &Expr, ctx: ExprCtx) -> (Purity, BoolValue) { - let Some(ctx) = ctx.consume_depth() else { +fn cast_to_bool(expr: &Expr, mut ctx: ExprCtx) -> (Purity, BoolValue) { + if ctx.remaining_depth == 0 { return (MayBeImpure, Unknown); - }; + } - if expr.is_global_ref_to(ctx, "undefined") { + if expr.is_global_ref_to(ctx.unresolved_ctxt, "undefined") { return (Pure, Known(false)); } if expr.is_nan() { return (Pure, Known(false)); } + ctx.remaining_depth -= 1; + let val = match expr { Expr::Paren(ref e) => return e.expr.cast_to_bool(ctx), @@ -2846,7 +2833,9 @@ fn cast_to_bool(expr: &Expr, ctx: ExprCtx) -> (Purity, BoolValue) { ref right, .. }) => { - if left.get_type(ctx) != Known(BoolType) || right.get_type(ctx) != Known(BoolType) { + if left.get_type(ctx.unresolved_ctxt, ctx.remaining_depth) != Known(BoolType) + || right.get_type(ctx.unresolved_ctxt, ctx.remaining_depth) != Known(BoolType) + { return (MayBeImpure, Unknown); } @@ -2964,10 +2953,11 @@ fn cast_to_bool(expr: &Expr, ctx: ExprCtx) -> (Purity, BoolValue) { } } -fn cast_to_number(expr: &Expr, ctx: ExprCtx) -> (Purity, Value) { - let Some(ctx) = ctx.consume_depth() else { +fn cast_to_number(expr: &Expr, mut ctx: ExprCtx) -> (Purity, Value) { + if ctx.remaining_depth == 0 { return (MayBeImpure, Unknown); - }; + } + ctx.remaining_depth -= 1; let v = match expr { Expr::Lit(l) => match l { @@ -3059,10 +3049,11 @@ fn as_pure_number(expr: &Expr, ctx: ExprCtx) -> Value { v } -fn as_pure_string(expr: &Expr, ctx: ExprCtx) -> Value> { - let Some(ctx) = ctx.consume_depth() else { +fn as_pure_string(expr: &Expr, mut ctx: ExprCtx) -> Value> { + if ctx.remaining_depth == 0 { return Unknown; - }; + } + ctx.remaining_depth -= 1; match *expr { Expr::Lit(ref l) => match *l { @@ -3155,17 +3146,18 @@ fn as_pure_string(expr: &Expr, ctx: ExprCtx) -> Value> { } } -fn get_type(expr: &Expr, ctx: ExprCtx) -> Value { - let Some(ctx) = ctx.consume_depth() else { +fn get_type(expr: &Expr, unresolved_ctxt: SyntaxContext, remaining_depth: u8) -> Value { + if remaining_depth == 0 { return Unknown; - }; + } + let remaining_depth = remaining_depth - 1; match expr { Expr::Assign(AssignExpr { ref right, op: op!("="), .. - }) => right.get_type(ctx), + }) => right.get_type(unresolved_ctxt, remaining_depth), Expr::Member(MemberExpr { obj, @@ -3182,7 +3174,7 @@ fn get_type(expr: &Expr, ctx: ExprCtx) -> Value { Expr::Seq(SeqExpr { ref exprs, .. }) => exprs .last() .expect("sequence expression should not be empty") - .get_type(ctx), + .get_type(unresolved_ctxt, remaining_depth), Expr::Bin(BinExpr { ref left, @@ -3200,7 +3192,10 @@ fn get_type(expr: &Expr, ctx: ExprCtx) -> Value { cons: ref left, alt: ref right, .. - }) => and(left.get_type(ctx), right.get_type(ctx)), + }) => and( + left.get_type(unresolved_ctxt, remaining_depth), + right.get_type(unresolved_ctxt, remaining_depth), + ), Expr::Bin(BinExpr { ref left, @@ -3208,12 +3203,12 @@ fn get_type(expr: &Expr, ctx: ExprCtx) -> Value { ref right, .. }) => { - let rt = right.get_type(ctx); + let rt = right.get_type(unresolved_ctxt, remaining_depth); if rt == Known(StringType) { return Known(StringType); } - let lt = left.get_type(ctx); + let lt = left.get_type(unresolved_ctxt, remaining_depth); if lt == Known(StringType) { return Known(StringType); } @@ -3242,7 +3237,7 @@ fn get_type(expr: &Expr, ctx: ExprCtx) -> Value { ref right, .. }) => { - if right.get_type(ctx) == Known(StringType) { + if right.get_type(unresolved_ctxt, remaining_depth) == Known(StringType) { return Known(StringType); } Unknown @@ -3335,8 +3330,8 @@ fn get_type(expr: &Expr, ctx: ExprCtx) -> Value { } } -fn is_pure_callee(expr: &Expr, ctx: ExprCtx) -> bool { - if expr.is_global_ref_to(ctx, "Date") { +fn is_pure_callee(expr: &Expr, unresolved_ctxt: SyntaxContext) -> bool { + if expr.is_global_ref_to(unresolved_ctxt, "Date") { return true; } @@ -3374,7 +3369,7 @@ fn is_pure_callee(expr: &Expr, ctx: ExprCtx) -> bool { ) } - obj.is_global_ref_to(ctx, "Math") + obj.is_global_ref_to(unresolved_ctxt, "Math") || match &**obj { // Allow dummy span Expr::Ident(Ident { @@ -3402,15 +3397,14 @@ fn is_pure_callee(expr: &Expr, ctx: ExprCtx) -> bool { } } -fn may_have_side_effects(expr: &Expr, ctx: ExprCtx) -> bool { - let Some(ctx) = ctx.consume_depth() else { +fn may_have_side_effects(expr: &Expr, mut ctx: ExprCtx) -> bool { + if ctx.remaining_depth == 0 { return true; - }; - - if expr.is_pure_callee(ctx) { + } + if expr.is_pure_callee(ctx.unresolved_ctxt) { return false; } - + ctx.remaining_depth -= 1; match expr { Expr::Ident(i) => { if ctx.is_unresolved_ref_safe { @@ -3543,7 +3537,7 @@ fn may_have_side_effects(expr: &Expr, ctx: ExprCtx) -> bool { callee: Callee::Expr(callee), ref args, .. - }) if callee.is_pure_callee(ctx) => { + }) if callee.is_pure_callee(ctx.unresolved_ctxt) => { args.iter().any(|arg| arg.expr.may_have_side_effects(ctx)) } Expr::OptChain(OptChainExpr { base, .. }) @@ -3551,7 +3545,7 @@ fn may_have_side_effects(expr: &Expr, ctx: ExprCtx) -> bool { && OptChainBase::as_call(base) .unwrap() .callee - .is_pure_callee(ctx) => + .is_pure_callee(ctx.unresolved_ctxt) => { OptChainBase::as_call(base) .unwrap()