feat(es/minifier): Implement more rules for dropping unused assignemnts (#4171)

This commit is contained in:
Donny/강동윤 2022-03-28 18:14:16 +09:00 committed by GitHub
parent e7089d8b3e
commit 6f25e5774b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 115 additions and 46 deletions

View File

@ -1 +1 @@
for (let v of [])for (let v1 of [])v1++;
for (let v of [])for (let v1 of []);

View File

@ -4,10 +4,7 @@ try {
_step.value;
var _iteratorNormalCompletion1 = !0, _didIteratorError1 = !1, _iteratorError1 = void 0;
try {
for(var _step1, _iterator1 = [][Symbol.iterator](); !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = !0){
var v = _step1.value;
v++;
}
for(var _step1, _iterator1 = [][Symbol.iterator](); !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = !0)_step1.value;
} catch (err) {
_didIteratorError1 = !0, _iteratorError1 = err;
} finally{

View File

@ -1,4 +1,4 @@
var E, a, b, x1, x2, x3, x4, x5, x6, x7;
var E, a, b, x1, x2, x3, x4, x6;
!function(E) {
E[E.a = 0] = "a", E[E.b = 1] = "b";
}(E || (E = {})), x1 += a, x1 += b, x1 += !0, x1 += 0, x1 += '', x1 += E.a, x1 += {}, x1 += null, x1 += void 0, x2 += a, x2 += b, x2 += !0, x2 += 0, x2 += '', x2 += E.a, x2 += {}, x2 += null, x2 += void 0, x3 += a, x3 += 0, x3 += E.a, x3 += null, x3 += void 0, x4 += a, x4 += 0, x4 += E.a, x4 += null, x4 += void 0, x5 += a, x6 += a, x6 += '', x7 += a;
}(E || (E = {})), x1 += a, x1 += b, x1 += !0, x1 += 0, x1 += '', x1 += E.a, x1 += {}, x1 += null, x1 += void 0, x2 += a, x2 += b, x2 += !0, x2 += 0, x2 += '', x2 += E.a, x2 += {}, x2 += null, x2 += void 0, x3 += a, x3 += 0, x3 += E.a, x3 += null, x3 += void 0, x4 += a, x4 += 0, x4 += E.a, x4 += null, x4 += void 0, x6 += a, x6 += '';

View File

@ -1,4 +1,4 @@
var E, a, b, x1, x2, x3, x4, x5, x6, x7;
var E, a, b, x1, x2, x3, x4, x6;
!function(E) {
E[E.a = 0] = "a", E[E.b = 1] = "b";
}(E || (E = {})), x1 += a, x1 += b, x1 += !0, x1 += 0, x1 += "", x1 += E.a, x1 += {}, x1 += null, x1 += void 0, x2 += a, x2 += b, x2 += !0, x2 += 0, x2 += "", x2 += E.a, x2 += {}, x2 += null, x2 += void 0, x3 += a, x3 += 0, x3 += E.a, x3 += null, x3 += void 0, x4 += a, x4 += 0, x4 += E.a, x4 += null, x4 += void 0, x5 += a, x6 += a, x6 += "", x7 += a;
}(E || (E = {})), x1 += a, x1 += b, x1 += !0, x1 += 0, x1 += "", x1 += E.a, x1 += {}, x1 += null, x1 += void 0, x2 += a, x2 += b, x2 += !0, x2 += 0, x2 += "", x2 += E.a, x2 += {}, x2 += null, x2 += void 0, x3 += a, x3 += 0, x3 += E.a, x3 += null, x3 += void 0, x4 += a, x4 += 0, x4 += E.a, x4 += null, x4 += void 0, x6 += a, x6 += "";

View File

@ -1,4 +1,4 @@
var E, x1, x2, x3, x4, x5;
var E;
!function(E) {
E[E.a = 0] = "a", E[E.b = 1] = "b", E[E.c = 2] = "c";
}(E || (E = {})), x1 += '', x2 += '', x3 += '', x4 += '', x5 += '';
}(E || (E = {}));

View File

