fix(es/minifier): Fix bugs of the minifier (#2052)

swc_ecma_minifier:
 - Test mangler using execution test suite.
 - `mangler`: Preserve `arguments`.
 - `mangler`: Handle shorthand. (#2051)
 - `mangler`: Handle object pattern properties.
 - `precompress`: Don't drop function declarations if the variable with same name is in different scope. (#2011)
This commit is contained in:
강동윤 2021-08-11 16:24:52 +09:00 committed by GitHub
parent a26071f99d
commit a7cb2aba9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
804 changed files with 560 additions and 12 deletions

6
Cargo.lock generated
View File

@ -2409,7 +2409,7 @@ dependencies = [
[[package]]
name = "swc_ecma_minifier"
version = "0.19.1"
version = "0.19.2"
dependencies = [
"ansi_term 0.12.1",
"anyhow",
@ -2585,7 +2585,7 @@ dependencies = [
[[package]]
name = "swc_ecma_transforms_module"
version = "0.31.0"
version = "0.31.1"
dependencies = [
"Inflector",
"anyhow",
@ -3084,7 +3084,7 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "wasm"
version = "1.2.76"
version = "1.2.77"
dependencies = [
"console_error_panic_hook",
"once_cell",

View File

@ -7,7 +7,7 @@ include = ["Cargo.toml", "src/**/*.rs", "src/lists/*.json"]
license = "Apache-2.0/MIT"
name = "swc_ecma_minifier"
repository = "https://github.com/swc-project/swc.git"
version = "0.19.1"
version = "0.19.2"
[features]
debug = []

View File

@ -7,6 +7,7 @@ use crate::option::MangleOptions;
use crate::util::base54::incr_base54;
use fxhash::FxHashMap;
use fxhash::FxHashSet;
use swc_atoms::js_word;
use swc_atoms::JsWord;
use swc_common::SyntaxContext;
use swc_ecma_ast::*;
@ -52,21 +53,26 @@ struct Mangler {
}
impl Mangler {
fn rename(&mut self, i: &mut Ident) {
fn rename(&mut self, i: &mut Ident) -> bool {
match i.sym {
js_word!("arguments") => return false,
_ => {}
}
if self.preserved.contains(&i.to_id()) {
return;
return false;
}
if let Some(var) = self.data.as_ref().unwrap().vars.get(&i.to_id()) {
if !var.declared {
return;
return false;
}
}
if let Some(v) = self.renamed.get(&i.to_id()) {
i.span.ctxt = SyntaxContext::empty();
i.sym = v.clone();
return;
return true;
}
loop {
@ -83,6 +89,8 @@ impl Mangler {
i.span.ctxt = SyntaxContext::empty();
break;
}
true
}
fn rename_private(&mut self, private_name: &mut PrivateName) {
@ -121,6 +129,7 @@ impl VisitMut for Mangler {
self.rename(&mut n.orig);
}
fn visit_mut_expr(&mut self, e: &mut Expr) {
e.visit_mut_children_with(self);
@ -183,6 +192,52 @@ impl VisitMut for Mangler {
n.visit_mut_children_with(self)
}
fn visit_mut_object_pat_prop(&mut self, n: &mut ObjectPatProp) {
match n {
ObjectPatProp::Assign(AssignPatProp {
value: None, key, ..
}) => {
let key_span = key.span.with_ctxt(SyntaxContext::empty());
let orig = key.sym.clone();
if self.rename(key) {
*n = ObjectPatProp::KeyValue(KeyValuePatProp {
key: PropName::Ident(Ident::new(orig, key_span)),
value: Box::new(Pat::Ident(key.clone().into())),
});
}
}
ObjectPatProp::Assign(p) => {
let key_span = p.key.span.with_ctxt(SyntaxContext::empty());
let orig = p.key.sym.clone();
if self.rename(&mut p.key) {
if let Some(right) = p.value.take() {
*n = ObjectPatProp::KeyValue(KeyValuePatProp {
key: PropName::Ident(Ident::new(orig, key_span)),
value: Box::new(Pat::Assign(AssignPat {
span: p.span,
left: Box::new(Pat::Ident(p.key.clone().into())),
right,
type_ann: None,
})),
});
} else {
*n = ObjectPatProp::KeyValue(KeyValuePatProp {
key: PropName::Ident(Ident::new(orig, key_span)),
value: Box::new(Pat::Ident(p.key.clone().into())),
});
}
}
}
_ => {
n.visit_mut_children_with(self);
}
}
}
fn visit_mut_pat(&mut self, n: &mut Pat) {
n.visit_mut_children_with(self);
@ -200,6 +255,25 @@ impl VisitMut for Mangler {
}
}
fn visit_mut_prop(&mut self, n: &mut Prop) {
match n {
Prop::Shorthand(p) => {
let span = p.span.with_ctxt(SyntaxContext::empty());
let orig = p.sym.clone();
if self.rename(p) {
*n = Prop::KeyValue(KeyValueProp {
key: PropName::Ident(Ident::new(orig, span)),
value: Box::new(Expr::Ident(p.clone())),
});
}
}
_ => {
n.visit_mut_children_with(self);
}
}
}
fn visit_mut_script(&mut self, n: &mut Script) {
let data = analyze(&*n, None);
self.data = Some(data);

View File

@ -119,7 +119,7 @@ impl VisitMut for PrecompressOptimizer<'_> {
if self.options.dead_code || self.options.unused {
if let Some(usage) = self.data.as_ref().unwrap().vars.get(&n.ident.to_id()) {
// Remove if variable with same name exists.
if usage.var_kind.is_some() && usage.var_initialized {
if usage.var_kind.is_some() && usage.var_initialized && usage.is_fn_local {
n.ident.take();
return;
}

View File

@ -312,7 +312,7 @@ fn projects(input: PathBuf) {
}
/// Tests used to prevent regressions.
#[testing::fixture("tests/compress/exec/**/input.js")]
#[testing::fixture("tests/exec/**/input.js")]
fn base_exec(input: PathBuf) {
let dir = input.parent().unwrap();
let config = dir.join("config.json");

View File

@ -0,0 +1,55 @@
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
var ClassA = function ClassA() {
"use strict";
_classCallCheck(this, ClassA);
};
module.exports = (function () {
var ClassB = /*#__PURE__*/ function () {
"use strict";
function ClassB() {
_classCallCheck(this, ClassB);
}
_createClass(ClassB, [
{
key: "it",
value: function it() {
this.bb = new ClassB.MyA();
}
}
]);
return ClassB;
}();
_defineProperty(ClassB, "MyA", ClassA);
return ClassB;
})();

View File

@ -0,0 +1,41 @@
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
}
function _defineProperties(target, props) {
for(var i = 0; i < props.length; i++){
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || !1, descriptor.configurable = !0, "value" in descriptor && (descriptor.writable = !0), Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
return protoProps && _defineProperties(Constructor.prototype, protoProps), staticProps && _defineProperties(Constructor, staticProps), Constructor;
}
function _defineProperty(obj, key, value) {
return key in obj ? Object.defineProperty(obj, key, {
value: value,
enumerable: !0,
configurable: !0,
writable: !0
}) : obj[key] = value, obj;
}
var ClassA = function ClassA() {
"use strict";
_classCallCheck(this, ClassA);
};
module.exports = (function() {
var ClassB = function() {
"use strict";
function ClassB() {
_classCallCheck(this, ClassB);
}
return _createClass(ClassB, [
{
key: "it",
value: function() {
this.bb = new ClassB.MyA();
}
}
]), ClassB;
}();
return _defineProperty(ClassB, "MyA", ClassA), ClassB;
})();

View File

@ -0,0 +1,9 @@
class ClassA { }
module.exports = class ClassB {
static MyA = ClassA;
it() {
this.bb = new ClassB.MyA();
}
}

View File

@ -0,0 +1,8 @@
class ClassA {
}
module.exports = class ClassB {
static MyA = ClassA;
it() {
this.bb = new ClassB.MyA();
}
};

View File

@ -0,0 +1,3 @@
{
"defaults": true
}

View File

@ -0,0 +1,24 @@
class ClassA {
constructor() {
console.log('Class A');
}
}
const cls = class ClassB {
static MyA = ClassA;
constructor() {
console.log('Claas B');
}
it() {
console.log('method it - start');
this.bb = new ClassB.MyA();
console.log('method it - end');
}
}
new cls().it();

View File

@ -0,0 +1,3 @@
{
"defaults": true
}

View File

@ -0,0 +1,60 @@
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
var ClassA = function ClassA() {
"use strict";
_classCallCheck(this, ClassA);
console.log('Class A');
};
var cls = function () {
var ClassB = /*#__PURE__*/ function () {
"use strict";
function ClassB() {
_classCallCheck(this, ClassB);
console.log('Claas B');
}
_createClass(ClassB, [
{
key: "it",
value: function it() {
console.log('method it - start');
this.bb = new ClassB.MyA();
console.log('method it - end');
}
}
]);
return ClassB;
}();
_defineProperty(ClassB, "MyA", ClassA);
return ClassB;
}();
new cls().it();

Some files were not shown because too many files have changed in this diff Show More