mirror of
https://github.com/swc-project/swc.git
synced 2024-12-29 00:23:10 +03:00
fix(es/minifier): Don't create direct eval
(#4634)
- `expr_simplifier`: Preserve `this`. - `ExprReplacer`: Inject `0` after inlining eval. - `MultiReplacer`: Inject `0` after inlining eval.
This commit is contained in:
parent
dca5bf8b2f
commit
56a227ad9d
@ -142,6 +142,11 @@ where
|
||||
&& !usage.reassigned()
|
||||
&& !usage.has_property_mutation))
|
||||
&& match &**init {
|
||||
Expr::Ident(Ident {
|
||||
sym: js_word!("eval"),
|
||||
..
|
||||
}) => false,
|
||||
|
||||
Expr::Lit(lit) => match lit {
|
||||
Lit::Str(s) => usage.ref_count == 1 || s.value.len() <= 3,
|
||||
Lit::Bool(_) | Lit::Null(_) | Lit::Num(_) | Lit::BigInt(_) => true,
|
||||
|
@ -1,7 +1,8 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
use swc_common::{Span, SyntaxContext};
|
||||
use swc_atoms::js_word;
|
||||
use swc_common::{Span, SyntaxContext, DUMMY_SP};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_utils::{ExprCtx, ExprExt};
|
||||
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
|
||||
@ -177,10 +178,21 @@ impl<'a> MultiReplacer<'a> {
|
||||
}
|
||||
|
||||
fn var(&mut self, i: &Id) -> Option<Box<Expr>> {
|
||||
if self.clone {
|
||||
self.vars.get(i).cloned()
|
||||
let e = if self.clone {
|
||||
self.vars.get(i).cloned()?
|
||||
} else {
|
||||
self.vars.remove(i)
|
||||
self.vars.remove(i)?
|
||||
};
|
||||
|
||||
match &*e {
|
||||
Expr::Ident(Ident {
|
||||
sym: js_word!("eval"),
|
||||
..
|
||||
}) => Some(Box::new(Expr::Seq(SeqExpr {
|
||||
span: DUMMY_SP,
|
||||
exprs: vec![0.into(), e],
|
||||
}))),
|
||||
_ => Some(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -285,6 +297,23 @@ pub(crate) struct ExprReplacer {
|
||||
to: Option<Box<Expr>>,
|
||||
}
|
||||
|
||||
impl ExprReplacer {
|
||||
fn take(&mut self) -> Option<Box<Expr>> {
|
||||
let e = self.to.take()?;
|
||||
|
||||
match &*e {
|
||||
Expr::Ident(Ident {
|
||||
sym: js_word!("eval"),
|
||||
..
|
||||
}) => Some(Box::new(Expr::Seq(SeqExpr {
|
||||
span: DUMMY_SP,
|
||||
exprs: vec![0.into(), e],
|
||||
}))),
|
||||
_ => Some(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitMut for ExprReplacer {
|
||||
noop_visit_mut_type!();
|
||||
|
||||
@ -293,7 +322,7 @@ impl VisitMut for ExprReplacer {
|
||||
|
||||
if let Expr::Ident(i) = e {
|
||||
if self.from.0 == i.sym && self.from.1 == i.span.ctxt {
|
||||
if let Some(new) = self.to.take() {
|
||||
if let Some(new) = self.take() {
|
||||
*e = *new;
|
||||
} else {
|
||||
unreachable!("`{}` is already taken", i)
|
||||
@ -307,7 +336,7 @@ impl VisitMut for ExprReplacer {
|
||||
|
||||
if let Prop::Shorthand(i) = p {
|
||||
if self.from.0 == i.sym && self.from.1 == i.span.ctxt {
|
||||
let value = if let Some(new) = self.to.take() {
|
||||
let value = if let Some(new) = self.take() {
|
||||
new
|
||||
} else {
|
||||
unreachable!("`{}` is already taken", i)
|
||||
|
@ -1,21 +1,12 @@
|
||||
asm/asm_mixed/input.js
|
||||
collapse_vars/collapse_vars_do_while/input.js
|
||||
collapse_vars/collapse_vars_if/input.js
|
||||
collapse_vars/collapse_vars_lvalues/input.js
|
||||
collapse_vars/collapse_vars_lvalues_drop_assign/input.js
|
||||
collapse_vars/collapse_vars_misc1/input.js
|
||||
collapse_vars/collapse_vars_short_circuited_conditions/input.js
|
||||
collapse_vars/collapse_vars_side_effects_2/input.js
|
||||
collapse_vars/collapse_vars_switch/input.js
|
||||
collapse_vars/collapse_vars_unary/input.js
|
||||
collapse_vars/issue_2436_11/input.js
|
||||
collapse_vars/issue_2497/input.js
|
||||
collapse_vars/switch_case_1/input.js
|
||||
collapse_vars/toplevel_single_reference/input.js
|
||||
collapse_vars/undeclared/input.js
|
||||
conditionals/ifs_5/input.js
|
||||
conditionals/issue_2535_1/input.js
|
||||
destructuring/destructuring_dont_evaluate_with_undefined_as_default_assignment/input.js
|
||||
drop_unused/keep_assign/input.js
|
||||
drop_unused/reassign_const/input.js
|
||||
drop_unused/var_catch_toplevel/input.js
|
||||
@ -24,38 +15,28 @@ expansions/avoid_spread_getset_object/input.js
|
||||
expansions/avoid_spread_hole/input.js
|
||||
functions/avoid_generating_duplicate_functions_compared_together_3/input.js
|
||||
harmony/array_literal_with_spread_4a/input.js
|
||||
harmony/classes_extending_classes_out_of_pure_iifes/input.js
|
||||
harmony/default_assign/input.js
|
||||
if_return/if_return_same_value/input.js
|
||||
if_return/if_var_return/input.js
|
||||
inline/dont_inline_funcs_into_default_param_2/input.js
|
||||
inline/inline_into_scope_conflict/input.js
|
||||
inline/inline_within_extends_1/input.js
|
||||
issue_1034/non_hoisted_function_after_return_2a/input.js
|
||||
issue_1034/non_hoisted_function_after_return_2b/input.js
|
||||
issue_1052/defun_else_if_return/input.js
|
||||
issue_1052/defun_if_return/input.js
|
||||
issue_1443/keep_fnames/input.js
|
||||
issue_1443/unsafe_undefined/input.js
|
||||
issue_281/collapse_vars_constants/input.js
|
||||
issue_281/inner_var_for_in_1/input.js
|
||||
issue_368/collapse/input.js
|
||||
issue_417/test_unexpected_crash/input.js
|
||||
issue_417/test_unexpected_crash_2/input.js
|
||||
issue_44/issue_44_valid_ast_1/input.js
|
||||
issue_44/issue_44_valid_ast_2/input.js
|
||||
issue_973/this_binding_collapse_vars/input.js
|
||||
issue_973/this_binding_conditionals/input.js
|
||||
nullish/conditional_to_nullish_coalescing_2/input.js
|
||||
parameters/destructuring_arguments_2/input.js
|
||||
properties/dot_properties/input.js
|
||||
properties/issue_2256/input.js
|
||||
properties/mangle_properties/input.js
|
||||
properties/mangle_undeclared_properties/input.js
|
||||
properties/methods_keep_quoted_false/input.js
|
||||
properties/methods_keep_quoted_from_dead_code/input.js
|
||||
properties/methods_keep_quoted_true/input.js
|
||||
properties/skip_undeclared_properties_by_default/input.js
|
||||
pure_funcs/unary/input.js
|
||||
pure_getters/issue_2265_3/input.js
|
||||
reduce_vars/variables_collision_in_immediately_invoked_func/input.js
|
||||
|
@ -2285,11 +2285,11 @@
|
||||
function(data, headers) {
|
||||
return (normalizeHeaderName(headers, "Accept"), normalizeHeaderName(headers, "Content-Type"), utils.isFormData(data) || utils.isArrayBuffer(data) || utils.isBuffer(data) || utils.isStream(data) || utils.isFile(data) || utils.isBlob(data)) ? data : utils.isArrayBufferView(data) ? data.buffer : utils.isURLSearchParams(data) ? (setContentTypeIfUnset(headers, "application/x-www-form-urlencoded;charset=utf-8"), data.toString()) : utils.isObject(data) || headers && "application/json" === headers["Content-Type"] ? (setContentTypeIfUnset(headers, "application/json"), function(rawValue, parser, encoder) {
|
||||
if (utils.isString(rawValue)) try {
|
||||
return JSON.parse(rawValue), utils.trim(rawValue);
|
||||
return (0, JSON.parse)(rawValue), utils.trim(rawValue);
|
||||
} catch (e) {
|
||||
if ("SyntaxError" !== e.name) throw e;
|
||||
}
|
||||
return JSON.stringify(rawValue);
|
||||
return (0, JSON.stringify)(rawValue);
|
||||
}(data)) : data;
|
||||
},
|
||||
],
|
||||
|
@ -840,6 +840,8 @@ issue_751/negate_booleans_2/input.js
|
||||
issue_782/dont_remove_this_binding_sequence/input.js
|
||||
issue_782/remove_redundant_sequence_items/input.js
|
||||
issue_926/template_strings/input.js
|
||||
issue_973/this_binding_collapse_vars/input.js
|
||||
issue_973/this_binding_conditionals/input.js
|
||||
issue_973/this_binding_sequences/input.js
|
||||
issue_973/this_binding_side_effects/input.js
|
||||
issue_t120/issue_t120_1/input.js
|
||||
|
@ -38,11 +38,14 @@ collapse_vars/collapse_vars_arguments/input.js
|
||||
collapse_vars/collapse_vars_assignment/input.js
|
||||
collapse_vars/collapse_vars_constants/input.js
|
||||
collapse_vars/collapse_vars_do_while_drop_assign/input.js
|
||||
collapse_vars/collapse_vars_if/input.js
|
||||
collapse_vars/collapse_vars_object/input.js
|
||||
collapse_vars/collapse_vars_repeated/input.js
|
||||
collapse_vars/collapse_vars_self_reference/input.js
|
||||
collapse_vars/collapse_vars_short_circuit/input.js
|
||||
collapse_vars/collapse_vars_side_effects_1/input.js
|
||||
collapse_vars/collapse_vars_switch/input.js
|
||||
collapse_vars/collapse_vars_unary/input.js
|
||||
collapse_vars/cond_branch_1/input.js
|
||||
collapse_vars/cond_branch_2/input.js
|
||||
collapse_vars/double_def_1/input.js
|
||||
@ -59,6 +62,7 @@ collapse_vars/issue_2319_1/input.js
|
||||
collapse_vars/issue_2319_3/input.js
|
||||
collapse_vars/issue_2436_1/input.js
|
||||
collapse_vars/issue_2436_10/input.js
|
||||
collapse_vars/issue_2436_11/input.js
|
||||
collapse_vars/issue_2436_13/input.js
|
||||
collapse_vars/issue_2436_14/input.js
|
||||
collapse_vars/issue_2436_4/input.js
|
||||
@ -79,6 +83,9 @@ collapse_vars/recursive_function_replacement/input.js
|
||||
collapse_vars/reduce_vars_assign/input.js
|
||||
collapse_vars/ref_scope/input.js
|
||||
collapse_vars/replace_all_var_scope/input.js
|
||||
collapse_vars/switch_case_1/input.js
|
||||
collapse_vars/toplevel_single_reference/input.js
|
||||
collapse_vars/undeclared/input.js
|
||||
collapse_vars/unused_orig/input.js
|
||||
collapse_vars/var_defs/input.js
|
||||
collapse_vars/var_side_effects_2/input.js
|
||||
@ -87,6 +94,7 @@ conditionals/hoist_decl/input.js
|
||||
conditionals/ifs_6/input.js
|
||||
conditionals/ifs_same_consequent/input.js
|
||||
conditionals/issue_1154/input.js
|
||||
conditionals/issue_2535_1/input.js
|
||||
conditionals/issue_2994/input.js
|
||||
dead_code/dead_code_2_should_warn/input.js
|
||||
dead_code/dead_code_2_should_warn_strict/input.js
|
||||
@ -100,6 +108,7 @@ dead_code/return_assignment/input.js
|
||||
dead_code/unsafe_builtin/input.js
|
||||
destructuring/anon_func_with_destructuring_args/input.js
|
||||
destructuring/arrow_func_with_destructuring_args/input.js
|
||||
destructuring/destructuring_dont_evaluate_with_undefined_as_default_assignment/input.js
|
||||
destructuring/export_unreferenced_declarations_2/input.js
|
||||
destructuring/issue_3205_2/input.js
|
||||
destructuring/issue_3205_3/input.js
|
||||
@ -269,6 +278,7 @@ harmony/array_literal_with_spread_4b/input.js
|
||||
harmony/array_spread_of_sequence/input.js
|
||||
harmony/class_name_can_be_mangled/input.js
|
||||
harmony/class_name_can_be_preserved_with_reserved/input.js
|
||||
harmony/classes_extending_classes_out_of_pure_iifes/input.js
|
||||
harmony/expansion/input.js
|
||||
harmony/fat_arrow_as_param/input.js
|
||||
harmony/import_statement_mangling/input.js
|
||||
@ -323,10 +333,12 @@ ie8/issue_2120_1/input.js
|
||||
ie8/issue_2254_1/input.js
|
||||
ie8/issue_2254_2/input.js
|
||||
ie8/reduce_vars/input.js
|
||||
if_return/if_return_same_value/input.js
|
||||
if_return/issue_2747/input.js
|
||||
if_return/issue_512/input.js
|
||||
inline/do_not_repeat_when_variable_larger_than_inlined_node/input.js
|
||||
inline/inline_annotation/input.js
|
||||
inline/inline_within_extends_1/input.js
|
||||
inline/inline_within_extends_2/input.js
|
||||
issue_1034/non_hoisted_function_after_return/input.js
|
||||
issue_1034/non_hoisted_function_after_return_2a_strict/input.js
|
||||
@ -395,8 +407,11 @@ issue_269/issue_269_1/input.js
|
||||
issue_269/regexp/input.js
|
||||
issue_269/strings_concat/input.js
|
||||
issue_2719/warn/input.js
|
||||
issue_281/collapse_vars_constants/input.js
|
||||
issue_281/issue_1758/input.js
|
||||
issue_2871/comparison_with_undefined/input.js
|
||||
issue_44/issue_44_valid_ast_1/input.js
|
||||
issue_44/issue_44_valid_ast_2/input.js
|
||||
issue_597/issue_1724/input.js
|
||||
issue_637/wrongly_optimized/input.js
|
||||
issue_640/cond_5/input.js
|
||||
@ -500,11 +515,13 @@ properties/mangle_debug/input.js
|
||||
properties/mangle_debug_suffix/input.js
|
||||
properties/mangle_debug_suffix_keep_quoted/input.js
|
||||
properties/mangle_debug_true/input.js
|
||||
properties/mangle_undeclared_properties/input.js
|
||||
properties/mangle_unquoted_properties/input.js
|
||||
properties/native_prototype/input.js
|
||||
properties/new_this/input.js
|
||||
properties/prop_side_effects_1/input.js
|
||||
properties/prop_side_effects_2/input.js
|
||||
properties/skip_undeclared_properties_by_default/input.js
|
||||
properties/sub_properties/input.js
|
||||
properties/unsafe_methods_regex/input.js
|
||||
pure_funcs/arithmetic/input.js
|
||||
|
@ -1202,36 +1202,55 @@ impl VisitMut for SimplifyExpr {
|
||||
self.in_callee = true;
|
||||
match &mut n.callee {
|
||||
Callee::Super(..) | Callee::Import(..) => {}
|
||||
Callee::Expr(e) => match &mut **e {
|
||||
Expr::Seq(seq) => {
|
||||
if seq.exprs.len() == 1 {
|
||||
let mut expr = seq.exprs.take().into_iter().next().unwrap();
|
||||
expr.visit_mut_with(self);
|
||||
*e = expr;
|
||||
} else if let Some(
|
||||
Expr::Member(..)
|
||||
| Expr::Ident(Ident {
|
||||
sym: js_word!("eval"),
|
||||
..
|
||||
}),
|
||||
) = seq.exprs.last().map(|v| &**v)
|
||||
{
|
||||
match seq.exprs.get(0).map(|v| &**v) {
|
||||
Some(Expr::Lit(..) | Expr::Ident(..)) => {}
|
||||
_ => {
|
||||
tracing::debug!("Injecting `0` to preserve `this = undefined`");
|
||||
seq.exprs.insert(0, 0.0.into());
|
||||
}
|
||||
}
|
||||
Callee::Expr(e) => {
|
||||
let may_inject_zero = !need_zero_for_this(e);
|
||||
|
||||
seq.visit_mut_with(self);
|
||||
match &mut **e {
|
||||
Expr::Seq(seq) => {
|
||||
if seq.exprs.len() == 1 {
|
||||
let mut expr = seq.exprs.take().into_iter().next().unwrap();
|
||||
expr.visit_mut_with(self);
|
||||
*e = expr;
|
||||
} else if let Some(
|
||||
Expr::Member(..)
|
||||
| Expr::Ident(Ident {
|
||||
sym: js_word!("eval"),
|
||||
..
|
||||
}),
|
||||
) = seq.exprs.last().map(|v| &**v)
|
||||
{
|
||||
match seq.exprs.get(0).map(|v| &**v) {
|
||||
Some(Expr::Lit(..) | Expr::Ident(..)) => {}
|
||||
_ => {
|
||||
tracing::debug!("Injecting `0` to preserve `this = undefined`");
|
||||
seq.exprs.insert(0, 0.0.into());
|
||||
}
|
||||
}
|
||||
|
||||
seq.visit_mut_with(self);
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
e.visit_mut_with(self);
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
e.visit_mut_with(self);
|
||||
if may_inject_zero && need_zero_for_this(e) {
|
||||
match &mut **e {
|
||||
Expr::Seq(seq) => {
|
||||
seq.exprs.insert(0, 0.into());
|
||||
}
|
||||
_ => {
|
||||
let seq = SeqExpr {
|
||||
span: DUMMY_SP,
|
||||
exprs: vec![0.0.into(), e.take()],
|
||||
};
|
||||
**e = Expr::Seq(seq);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
self.in_callee = false;
|
||||
@ -1611,3 +1630,14 @@ fn nth_char(s: &str, mut idx: usize) -> Cow<str> {
|
||||
|
||||
unreachable!("string is too short")
|
||||
}
|
||||
|
||||
fn need_zero_for_this(e: &Expr) -> bool {
|
||||
matches!(
|
||||
e,
|
||||
Expr::Ident(Ident {
|
||||
sym: js_word!("eval"),
|
||||
..
|
||||
}) | Expr::Member(..)
|
||||
| Expr::Seq(..)
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user