mirror of
https://github.com/swc-project/swc.git
synced 2024-12-23 13:51:19 +03:00
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:
parent
a26071f99d
commit
a7cb2aba9d
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -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",
|
||||
|
@ -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 = []
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
})();
|
@ -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;
|
||||
})();
|
@ -0,0 +1,9 @@
|
||||
class ClassA { }
|
||||
|
||||
module.exports = class ClassB {
|
||||
static MyA = ClassA;
|
||||
|
||||
it() {
|
||||
this.bb = new ClassB.MyA();
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
class ClassA {
|
||||
}
|
||||
module.exports = class ClassB {
|
||||
static MyA = ClassA;
|
||||
it() {
|
||||
this.bb = new ClassB.MyA();
|
||||
}
|
||||
};
|
3
ecmascript/minifier/tests/exec/issues/2011/1/config.json
Normal file
3
ecmascript/minifier/tests/exec/issues/2011/1/config.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"defaults": true
|
||||
}
|
24
ecmascript/minifier/tests/exec/issues/2011/1/input.js
Normal file
24
ecmascript/minifier/tests/exec/issues/2011/1/input.js
Normal 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();
|
3
ecmascript/minifier/tests/exec/issues/2011/2/config.json
Normal file
3
ecmascript/minifier/tests/exec/issues/2011/2/config.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"defaults": true
|
||||
}
|
60
ecmascript/minifier/tests/exec/issues/2011/2/input.js
Normal file
60
ecmascript/minifier/tests/exec/issues/2011/2/input.js
Normal 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
Loading…
Reference in New Issue
Block a user