Skip to content

Commit

Permalink
feat(es/minifier): Inline pure array literal partially (#6099)
Browse files Browse the repository at this point in the history
  • Loading branch information
kdy1 committed Oct 12, 2022
1 parent e37ced5 commit 66196a6
Show file tree
Hide file tree
Showing 24 changed files with 119 additions and 129 deletions.
@@ -1,9 +1,6 @@
//// [bitwiseNotOperatorWithAnyOtherType.ts]
import _class_call_check from "@swc/helpers/src/_class_call_check.mjs";
var M, ANY2 = [
"",
""
], obj1 = {
var M, obj1 = {
x: "",
y: function() {}
}, A = function() {
Expand All @@ -18,4 +15,4 @@ var M, ANY2 = [
M.n = n;
}(M || (M = {}));
var objA = new A();
ANY2[0], obj1.x, obj1.y, objA.a, M.n, A.foo(), ANY2[0], obj1.y, objA.a, M.n, obj1.x;
obj1.x, obj1.y, objA.a, M.n, A.foo(), obj1.y, objA.a, M.n, obj1.x;
@@ -1,7 +1 @@
//// [conditionalOperatorConditionIsNumberType.ts]
var array = [
1,
2,
3
];
array[1], array[1], array[1];
@@ -1,11 +1,7 @@
//// [conditionalOperatorConditoinIsStringType.ts]
var condString;
import _type_of from "@swc/helpers/src/_type_of.mjs";
function foo() {
return "string";
}
var condString, array = [
"1",
"2",
"3"
];
void 0 === condString || _type_of(condString), condString.toUpperCase, foo(), array[1], foo(), void 0 === condString || _type_of(condString), condString.toUpperCase, foo(), array[1], void 0 === condString || _type_of(condString), condString.toUpperCase;
void 0 === condString || _type_of(condString), condString.toUpperCase, foo(), foo(), void 0 === condString || _type_of(condString), condString.toUpperCase, foo(), void 0 === condString || _type_of(condString), condString.toUpperCase;
@@ -1,25 +1,25 @@
//// [destructuringArrayBindingPatternAndAssignment2.ts]
import _sliced_to_array from "@swc/helpers/src/_sliced_to_array.mjs";
import _to_consumable_array from "@swc/helpers/src/_to_consumable_array.mjs";
var ref = [], ref1 = (_sliced_to_array(ref[0], 1)[0], _sliced_to_array(ref[1], 1));
var ref = (_sliced_to_array([][0], 1)[0], _sliced_to_array([][1], 1));
_sliced_to_array(ref[0], 1)[0];
var _undefined = _sliced_to_array(void 0, 2), ref1 = (_sliced_to_array(_undefined[0], 1)[0], _sliced_to_array(_undefined[1], 1));
_sliced_to_array(ref1[0], 1)[0];
var _undefined = _sliced_to_array(void 0, 2), ref2 = (_sliced_to_array(_undefined[0], 1)[0], _sliced_to_array(_undefined[1], 1));
_sliced_to_array(ref2[0], 1)[0];
var ref3 = _sliced_to_array([
var ref2 = _sliced_to_array([
1,
2,
3
], 3);
ref3[0], ref3[1], ref3[2];
ref2[0], ref2[1], ref2[2];
var temp = [
1,
2,
3
], ref4 = _sliced_to_array(_to_consumable_array(temp), 2);
], ref3 = _sliced_to_array(_to_consumable_array(temp), 2);
ref3[0], ref3[1];
var ref4 = _sliced_to_array(_to_consumable_array(temp), 2);
ref4[0], ref4[1];
var ref5 = _sliced_to_array(_to_consumable_array(temp), 2);
ref5[0], ref5[1];
var ref6 = _sliced_to_array({
var ref5 = _sliced_to_array({
2: !0
}, 3);
ref6[0], ref6[1], ref6[2];
ref5[0], ref5[1], ref5[2];
@@ -1,15 +1,9 @@
//// [destructuringArrayBindingPatternAndAssignment3.ts]
import _sliced_to_array from "@swc/helpers/src/_sliced_to_array.mjs";
var ref = [
var tmp = [
1
];
ref[0], ref[1];
var ref1 = [
1
], tmp = (ref1[0], ref1[1], ref1[2]), e = void 0 === tmp ? e : tmp, ref2 = [
1
];
ref2[0], ref2[1], ref2[2], ref2[3], function(param) {
][2], e = void 0 === tmp ? e : tmp;
!function(param) {
var _param = _sliced_to_array(param, 2);
_param[0], _param[1];
}([
Expand Down
@@ -1,5 +1,4 @@
//// [destructuringControlFlow.ts]
var ref = [
(0, [
"foo"
];
(ref[0], ref[1]).toUpperCase();
][1]).toUpperCase();
Expand Up @@ -15,4 +15,4 @@ var strNumTuple = [
!0,
"foo"
];
strNumTuple[0], strNumTuple[1], strNumTuple[2], strNumTuple[0], strNumTuple[1], strNumTuple["0"], strNumTuple["1"], numTupleTuple[1], numTupleTuple[2], strNumTuple[-1], unionTuple1[0], unionTuple1[1], unionTuple1[2], unionTuple1[0], unionTuple1[1], unionTuple1["0"], unionTuple1["1"], unionTuple2[0], unionTuple2[1], unionTuple2[2], unionTuple2[0], unionTuple2[1], unionTuple2["0"], unionTuple2["1"];
strNumTuple["0"], strNumTuple["1"], numTupleTuple[1], numTupleTuple[2], unionTuple1["0"], unionTuple1["1"], unionTuple2[0], unionTuple2[1], unionTuple2[2], unionTuple2[0], unionTuple2[1], unionTuple2["0"], unionTuple2["1"];
@@ -1,9 +1,6 @@
//// [logicalNotOperatorWithAnyOtherType.ts]
import _class_call_check from "@swc/helpers/src/_class_call_check.mjs";
var M, ANY2 = [
"",
""
], obj1 = {
var M, obj1 = {
x: "",
y: function() {}
}, A = function() {
Expand All @@ -18,4 +15,4 @@ var M, ANY2 = [
M.n = n;
}(M || (M = {}));
var objA = new A();
ANY2[0], obj1.x, obj1.y, objA.a, M.n, A.foo(), ANY2[0], objA.a, M.n;
obj1.x, obj1.y, objA.a, M.n, A.foo(), objA.a, M.n;
@@ -1,9 +1,6 @@
//// [negateOperatorWithAnyOtherType.ts]
import _class_call_check from "@swc/helpers/src/_class_call_check.mjs";
var M, ANY2 = [
"",
""
], obj1 = {
var M, obj1 = {
x: "",
y: function() {}
}, A = function() {
Expand All @@ -18,4 +15,4 @@ var M, ANY2 = [
M.n = n;
}(M || (M = {}));
var objA = new A();
ANY2[0], obj1.x, obj1.y, objA.a, M.n, A.foo(), ANY2[0], objA.a, M.n;
obj1.x, obj1.y, objA.a, M.n, A.foo(), objA.a, M.n;
@@ -1,9 +1,6 @@
//// [plusOperatorWithAnyOtherType.ts]
import _class_call_check from "@swc/helpers/src/_class_call_check.mjs";
var M, ANY2 = [
"",
""
], obj1 = {
var M, obj1 = {
x: function(s) {},
y: function(s1) {}
}, A = function() {
Expand All @@ -18,4 +15,4 @@ var M, ANY2 = [
M.n = n;
}(M || (M = {}));
var objA = new A();
ANY2[0], obj1.x, obj1.y, objA.a, M.n, A.foo(), ANY2[0], objA.a, M.n;
obj1.x, obj1.y, objA.a, M.n, A.foo(), objA.a, M.n;
@@ -1,9 +1,4 @@
//// [typeInferenceWithTupleType.ts]
var combineResult = [
"string",
10
];
combineResult[0], combineResult[1];
var zipResult = function(array1, array2) {
if (array1.length != array2.length) return [
[
Expand Down
@@ -1,9 +1,6 @@
//// [voidOperatorWithAnyOtherType.ts]
import _class_call_check from "@swc/helpers/src/_class_call_check.mjs";
var M, ANY2 = [
"",
""
], obj1 = {
var M, obj1 = {
x: "",
y: 1
}, A = function() {
Expand All @@ -18,4 +15,4 @@ var M, ANY2 = [
M.n = n;
}(M || (M = {}));
var objA = new A();
ANY2[0], obj1.x, obj1.y, objA.a, M.n, A.foo(), ANY2[0], objA.a, M.n;
obj1.x, obj1.y, objA.a, M.n, A.foo(), objA.a, M.n;
1 change: 1 addition & 0 deletions crates/swc_ecma_minifier/src/analyzer/mod.rs
Expand Up @@ -137,6 +137,7 @@ pub(crate) struct VarUsageInfo {
infects: Vec<Access>,

pub used_in_non_child_fn: bool,
/// Only **string** properties.
pub accessed_props: Box<AHashMap<JsWord, u32>>,

pub used_recursively: bool,
Expand Down
4 changes: 4 additions & 0 deletions crates/swc_ecma_minifier/src/analyzer/storage/normal.rs
Expand Up @@ -104,6 +104,10 @@ impl Storage for ProgramData {
e.get_mut().is_fn_local &= var_info.is_fn_local;
e.get_mut().used_in_non_child_fn |= var_info.used_in_non_child_fn;

for (k, v) in *var_info.accessed_props {
*e.get_mut().accessed_props.entry(k).or_default() += v;
}

match kind {
ScopeKind::Fn => {
e.get_mut().is_fn_local = false;
Expand Down
37 changes: 34 additions & 3 deletions crates/swc_ecma_minifier/src/compress/optimize/inline.rs
Expand Up @@ -89,8 +89,42 @@ where
return;
}

let is_inline_enabled =
self.options.reduce_vars || self.options.collapse_vars || self.options.inline != 0;

self.vars.inline_with_multi_replacer(init);

// We inline arrays partially if it's pure (all elements are literal), and not
// modified.
// We don't drop definition, but we just inline array accesses with numeric
// literal key.
//
// TODO: Allow `length` in usage.accessed_props
if usage.declared
&& !usage.reassigned()
&& !usage.mutated
&& !usage.has_property_mutation
&& usage.accessed_props.is_empty()
&& !usage.is_infected()
&& is_inline_enabled
{
if let Expr::Array(arr) = init {
if arr.elems.len() < 32
&& arr.elems.iter().all(|e| match e {
Some(ExprOrSpread { spread: None, expr }) => match &**expr {
Expr::Lit(..) => true,
_ => false,
},
_ => false,
})
{
self.vars
.lits_for_array_access
.insert(ident.to_id(), Box::new(init.clone()));
}
}
}

if !usage.is_fn_local {
match init {
Expr::Lit(..) | Expr::Ident(..) => {}
Expand Down Expand Up @@ -133,9 +167,6 @@ where
self.mode.store(ident.to_id(), &*init);
}

let is_inline_enabled =
self.options.reduce_vars || self.options.collapse_vars || self.options.inline != 0;

// Mutation of properties are ok
if is_inline_enabled
&& usage.declared_count == 1
Expand Down
5 changes: 5 additions & 0 deletions crates/swc_ecma_minifier/src/compress/optimize/mod.rs
Expand Up @@ -246,6 +246,9 @@ struct Vars {
/// https://github.com/swc-project/swc/issues/4415
lits_for_cmp: FxHashMap<Id, Box<Expr>>,

/// This stores [Expr::Array] if all elements are literals.
lits_for_array_access: FxHashMap<Id, Box<Expr>>,

/// Used for copying functions.
///
/// We use this to distinguish [Callee::Expr] from other [Expr]s.
Expand All @@ -271,11 +274,13 @@ impl Vars {
let mut changed = false;
if !self.simple_functions.is_empty()
|| !self.lits_for_cmp.is_empty()
|| !self.lits_for_array_access.is_empty()
|| !self.removed.is_empty()
{
let mut v = Finalizer {
simple_functions: &self.simple_functions,
lits_for_cmp: &self.lits_for_cmp,
lits_for_array_access: &self.lits_for_array_access,
vars_to_remove: &self.removed,
changed: false,
};
Expand Down
27 changes: 20 additions & 7 deletions crates/swc_ecma_minifier/src/compress/optimize/util.rs
Expand Up @@ -164,6 +164,7 @@ impl VisitMut for Remapper {
pub(crate) struct Finalizer<'a> {
pub simple_functions: &'a FxHashMap<Id, Box<Expr>>,
pub lits_for_cmp: &'a FxHashMap<Id, Box<Expr>>,
pub lits_for_array_access: &'a FxHashMap<Id, Box<Expr>>,

pub vars_to_remove: &'a FxHashSet<Id>,

Expand All @@ -183,8 +184,9 @@ impl Parallel for Finalizer<'_> {
impl<'a> Finalizer<'a> {
fn var(&mut self, i: &Id, mode: FinalizerMode) -> Option<Box<Expr>> {
let mut e = match mode {
FinalizerMode::OnlyCallee => self.simple_functions.get(i).cloned()?,
FinalizerMode::OnlyComparisonWithLit => self.lits_for_cmp.get(i).cloned()?,
FinalizerMode::Callee => self.simple_functions.get(i).cloned()?,
FinalizerMode::ComparisonWithLit => self.lits_for_cmp.get(i).cloned()?,
FinalizerMode::MemberAccess => self.lits_for_array_access.get(i).cloned()?,
};

e.visit_mut_children_with(self);
Expand Down Expand Up @@ -215,8 +217,9 @@ impl<'a> Finalizer<'a> {

#[derive(Debug, Clone, Copy)]
enum FinalizerMode {
OnlyCallee,
OnlyComparisonWithLit,
Callee,
ComparisonWithLit,
MemberAccess,
}

impl VisitMut for Finalizer<'_> {
Expand All @@ -226,7 +229,17 @@ impl VisitMut for Finalizer<'_> {
e.visit_mut_children_with(self);

if let Callee::Expr(e) = e {
self.check(e, FinalizerMode::OnlyCallee);
self.check(e, FinalizerMode::Callee);
}
}

fn visit_mut_member_expr(&mut self, e: &mut MemberExpr) {
e.visit_mut_children_with(self);

if let MemberProp::Computed(ref mut prop) = e.prop {
if let Expr::Lit(Lit::Num(..)) = &*prop.expr {
self.check(&mut e.obj, FinalizerMode::MemberAccess);
}
}
}

Expand All @@ -237,9 +250,9 @@ impl VisitMut for Finalizer<'_> {
op!("===") | op!("!==") | op!("==") | op!("!=") => {
//
if e.left.is_lit() {
self.check(&mut e.right, FinalizerMode::OnlyComparisonWithLit);
self.check(&mut e.right, FinalizerMode::ComparisonWithLit);
} else if e.right.is_lit() {
self.check(&mut e.left, FinalizerMode::OnlyComparisonWithLit);
self.check(&mut e.left, FinalizerMode::ComparisonWithLit);
}
}
_ => {}
Expand Down
1 change: 0 additions & 1 deletion crates/swc_ecma_minifier/tests/TODO.txt
Expand Up @@ -3,7 +3,6 @@ arrays/constant_join/input.js
arrays/constant_join_2/input.js
arrays/constant_join_3/input.js
arrays/for_loop/input.js
arrays/index/input.js
arrays/index_length/input.js
arrow/issue_2084/input.js
arrow/issue_2105_1/input.js
Expand Down
12 changes: 3 additions & 9 deletions crates/swc_ecma_minifier/tests/benches-full/echarts.js
Expand Up @@ -28249,10 +28249,7 @@
seriesType: 'candlestick',
plan: createRenderPlanner(),
reset: function(seriesModel) {
var seriesModel1, data, extent, baseAxis, bandWidth, barMaxWidth, barMinWidth, barWidth, coordSys = seriesModel.coordinateSystem, data1 = seriesModel.getData(), candleWidth = (seriesModel1 = seriesModel, data = data1, bandWidth = 'category' === (baseAxis = seriesModel1.getBaseAxis()).type ? baseAxis.getBandWidth() : Math.abs((extent = baseAxis.getExtent())[1] - extent[0]) / data.count(), barMaxWidth = parsePercent$1(retrieve2(seriesModel1.get('barMaxWidth'), bandWidth), bandWidth), barMinWidth = parsePercent$1(retrieve2(seriesModel1.get('barMinWidth'), 1), bandWidth), null != (barWidth = seriesModel1.get('barWidth')) ? parsePercent$1(barWidth, bandWidth) : Math.max(Math.min(bandWidth / 2, barMaxWidth), barMinWidth)), coordDims = [
'x',
'y'
], cDim = data1.mapDimension(coordDims[0]), vDims = data1.mapDimensionsAll(coordDims[1]), openDim = vDims[0], closeDim = vDims[1], lowestDim = vDims[2], highestDim = vDims[3];
var seriesModel1, data, extent, baseAxis, bandWidth, barMaxWidth, barMinWidth, barWidth, coordSys = seriesModel.coordinateSystem, data1 = seriesModel.getData(), candleWidth = (seriesModel1 = seriesModel, data = data1, bandWidth = 'category' === (baseAxis = seriesModel1.getBaseAxis()).type ? baseAxis.getBandWidth() : Math.abs((extent = baseAxis.getExtent())[1] - extent[0]) / data.count(), barMaxWidth = parsePercent$1(retrieve2(seriesModel1.get('barMaxWidth'), bandWidth), bandWidth), barMinWidth = parsePercent$1(retrieve2(seriesModel1.get('barMinWidth'), 1), bandWidth), null != (barWidth = seriesModel1.get('barWidth')) ? parsePercent$1(barWidth, bandWidth) : Math.max(Math.min(bandWidth / 2, barMaxWidth), barMinWidth)), cDim = data1.mapDimension('x'), vDims = data1.mapDimensionsAll('y'), openDim = vDims[0], closeDim = vDims[1], lowestDim = vDims[2], highestDim = vDims[3];
if (data1.setLayout({
candleWidth: candleWidth,
isSimpleBox: candleWidth <= 1.3
Expand Down Expand Up @@ -39656,10 +39653,7 @@
color: '#333'
}
}, VisualMapModel;
}(ComponentModel), DEFAULT_BAR_BOUND = [
20,
140
], ContinuousModel = function(_super) {
}(ComponentModel), ContinuousModel = function(_super) {
function ContinuousModel() {
var _this = null !== _super && _super.apply(this, arguments) || this;
return _this.type = ContinuousModel.type, _this;
Expand All @@ -39671,7 +39665,7 @@
}, ContinuousModel.prototype.resetItemSize = function() {
_super.prototype.resetItemSize.apply(this, arguments);
var itemSize = this.itemSize;
(null == itemSize[0] || isNaN(itemSize[0])) && (itemSize[0] = DEFAULT_BAR_BOUND[0]), (null == itemSize[1] || isNaN(itemSize[1])) && (itemSize[1] = DEFAULT_BAR_BOUND[1]);
(null == itemSize[0] || isNaN(itemSize[0])) && (itemSize[0] = 20), (null == itemSize[1] || isNaN(itemSize[1])) && (itemSize[1] = 140);
}, ContinuousModel.prototype._resetRange = function() {
var dataExtent = this.getExtent(), range = this.option.range;
!range || range.auto ? (dataExtent.auto = 1, this.option.range = dataExtent) : isArray(range) && (range[0] > range[1] && range.reverse(), range[0] = Math.max(range[0], dataExtent[0]), range[1] = Math.min(range[1], dataExtent[1]));
Expand Down

1 comment on commit 66196a6

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: 66196a6 Previous: 5a23949 Ratio
es/full/minify/libraries/antd 1841640549 ns/iter (± 41840828) 1750422067 ns/iter (± 58487204) 1.05
es/full/minify/libraries/d3 389664519 ns/iter (± 24714000) 370949220 ns/iter (± 28859221) 1.05
es/full/minify/libraries/echarts 1426277893 ns/iter (± 98973466) 1349211554 ns/iter (± 40923464) 1.06
es/full/minify/libraries/jquery 104245284 ns/iter (± 7491499) 84487445 ns/iter (± 2366396) 1.23
es/full/minify/libraries/lodash 117558002 ns/iter (± 14531090) 95172666 ns/iter (± 2282393) 1.24
es/full/minify/libraries/moment 54521441 ns/iter (± 3633033) 50107421 ns/iter (± 808699) 1.09
es/full/minify/libraries/react 21008694 ns/iter (± 2057482) 17531787 ns/iter (± 530550) 1.20
es/full/minify/libraries/terser 312194308 ns/iter (± 8065551) 268990172 ns/iter (± 20137024) 1.16
es/full/minify/libraries/three 546550779 ns/iter (± 8327970) 486088546 ns/iter (± 14764534) 1.12
es/full/minify/libraries/typescript 3223708704 ns/iter (± 121689548) 3251334183 ns/iter (± 87930567) 0.99
es/full/minify/libraries/victory 785126384 ns/iter (± 13825658) 777413276 ns/iter (± 30073884) 1.01
es/full/minify/libraries/vue 146274976 ns/iter (± 12902788) 141311679 ns/iter (± 7455873) 1.04
es/full/codegen/es3 36315 ns/iter (± 4210) 34549 ns/iter (± 3586) 1.05
es/full/codegen/es5 35378 ns/iter (± 1811) 34033 ns/iter (± 1732) 1.04
es/full/codegen/es2015 34788 ns/iter (± 1508) 34395 ns/iter (± 904) 1.01
es/full/codegen/es2016 35137 ns/iter (± 1347) 35511 ns/iter (± 4764) 0.99
es/full/codegen/es2017 35512 ns/iter (± 3347) 34441 ns/iter (± 4069) 1.03
es/full/codegen/es2018 35277 ns/iter (± 3084) 34423 ns/iter (± 2399) 1.02
es/full/codegen/es2019 34740 ns/iter (± 1929) 34829 ns/iter (± 3117) 1.00
es/full/codegen/es2020 34868 ns/iter (± 2918) 34033 ns/iter (± 3492) 1.02
es/full/all/es3 234670572 ns/iter (± 20030602) 208791636 ns/iter (± 33755395) 1.12
es/full/all/es5 219644343 ns/iter (± 21161775) 190898736 ns/iter (± 16497226) 1.15
es/full/all/es2015 178679364 ns/iter (± 18021872) 169263450 ns/iter (± 19095085) 1.06
es/full/all/es2016 181711050 ns/iter (± 17808286) 172705126 ns/iter (± 19937958) 1.05
es/full/all/es2017 168858909 ns/iter (± 16892528) 147651273 ns/iter (± 8238689) 1.14
es/full/all/es2018 160223905 ns/iter (± 14409721) 148204267 ns/iter (± 9228689) 1.08
es/full/all/es2019 157448019 ns/iter (± 15273834) 153042850 ns/iter (± 9141838) 1.03
es/full/all/es2020 170758461 ns/iter (± 15970447) 150023391 ns/iter (± 14947060) 1.14
es/full/parser 776783 ns/iter (± 46322) 771167 ns/iter (± 111263) 1.01
es/full/base/fixer 28072 ns/iter (± 4185) 27561 ns/iter (± 1292) 1.02
es/full/base/resolver_and_hygiene 98243 ns/iter (± 6311) 96064 ns/iter (± 6058) 1.02
serialization of ast node 232 ns/iter (± 16) 218 ns/iter (± 3) 1.06
serialization of serde 238 ns/iter (± 5) 224 ns/iter (± 7) 1.06

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.