fix(es/minifier): Prepend/append correctly (#3367)

swc_ecma_minifier:
 - Add some assertions for injections.
 - Fix prepend/append logic of statements.
This commit is contained in:
Donny/강동윤 2022-01-26 13:11:26 +09:00 committed by GitHub
parent 14e87d3a95
commit 703972dc29
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 100 additions and 22 deletions

View File

@ -24,10 +24,6 @@ var MyClass = function() {
}
], _defineProperties((Constructor = MyClass).prototype, protoProps), staticProps && _defineProperties(Constructor, staticProps), MyClass;
}();
(m = 3, function() {
return function(n) {
return 3 + n;
};
})()(4), (function() {
(function() {
return 0;
})().toExponential();

View File

@ -1,8 +1,8 @@
var A1, D, E, F;
var A, D, E, F;
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
}
(A = A1 || (A1 = {})).x = 12, (D || (D = {})).yes = function() {
(A || (A = {})).x = 12, (D || (D = {})).yes = function() {
return !0;
}, (function(E1) {
(Color = Color = E1.Color || (E1.Color = {}))[Color.Red = 0] = "Red", E1.fn = function() {};

View File

@ -1,2 +1,2 @@
var m11, m21, m31, m41, m51, m6;
m1 = m11 || (m11 = {}), (void 0).then(testFunction), m2 = m21 || (m21 = {}), (void 0).then(testFunction), m3 = m31 || (m31 = {}), (void 0).then(testFunction), m4 = m41 || (m41 = {}), (void 0).then(testFunction), m5 = m51 || (m51 = {}), (void 0).then(testFunction), m6 || (m6 = {}), (void 0).then(testFunction);
var m1, m2, m3, m4, m5, m6;
m1 || (m1 = {}), (void 0).then(testFunction), m2 || (m2 = {}), (void 0).then(testFunction), m3 || (m3 = {}), (void 0).then(testFunction), m4 || (m4 = {}), (void 0).then(testFunction), m5 || (m5 = {}), (void 0).then(testFunction), m6 || (m6 = {}), (void 0).then(testFunction);

View File

@ -1,2 +1,2 @@
var m11, m21, m31, m41, m51, m6;
m1 = m11 || (m11 = {}), (void 0).then(testFunction), m2 = m21 || (m21 = {}), (void 0).then(testFunction), m3 = m31 || (m31 = {}), (void 0).then(testFunction), m4 = m41 || (m41 = {}), (void 0).then(testFunction), m5 = m51 || (m51 = {}), (void 0).then(testFunction), m6 || (m6 = {}), (void 0).then(testFunction);
var m1, m2, m3, m4, m5, m6;
m1 || (m1 = {}), (void 0).then(testFunction), m2 || (m2 = {}), (void 0).then(testFunction), m3 || (m3 = {}), (void 0).then(testFunction), m4 || (m4 = {}), (void 0).then(testFunction), m5 || (m5 = {}), (void 0).then(testFunction), m6 || (m6 = {}), (void 0).then(testFunction);

View File

@ -137,6 +137,7 @@ where
/// })(x);
/// })(7);
/// ```
#[cfg_attr(feature = "debug", tracing::instrument(skip(self, e)))]
pub(super) fn inline_args_of_iife(&mut self, e: &mut CallExpr) {
if self.options.inline == 0 {
return;
@ -250,6 +251,7 @@ where
}
}
#[cfg_attr(feature = "debug", tracing::instrument(skip(self, n, vars)))]
pub(super) fn inline_vars_in_node<N>(&mut self, n: &mut N, vars: AHashMap<Id, Box<Expr>>)
where
N: VisitMutWith<Self>,
@ -616,6 +618,9 @@ where
.collect::<Vec<_>>();
if !vars.is_empty() {
if cfg!(feature = "debug") {
tracing::debug!("iife: Creating variables: {:?}", vars);
}
self.prepend_stmts.push(Stmt::Decl(Decl::Var(VarDecl {
span: DUMMY_SP.apply_mark(self.marks.non_top_level),
kind: VarDeclKind::Var,

View File

@ -188,9 +188,9 @@ struct Optimizer<'a, M> {
options: &'a CompressOptions,
/// Statements prepended to the current statement.
prepend_stmts: Vec<Stmt>,
prepend_stmts: SynthesizedStmts,
/// Statements appended to the current statement.
append_stmts: Vec<Stmt>,
append_stmts: SynthesizedStmts,
/// Cheap to clone.
///
@ -239,6 +239,7 @@ impl<M> Optimizer<'_, M>
where
M: Mode,
{
#[cfg_attr(feature = "debug", tracing::instrument(skip(self, stmts)))]
fn handle_stmt_likes<T>(&mut self, stmts: &mut Vec<T>)
where
T: StmtLike + ModuleItemLike + ModuleItemExt + VisitMutWith<Self>,
@ -251,8 +252,8 @@ where
}
}
let mut use_asm = false;
// let prepend_stmts = self.prepend_stmts.take();
// let append_stmts = self.append_stmts.take();
let prepend_stmts = self.prepend_stmts.take();
let append_stmts = self.append_stmts.take();
{
let mut child_ctx = Ctx { ..self.ctx };
@ -289,6 +290,8 @@ where
stmt.visit_mut_with(&mut *self.with_ctx(child_ctx));
}
new.extend(self.prepend_stmts.drain(..).map(T::from_stmt));
match stmt.try_into_stmt() {
Ok(Stmt::Block(s)) if s.span.has_mark(self.marks.fake_block) => {
new.extend(s.stmts.into_iter().map(T::from_stmt));
@ -301,8 +304,7 @@ where
}
}
// new.extend(self.prepend_stmts.drain(..).map(T::from_stmt));
// new.extend(self.append_stmts.drain(..).map(T::from_stmt));
new.extend(self.append_stmts.drain(..).map(T::from_stmt));
}
*stmts = new;
}
@ -360,8 +362,8 @@ where
drop_invalid_stmts(stmts);
// debug_assert_eq!(self.prepend_stmts, vec![]);
// self.prepend_stmts = prepend_stmts;
// self.append_stmts = append_stmts;
self.prepend_stmts = prepend_stmts;
self.append_stmts = append_stmts;
}
/// `a = a + 1` => `a += 1`.
@ -1535,6 +1537,7 @@ where
{
noop_visit_mut_type!();
#[cfg_attr(feature = "debug", tracing::instrument(skip(self, n,)))]
fn visit_mut_arrow_expr(&mut self, n: &mut ArrowExpr) {
let prepend = self.prepend_stmts.take();
@ -1546,7 +1549,7 @@ where
n.visit_mut_children_with(&mut *self.with_ctx(ctx));
if !self.prepend_stmts.is_empty() {
let mut stmts = self.prepend_stmts.take();
let mut stmts = self.prepend_stmts.take().take_stmts();
match &mut n.body {
BlockStmtOrExpr::BlockStmt(v) => {
prepend_stmts(&mut v.stmts, stmts.into_iter());
@ -1575,6 +1578,7 @@ where
}
}
#[cfg_attr(feature = "debug", tracing::instrument(skip(self, e)))]
fn visit_mut_assign_expr(&mut self, e: &mut AssignExpr) {
{
let ctx = Ctx {
@ -1594,6 +1598,7 @@ where
self.compress_bin_assignment_to_right(e);
}
#[cfg_attr(feature = "debug", tracing::instrument(skip(self, n)))]
fn visit_mut_assign_pat_prop(&mut self, n: &mut AssignPatProp) {
n.visit_mut_children_with(self);
@ -1604,6 +1609,7 @@ where
}
}
#[cfg_attr(feature = "debug", tracing::instrument(skip(self, n)))]
fn visit_mut_bin_expr(&mut self, n: &mut BinExpr) {
{
let ctx = Ctx {
@ -1631,6 +1637,7 @@ where
}
}
#[cfg_attr(feature = "debug", tracing::instrument(skip(self, n)))]
fn visit_mut_block_stmt(&mut self, n: &mut BlockStmt) {
let ctx = Ctx {
stmt_labelled: false,
@ -1654,6 +1661,7 @@ where
}
}
#[cfg_attr(feature = "debug", tracing::instrument(skip(self, e)))]
fn visit_mut_call_expr(&mut self, e: &mut CallExpr) {
let is_this_undefined = match &e.callee {
Callee::Super(_) | Callee::Import(_) => false,
@ -1703,6 +1711,7 @@ where
self.inline_args_of_iife(e);
}
#[cfg_attr(feature = "debug", tracing::instrument(skip(self, n)))]
fn visit_mut_class(&mut self, n: &mut Class) {
n.decorators.visit_mut_with(self);
@ -1731,6 +1740,7 @@ where
e.visit_mut_children_with(self);
}
#[cfg_attr(feature = "debug", tracing::instrument(skip(self, decl)))]
fn visit_mut_decl(&mut self, decl: &mut Decl) {
decl.visit_mut_children_with(self);
@ -1739,6 +1749,7 @@ where
self.store_decl_for_inlining(decl);
}
#[cfg_attr(feature = "debug", tracing::instrument(skip(self, n)))]
fn visit_mut_default_decl(&mut self, n: &mut DefaultDecl) {
match n {
DefaultDecl::Class(_) => {}
@ -1753,6 +1764,7 @@ where
n.visit_mut_children_with(self);
}
#[cfg_attr(feature = "debug", tracing::instrument(skip(self, n)))]
fn visit_mut_export_decl(&mut self, n: &mut ExportDecl) {
if let Decl::Fn(f) = &mut n.decl {
// I don't know why, but terser removes parameters from an exported function if
@ -1777,6 +1789,7 @@ where
n.visit_mut_children_with(&mut *self.with_ctx(ctx));
}
#[cfg_attr(feature = "debug", tracing::instrument(skip(self, e)))]
fn visit_mut_expr(&mut self, e: &mut Expr) {
let ctx = Ctx {
is_exported: false,
@ -1848,6 +1861,7 @@ where
}
}
#[cfg_attr(feature = "debug", tracing::instrument(skip(self, n)))]
fn visit_mut_expr_stmt(&mut self, n: &mut ExprStmt) {
n.visit_mut_children_with(self);
@ -1977,6 +1991,7 @@ where
self.with_ctx(ctx).drop_if_break(s);
}
#[cfg_attr(feature = "debug", tracing::instrument(skip(self, n)))]
fn visit_mut_function(&mut self, n: &mut Function) {
{
let ctx = Ctx {
@ -2267,6 +2282,7 @@ where
self.lift_seqs_of_assign(n);
}
#[cfg_attr(feature = "debug", tracing::instrument(skip(self, s)))]
fn visit_mut_stmt(&mut self, s: &mut Stmt) {
let old_prepend = self.prepend_stmts.take();
let old_append = self.append_stmts.take();
@ -2338,10 +2354,10 @@ where
span: span.apply_mark(self.marks.fake_block),
stmts: self
.prepend_stmts
.take()
.take_stmts()
.into_iter()
.chain(once(s.take()))
.chain(self.append_stmts.take().into_iter())
.chain(self.append_stmts.take_stmts().into_iter())
.filter(|s| match s {
Stmt::Empty(..) => false,
Stmt::Decl(Decl::Var(v)) => !v.decls.is_empty(),
@ -2358,6 +2374,8 @@ where
self.prepend_stmts = old_prepend;
self.append_stmts = old_append;
let len = self.prepend_stmts.len();
if cfg!(feature = "debug") && self.debug_infinite_loop {
let text = dump(&*s, false);
@ -2366,6 +2384,8 @@ where
}
}
debug_assert_eq!(self.prepend_stmts.len(), len);
if let Stmt::Expr(ExprStmt { expr, .. }) = s {
if is_pure_undefined(expr) {
*s = Stmt::Empty(EmptyStmt { span: DUMMY_SP });
@ -2402,6 +2422,8 @@ where
}
}
debug_assert_eq!(self.prepend_stmts.len(), len);
match s {
// We use var decl with no declarator to indicate we dropped an decl.
Stmt::Decl(Decl::Var(VarDecl { decls, .. })) if decls.is_empty() => {
@ -2411,20 +2433,34 @@ where
_ => {}
}
debug_assert_eq!(self.prepend_stmts.len(), len);
if cfg!(debug_assertions) {
s.visit_with(&mut AssertValid);
}
debug_assert_eq!(self.prepend_stmts.len(), len);
self.compress_if_without_alt(s);
debug_assert_eq!(self.prepend_stmts.len(), len);
self.compress_if_stmt_as_cond(s);
debug_assert_eq!(self.prepend_stmts.len(), len);
self.compress_if_stmt_as_expr(s);
debug_assert_eq!(self.prepend_stmts.len(), len);
self.optimize_const_switches(s);
debug_assert_eq!(self.prepend_stmts.len(), len);
self.optimize_switches(s);
debug_assert_eq!(self.prepend_stmts.len(), len);
if cfg!(feature = "debug") && self.debug_infinite_loop {
let text = dump(&*s, false);
@ -2433,9 +2469,13 @@ where
}
}
debug_assert_eq!(self.prepend_stmts.len(), len);
if cfg!(debug_assertions) {
s.visit_with(&mut AssertValid);
}
debug_assert_eq!(self.prepend_stmts.len(), len);
}
fn visit_mut_stmts(&mut self, stmts: &mut Vec<Stmt>) {
@ -2743,3 +2783,40 @@ fn is_left_access_to_arguments(l: &PatOrExpr) -> bool {
},
}
}
#[derive(Debug, Default, PartialEq)]
struct SynthesizedStmts(Vec<Stmt>);
impl SynthesizedStmts {
fn take_stmts(&mut self) -> Vec<Stmt> {
take(&mut self.0)
}
}
impl std::ops::Deref for SynthesizedStmts {
type Target = Vec<Stmt>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for SynthesizedStmts {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl Take for SynthesizedStmts {
fn dummy() -> Self {
Self(Take::dummy())
}
}
impl Drop for SynthesizedStmts {
fn drop(&mut self) {
if !self.0.is_empty() {
panic!("We should not drop synthesized stmts");
}
}
}