Skip to content

Commit

Permalink
feat(es/minifier): Implement correct hoist_props (#8593)
Browse files Browse the repository at this point in the history
**Description:**

 - The option `hoist_props` now does what it's supposed to do.
- Dropping of unused properties now does not drop properties too aggressively.
- The initializer of a dropped variable declaration is now properly visited.
- Indexing with string literals is not marked as a dynamic index anymore. This is required to handle codes like https://github.com/swc-project/swc/blob/c3f67ceb1eb0cab9ef4a47c321acf90684bf216a/crates/swc_ecma_minifier/tests/terser/compress/hoist_props/name_collision_1/input.js#L1-L7.
  • Loading branch information
kdy1 committed Feb 7, 2024
1 parent be55633 commit 3122e94
Show file tree
Hide file tree
Showing 59 changed files with 1,162 additions and 1,250 deletions.
Expand Up @@ -2,9 +2,5 @@
import { _ as _class_call_check } from "@swc/helpers/_/_class_call_check";
var c, i, a, f = function(x) {}, f2 = function(x, y) {};
f(1), f(), f2(1), f2(1, 2), c.foo(), c.foo(1), i(), i(1), i.foo(1), i.foo(1, 2), a(), a(1), a.foo(), a.foo(1);
var b = {
foo: function(x) {},
a: function(x, y) {},
b: function(x) {}
};
b.foo(), b.foo(1), b.a(1), b.a(1, 2), b.b(), b.b(1);
var b_foo = function(x) {}, b_a = function(x, y) {}, b_b = function(x) {};
b_foo(), b_foo(1), b_a(1), b_a(1, 2), b_b(), b_b(1);
Expand Up @@ -2,6 +2,7 @@
//// [/a.js]
var p = {
a: 0,
b: "hello"
b: "hello",
x: 8
};
p.a.toFixed(), p.b.substring(1), p.d;
Expand Up @@ -2,6 +2,7 @@
//// [/a.js]
var p = {
a: 0,
b: "hello"
b: "hello",
x: 8
};
p.a.toFixed(), p.b.substring(1), p.d;
@@ -1,17 +1,2 @@
//// [checkJsdocTypeTagOnObjectProperty2.ts]
//// [0.js]
var lol, obj = {
bar: 42,
method1: function(n1) {
return "42";
},
method2: function(n1) {
return "lol";
},
arrowFunc: function() {
var num = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : "0";
return num + 42;
},
lol: lol
};
lol = "string", obj.method1(0), obj.method2("0");
@@ -1,9 +1,6 @@
//// [destructuringObjectBindingPatternAndAssignment8.ts]
const K = {
a: "a",
b: "b"
}, { [K.a]: aVal, [K.b]: bVal } = {
[K.a]: 1,
[K.b]: 1
const { a: aVal, b: bVal } = {
a: 1,
b: 1
};
console.log(aVal, bVal);
@@ -1,7 +1,4 @@
//// [destructuringObjectBindingPatternAndAssignment8.ts]
import { _ as _define_property } from "@swc/helpers/_/_define_property";
var _obj, K = {
a: "a",
b: "b"
}, _ref = (_define_property(_obj = {}, K.a, 1), _define_property(_obj, K.b, 1), _obj);
console.log(_ref[K.a], _ref[K.b]);
var _obj, _ref = (_define_property(_obj = {}, "a", 1), _define_property(_obj, "b", 1), _obj);
console.log(_ref.a, _ref.b);
@@ -1,11 +1,6 @@
//// [destructuringVariableDeclaration1ES5.ts]
import { _ as _sliced_to_array } from "@swc/helpers/_/_sliced_to_array";
import { _ as _to_consumable_array } from "@swc/helpers/_/_to_consumable_array";
var _ref = {
a1: 10,
a2: "world"
};
_ref.a1, _ref.a2;
var tmp = {
b11: "world"
};
Expand Down
@@ -1,11 +1,6 @@
//// [destructuringVariableDeclaration1ES5iterable.ts]
import { _ as _sliced_to_array } from "@swc/helpers/_/_sliced_to_array";
import { _ as _to_consumable_array } from "@swc/helpers/_/_to_consumable_array";
var _ref = {
a1: 10,
a2: "world"
};
_ref.a1, _ref.a2;
var tmp = {
b11: "world"
};
Expand Down
@@ -1,9 +1,4 @@
//// [destructuringVariableDeclaration2.ts]
var _ref = {
a1: !0,
a2: 1
};
_ref.a1, _ref.a2;
var _ref1 = [
1,
2,
Expand Down
@@ -1,14 +1,11 @@
//// [logicalNotOperatorWithAnyOtherType.ts]
import { _ as _class_call_check } from "@swc/helpers/_/_class_call_check";
var M, obj1 = {
x: "",
y: function() {}
}, A = function() {
var M, A = function() {
function A() {
_class_call_check(this, A);
}
return A.foo = function() {}, A;
}();
M || (M = {});
var objA = new A();
obj1.x, obj1.y, objA.a, M.n, A.foo(), objA.a, M.n;
objA.a, M.n, A.foo(), objA.a, M.n;
Expand Up @@ -33,14 +33,11 @@ var b2 = _object_spread_props(_object_spread({}, b1), {
z: 55
});
_object_spread({}, b2), opts;
var d1 = {
kind: "a",
pos: {
x: 0,
y: 0
}
var d1_pos = {
x: 0,
y: 0
};
d1.kind, d1.pos, d1.pos.x, d1.pos.y, d1.pos.a, d1.pos.b, f({
d1_pos.x, d1_pos.y, d1_pos.a, d1_pos.b, f({
a: 1,
b: 2
}, {
Expand Down
@@ -1,6 +1,2 @@
//// [parserNotHexLiteral1.ts]
var x = {
e0: "cat",
x0: "dog"
};
console.info(x.x0), console.info(x.e0);
console.info("dog"), console.info("cat");
Expand Up @@ -6,7 +6,9 @@ import { _ as _inherits } from "@swc/helpers/_/_inherits";
import { _ as _create_super } from "@swc/helpers/_/_create_super";
export var inModule = 1;
inmodule.toFixed();
var object = {};
var object = {
spaaace: 3
};
object.spaaaace, object.spaace = 12, object.fresh = 12, other.puuuce, new Date().getGMTDate(), setIntegral(function() {
return console.log("ok");
}, 500), AudioBuffin, Jimmy, Jon;
Expand Down
@@ -1,5 +1 @@
//// [stringLiteralTypesAsTags01.ts]
var x = {
kind: "A"
};
x.kind, x.kind;
@@ -1,5 +1 @@
//// [stringLiteralTypesAsTags02.ts]
var x = {
kind: "A"
};
x.kind, x.kind;
@@ -1,5 +1 @@
//// [stringLiteralTypesAsTags03.ts]
var x = {
kind: "A"
};
x.kind, x.kind;
Expand Up @@ -28,11 +28,7 @@ var b, crate, RoyalGuard = function() {
}
return FollowerGuard.prototype.follow = function() {}, FollowerGuard;
}(RoyalGuard), a = new FollowerGuard();
a.isLeader() ? a.lead() : a.isFollower() && a.follow(), b.isLeader() ? b.lead() : b.isFollower() && b.follow();
var holder2 = {
a: a
};
holder2.a.isLeader(), holder2.a;
a.isLeader() ? a.lead() : a.isFollower() && a.follow(), b.isLeader() ? b.lead() : b.isFollower() && b.follow(), a.isLeader();
var ArrowGuard = function ArrowGuard() {
var _this = this;
_class_call_check(this, ArrowGuard), this.isElite = function() {
Expand Down
@@ -1,8 +1 @@
//// [typeTagOnPropertyAssignment.js]
var o = {
a: "a",
n: function() {
return "b";
}
};
o.a, o.n;
@@ -1,3 +1,5 @@
//// [typedefTagExtraneousProperty.js]
var y = {};
var y = {
bye: "no"
};
y.ignoreMe = "ok but just because of the index signature", y.hi = "yes";
@@ -1,7 +1 @@
//// [a.js]
var sala = {
name: "uppsala",
not: 0,
nested: "ok"
};
sala.name, sala.not, sala.nested;
Expand Up @@ -7,8 +7,3 @@ var C = function() {
return C.prototype.m = function(x) {}, C.m = function(x) {}, C;
}();
C.m(), new C().m(), new C().p();
var obj = {
m: function(x) {},
p: function(x) {}
};
obj.m(), obj.p();
@@ -1,14 +1,11 @@
//// [voidOperatorWithAnyOtherType.ts]
import { _ as _class_call_check } from "@swc/helpers/_/_class_call_check";
var M, obj1 = {
x: "",
y: 1
}, A = function() {
var M, A = function() {
function A() {
_class_call_check(this, A);
}
return A.foo = function() {}, A;
}();
M || (M = {});
var objA = new A();
obj1.x, obj1.y, objA.a, M.n, A.foo(), objA.a, M.n;
objA.a, M.n, A.foo(), objA.a, M.n;
2 changes: 2 additions & 0 deletions crates/swc_ecma_minifier/src/compress/optimize/inline.rs
Expand Up @@ -75,6 +75,8 @@ impl Optimizer<'_> {

// No use => dropped
if ref_count == 0 {
self.mode.store(ident.to_id(), &*init);

if init.may_have_side_effects(&self.expr_ctx) {
// TODO: Inline partially
return;
Expand Down
17 changes: 9 additions & 8 deletions crates/swc_ecma_minifier/src/compress/optimize/mod.rs
Expand Up @@ -89,8 +89,6 @@ pub(super) fn optimizer<'a>(
prepend_stmts: Default::default(),
append_stmts: Default::default(),
vars: Default::default(),
vars_for_prop_hoisting: Default::default(),
simple_props: Default::default(),
typeofs: Default::default(),
data,
ctx,
Expand Down Expand Up @@ -209,10 +207,6 @@ struct Optimizer<'a> {

vars: Vars,

/// Used for `hoist_props`.
vars_for_prop_hoisting: Box<FxHashMap<Id, Box<Expr>>>,
/// Used for `hoist_props`.
simple_props: Box<FxHashMap<(Id, JsWord), Box<Expr>>>,
typeofs: Box<AHashMap<Id, JsWord>>,
/// This information is created by analyzing identifier usages.
///
Expand All @@ -236,6 +230,9 @@ struct Vars {
/// Used for inlining.
lits: FxHashMap<Id, Box<Expr>>,

/// Used for `hoist_props`.
hoisted_props: Box<FxHashMap<(Id, JsWord), Ident>>,

/// Literals which are cheap to clone, but not sure if we can inline without
/// making output bigger.
///
Expand Down Expand Up @@ -279,6 +276,7 @@ impl Vars {
lits_for_cmp: &self.lits_for_cmp,
lits_for_array_access: &self.lits_for_array_access,
lits: &self.lits,
hoisted_props: &self.hoisted_props,
vars_to_remove: &self.removed,
changed: false,
};
Expand Down Expand Up @@ -2920,8 +2918,6 @@ impl VisitMut for Optimizer<'_> {
}
};

self.store_var_for_prop_hoisting(var);

self.drop_unused_properties(var);

debug_assert_valid(&var.init);
Expand All @@ -2948,6 +2944,8 @@ impl VisitMut for Optimizer<'_> {
true
});

self.hoist_props_of_vars(vars);

let uses_eval = self.data.scopes.get(&self.ctx.scope).unwrap().has_eval_call;

if !uses_eval && !self.ctx.dont_use_prepend_nor_append {
Expand All @@ -2967,6 +2965,9 @@ impl VisitMut for Optimizer<'_> {
for v in vars.iter_mut() {
let mut storage = None;
self.drop_unused_var_declarator(v, &mut storage);
if let Some(expr) = &mut storage {
expr.visit_mut_with(self);
}
side_effects.extend(storage);

// Dropped. Side effects of the initializer is stored in `side_effects`.
Expand Down

0 comments on commit 3122e94

Please sign in to comment.