@ -1,4 +1,4 @@
var E, x1, x2, x3, x4, x5;
var E;
!function(E) {
E[E.a = 0] = "a", E[E.b = 1] = "b", E[E.c = 2] = "c";
}(E || (E = {})), x1 += "", x2 += "", x3 += "", x4 += "", x5 += "";
}(E || (E = {}));

View File

@ -1,17 +1,14 @@
import * as a from "@swc/helpers";
Promise.all(assignAll).then(function() {
var b = a.asyncToGenerator(function*(e) {
let b = 'DELETE FROM "TABLE" WHERE "UUID" IN ( ';
for(let c in obj){
let a = obj[c];
b += `'${a.id}', `;
let d = yield listOfUser(a.id);
d.forEach((b)=>{
var b = a.asyncToGenerator(function*(c) {
for(let b in obj){
let a = obj[b];
`'${a.id}', `, (yield listOfUser(a.id)).forEach((b)=>{
insertQuery += `INSERT INTO "TABLE"("UUID", id, other_ids_here) VALUES ('${uuidv4()}', '${a.id}', now());`;
});
}
});
return function(e) {
return function(c) {
return b.apply(this, arguments);
};
}());

View File

@ -76,6 +76,9 @@ pub(crate) struct VarUsageInfo {
pub assign_count: usize,
pub mutation_by_call_count: usize,
/// ## Things to note
///
/// - Update is counted as usage
pub usage_count: usize,
/// The variable itself is modified.

View File

@ -1,7 +1,8 @@
use std::{collections::HashMap, mem::swap};
use rustc_hash::FxHashMap;
use swc_atoms::js_word;
use swc_common::{collections::AHashMap, pass::Either, util::take::Take, Spanned, DUMMY_SP};
use swc_common::{pass::Either, util::take::Take, Spanned, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_utils::{contains_arguments, ident::IdentLike, undefined, ExprFactory, Id};
use swc_ecma_visit::VisitMutWith;
@ -246,8 +247,8 @@ 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>>)
#[cfg_attr(feature = "debug", tracing::instrument(skip_all))]
pub(super) fn inline_vars_in_node<N>(&mut self, n: &mut N, vars: FxHashMap<Id, Box<Expr>>)
where
N: VisitMutWith<MultiReplacer>,
{

View File

@ -195,7 +195,7 @@ struct Optimizer<'a, M> {
/// Used for inlining.
lits: AHashMap<Id, Box<Expr>>,
vars_for_inlining: AHashMap<Id, Box<Expr>>,
vars_for_inlining: FxHashMap<Id, Box<Expr>>,
vars_for_prop_hoisting: AHashMap<Id, Box<Expr>>,
/// Used for `hoist_props`.
@ -703,6 +703,10 @@ where
self.compress_cond_to_logical_ignoring_return_value(e);
self.drop_unused_update(e);
self.drop_unused_op_assign(e);
match e {
Expr::This(_) | Expr::Invalid(_) | Expr::Lit(..) => {
if cfg!(feature = "debug") {

View File

@ -508,16 +508,39 @@ where
}
}
/// This should be only called from ignore_return_value
#[cfg_attr(feature = "debug", tracing::instrument(skip_all))]
pub(super) fn drop_unused_assignments(&mut self, e: &mut Expr) {
let assign = match e {
Expr::Assign(e) => e,
pub(super) fn drop_unused_update(&mut self, e: &mut Expr) {
if !self.options.unused {
return;
}
let update = match e {
Expr::Update(u) => u,
_ => return,
};
let has_mark = assign.span.has_mark(self.marks.non_top_level);
if let Expr::Ident(arg) = &*update.arg {
if let Some(var) = self.data.vars.get(&arg.to_id()) {
// Update is counted as usage
if var.declared && var.is_fn_local && var.usage_count == 1 {
self.changed = true;
tracing::debug!(
"unused: Dropping an update '{}{:?}' because it is not used",
arg.sym,
arg.span.ctxt
);
// This will remove the update.
e.take();
}
}
}
}
if !has_mark && !self.options.unused {
/// This should be only called from ignore_return_value
#[cfg_attr(feature = "debug", tracing::instrument(skip_all))]
pub(super) fn drop_unused_op_assign(&mut self, e: &mut Expr) {
if !self.options.unused {
return;
}
@ -529,6 +552,58 @@ where
return;
}
let assign = match e {
Expr::Assign(AssignExpr { op: op!("="), .. }) => return,
// RHS may not be evaluated
Expr::Assign(AssignExpr {
op: op!("&&=") | op!("||=") | op!("&&="),
..
}) => return,
Expr::Assign(e) => e,
_ => return,
};
assign.left.map_with_mut(|left| left.normalize_ident());
if let PatOrExpr::Pat(p) = &assign.left {
if let Pat::Ident(left) = &**p {
if let Some(var) = self.data.vars.get(&left.to_id()) {
// TODO: We don't need fn_local check
if var.declared && var.is_fn_local && var.usage_count == 1 {
self.changed = true;
tracing::debug!(
"unused: Dropping an op-assign '{}{:?}' because it is not used",
left.id.sym,
left.id.span.ctxt
);
// This will remove the op-assign.
*e = *assign.right.take();
}
}
}
}
}
#[cfg_attr(feature = "debug", tracing::instrument(skip_all))]
pub(super) fn drop_unused_assignments(&mut self, e: &mut Expr) {
if self.ctx.is_delete_arg {
return;
}
if self.data.top.has_eval_call || self.data.top.has_with_stmt {
return;
}
let assign = match e {
Expr::Assign(e) => e,
_ => return,
};
let has_mark = assign.span.has_mark(self.marks.non_top_level);
if !has_mark && !self.options.unused {
return;
}
let used_arguments = self
.data
.scopes

View File

@ -1,7 +1,8 @@
use std::ops::{Deref, DerefMut};
use rustc_hash::FxHashMap;
use swc_atoms::JsWord;
use swc_common::{collections::AHashMap, Span};
use swc_common::Span;
use swc_ecma_ast::*;
use swc_ecma_utils::{ident::IdentLike, prop_name_eq, ExprExt, Id};
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
@ -168,7 +169,7 @@ pub(crate) fn is_valid_for_lhs(e: &Expr) -> bool {
}
pub(crate) struct MultiReplacer {
pub vars: AHashMap<Id, Box<Expr>>,
pub vars: FxHashMap<Id, Box<Expr>>,
pub changed: bool,
}

View File

@ -174,7 +174,6 @@ drop_unused/issue_2105_2/input.js
drop_unused/issue_2136_2/input.js
drop_unused/issue_2136_3/input.js
drop_unused/issue_2163/input.js
drop_unused/issue_2226_1/input.js
drop_unused/issue_2226_2/input.js
drop_unused/issue_2226_3/input.js
drop_unused/issue_2288/input.js

View File

@ -381,6 +381,7 @@ drop_unused/issue_1656/input.js
drop_unused/issue_1715_1/input.js
drop_unused/issue_1715_2/input.js
drop_unused/issue_2136_1/input.js
drop_unused/issue_2226_1/input.js
drop_unused/issue_2995/input.js
drop_unused/issue_3146_1/input.js
drop_unused/issue_3146_2/input.js

View File

@ -6,7 +6,6 @@ function f2(a) {
b;
}
function f3(a) {
0;
}
function f4() {
var a = b;

View File

@ -1,11 +0,0 @@
//! Ensure that wrong macro definitions are caught by swc monorepo.
use swc_ecma_visit::Fold;
fn drop_console(_: ()) -> impl Fold {
DropConsole
}
struct DropConsole;
impl Fold for DropConsole {}

View File

@ -1,3 +1,5 @@
#![allow(clippy::unused_unit)]
use std::sync::Arc;
use anyhow::{Context, Error};
@ -8,7 +10,7 @@ use swc::{
};
use swc_common::{comments::Comments, FileName, FilePathMapping, SourceMap};
use swc_ecmascript::ast::{EsVersion, Program};
use wasm_bindgen::{prelude::*, JsCast};
use wasm_bindgen::prelude::*;
fn convert_err(err: Error) -> JsValue {
format!("{:?}", err).into()
@ -156,6 +158,7 @@ pub fn transform_sync(
{
if experimental_plugin_bytes_resolver.is_object() {
use js_sys::{Array, Object, Uint8Array};
use wasm_bindgen::JsCast;
// TODO: This is probably very inefficient, including each transform
// deserializes plugin bytes.