Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(es/minifier): Inline pure array literal partially #6099

Merged
merged 15 commits into from
Oct 12, 2022
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -1,7 +1 @@
//// [conditionalOperatorConditionIsNumberType.ts]
var array = [
1,
2,
3
];
array[1], array[1], array[1];
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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];
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//// [destructuringControlFlow.ts]
var ref = [
(0, [
"foo"
];
(ref[0], ref[1]).toUpperCase();
][1]).toUpperCase();
Original file line number Diff line number Diff line change
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"];
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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,
kdy1 marked this conversation as resolved.
Show resolved Hide resolved
})
{
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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