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:
Donny/강동윤 2022-05-12 15:09:09 +09:00
parent dca5bf8b2f
commit 56a227ad9d
7 changed files with 116 additions and 52 deletions

View File

@ -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,

View File

@ -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)

View File

@ -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

View File

@ -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;
},
],

View File

@ -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

View File

@ -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

View File

@ -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(..)
)
}