Skip to content

Commit

Permalink
feat(es/minifier): Compress negate eq (#9911)
Browse files Browse the repository at this point in the history
**Description:**

This should be fine since Google Closure Compiler does the same compress.
  • Loading branch information
Austaras authored Jan 22, 2025
1 parent 5b5c87e commit e8f23cf
Show file tree
Hide file tree
Showing 23 changed files with 181 additions and 96 deletions.
6 changes: 6 additions & 0 deletions .changeset/moody-laws-explain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
swc_core: minor
swc_ecma_minifier: minor
---

fix(es/minifier): Compress negate eq
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
//// [typeGuardRedundancy.ts]
var x;
"string" == typeof x && "string" == typeof x ? x.substr : x.toFixed, "string" == typeof x && "string" == typeof x ? x.substr : x.toFixed, "string" == typeof x || "string" == typeof x ? x.substr : x.toFixed, "string" == typeof x || "string" == typeof x ? x.substr : x.toFixed;
"string" == typeof x && "string" == typeof x ? x.substr : x.toFixed, "string" != typeof x || "string" != typeof x ? x.toFixed : x.substr, "string" == typeof x || "string" == typeof x ? x.substr : x.toFixed, "string" != typeof x && "string" != typeof x ? x.toFixed : x.substr;
2 changes: 1 addition & 1 deletion crates/swc/tests/vercel/full/utf8-1/output/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ function p() {
return t(this, function(t) {
switch(t.label){
case 0:
if (!(_ !== __webpack_hash__) || 'idle' !== module.hot.status()) return [
if (_ === __webpack_hash__ || 'idle' !== module.hot.status()) return [
2
];
t.label = 1;
Expand Down
77 changes: 77 additions & 0 deletions crates/swc_ecma_minifier/src/compress/pure/bools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,83 @@ impl Pure<'_> {
}
}

pub(super) fn optimize_negate_eq(&mut self, e: &mut Expr) {
fn is_eq(op: BinaryOp) -> bool {
matches!(op, op!("==") | op!("===") | op!("!=") | op!("!=="))
}

fn can_absorb_negate(e: &Expr) -> bool {
match e {
Expr::Lit(_) => true,
Expr::Bin(BinExpr {
op: op!("&&") | op!("||"),
left,
right,
..
}) => can_absorb_negate(left) && can_absorb_negate(right),
Expr::Bin(BinExpr { op, .. }) if is_eq(*op) => true,
Expr::Unary(UnaryExpr {
op: op!("!"), arg, ..
}) => arg.get_type() == Value::Known(Type::Bool),
_ => false,
}
}

fn negate_eq(op: BinaryOp) -> BinaryOp {
match op {
op!("==") => op!("!="),
op!("!=") => op!("=="),
op!("===") => op!("!=="),
op!("!==") => op!("==="),
_ => unreachable!(),
}
}

if !self.options.bools {
return;
}

let Expr::Unary(UnaryExpr {
op: op!("!"), arg, ..
}) = e
else {
return;
};

let arg_can_negate = can_absorb_negate(arg);

match &mut **arg {
Expr::Bin(BinExpr { op, .. }) if is_eq(*op) => {
self.changed = true;
report_change!("bools: Optimizing `!(a == b)` as `a != b`");

*op = negate_eq(*op);

*e = *arg.take();
}
Expr::Bin(BinExpr {
op: op @ (op!("&&") | op!("||")),
left,
right,
..
}) if arg_can_negate => {
self.changed = true;
report_change!("bools: Optimizing `!(a == b && c == d)` as `a != b`");

*op = match op {
op!("&&") => op!("||"),
op!("||") => op!("&&"),
_ => unreachable!(),
};

self.negate(left, false, false);
self.negate(right, false, false);
*e = *arg.take();
}
_ => (),
}
}

pub(super) fn compress_cmp_with_long_op(&mut self, e: &mut BinExpr) {
fn should_optimize(l: &Expr, r: &Expr, opts: &CompressOptions) -> bool {
match (l, r) {
Expand Down
2 changes: 2 additions & 0 deletions crates/swc_ecma_minifier/src/compress/pure/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,8 @@ impl VisitMut for Pure<'_> {
debug_assert_valid(e);
}

self.optimize_negate_eq(e);

self.lift_minus(e);
self.optimize_to_number(e);

Expand Down
14 changes: 7 additions & 7 deletions crates/swc_ecma_minifier/tests/benches-full/echarts.js
Original file line number Diff line number Diff line change
Expand Up @@ -5824,7 +5824,7 @@
this.path = new PathProxy(!1);
}, Path.prototype.hasStroke = function() {
var style = this.style, stroke = style.stroke;
return !(null == stroke || 'none' === stroke || !(style.lineWidth > 0));
return null != stroke && 'none' !== stroke && style.lineWidth > 0;
}, Path.prototype.hasFill = function() {
var fill = this.style.fill;
return null != fill && 'none' !== fill;
Expand Down Expand Up @@ -11865,7 +11865,7 @@
var inner = makeInner();
return function(seriesModel) {
var fields = inner(seriesModel), pipelineContext = seriesModel.pipelineContext, originalLarge = !!fields.large, originalProgressive = !!fields.progressiveRender, large = fields.large = !!(pipelineContext && pipelineContext.large), progressive = fields.progressiveRender = !!(pipelineContext && pipelineContext.progressiveRender);
return !!(originalLarge !== large || originalProgressive !== progressive) && 'reset';
return (originalLarge !== large || originalProgressive !== progressive) && 'reset';
};
}
enableClassExtend(ComponentView), enableClassManagement(ComponentView);
Expand Down Expand Up @@ -13497,7 +13497,7 @@
var pathProxyForDraw = new PathProxy(!0);
function styleHasStroke(style) {
var stroke = style.stroke;
return !(null == stroke || 'none' === stroke || !(style.lineWidth > 0));
return null != stroke && 'none' !== stroke && style.lineWidth > 0;
}
function styleHasFill(style) {
var fill = style.fill;
Expand Down Expand Up @@ -15506,7 +15506,7 @@
function applyElementStates(el) {
for(var newStates = [], oldStates = el.currentStates, i = 0; i < oldStates.length; i++){
var stateName = oldStates[i];
'emphasis' === stateName || 'blur' === stateName || 'select' === stateName || newStates.push(stateName);
'emphasis' !== stateName && 'blur' !== stateName && 'select' !== stateName && newStates.push(stateName);
} // Only use states when it's exists.
el.selected && el.states.select && newStates.push('select'), 2 === el.hoverState && el.states.emphasis ? newStates.push('emphasis') : 1 === el.hoverState && el.states.blur && newStates.push('blur'), el.useStates(newStates);
}
Expand Down Expand Up @@ -16739,7 +16739,7 @@
if (coordDim) {
assert(null == VISUAL_DIMENSIONS.get(coordDim));
var dimType, coordDimIndex = dimItem.coordDimIndex;
getOrCreateEncodeArr(encode, coordDim)[coordDimIndex] = dimName, dimItem.isExtraCoord || (notExtraCoordDimMap.set(coordDim, 1), 'ordinal' === (dimType = dimItem.type) || 'time' === dimType || (defaultedLabel[0] = dimName), // And it only has index. User can use index to retrieve value from the raw item array.
getOrCreateEncodeArr(encode, coordDim)[coordDimIndex] = dimName, dimItem.isExtraCoord || (notExtraCoordDimMap.set(coordDim, 1), 'ordinal' !== (dimType = dimItem.type) && 'time' !== dimType && (defaultedLabel[0] = dimName), // And it only has index. User can use index to retrieve value from the raw item array.
getOrCreateEncodeArr(userOutput.encode, coordDim)[coordDimIndex] = dimItem.index), dimItem.defaultTooltip && defaultedTooltip.push(dimName);
}
VISUAL_DIMENSIONS.each(function(v, otherDim) {
Expand Down Expand Up @@ -34278,7 +34278,7 @@
var width, height, xAxisExtent, yAxisExtent, coordSys = seriesModel.coordinateSystem;
if (isCoordinateSystemType(coordSys, 'cartesian2d')) {
var xAxis = coordSys.getAxis('x'), yAxis = coordSys.getAxis('y');
if (!('category' === xAxis.type && 'category' === yAxis.type)) throw Error('Heatmap on cartesian must have two category axes');
if ('category' !== xAxis.type || 'category' !== yAxis.type) throw Error('Heatmap on cartesian must have two category axes');
if (!(xAxis.onBand && yAxis.onBand)) throw Error('Heatmap on cartesian must have two axes with boundaryGap true');
width = xAxis.getBandWidth(), height = yAxis.getBandWidth(), xAxisExtent = xAxis.scale.getExtent(), yAxisExtent = yAxis.scale.getExtent();
}
Expand Down Expand Up @@ -36370,7 +36370,7 @@
}
}
if (!isInit && elPropsInAttr // Just ignore shape animation in morphing.
&& !(null != morphFromEl && 'shape' === mainAttr)) {
&& (null == morphFromEl || 'shape' !== mainAttr)) {
if (attrOpt.transition) {
transFromPropsInAttr || (transFromPropsInAttr = transFromProps[mainAttr] = {});
for(var transitionKeys = normalizeToArray(attrOpt.transition), i = 0; i < transitionKeys.length; i++){
Expand Down
2 changes: 1 addition & 1 deletion crates/swc_ecma_minifier/tests/benches-full/jquery.js
Original file line number Diff line number Diff line change
Expand Up @@ -2321,7 +2321,7 @@
!("click" === event.type && event.button >= 1)) {
for(; cur !== this; cur = cur.parentNode || this)// Don't check non-elements (#13208)
// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
if (1 === cur.nodeType && !("click" === event.type && !0 === cur.disabled)) {
if (1 === cur.nodeType && ("click" !== event.type || !0 !== cur.disabled)) {
for(i = 0, matchedHandlers = [], matchedSelectors = {}; i < delegateCount; i++)void 0 === matchedSelectors[// Don't conflict with Object.prototype properties (#13203)
sel = (handleObj = handlers[i]).selector + " "] && (matchedSelectors[sel] = handleObj.needsContext ? jQuery(sel, this).index(cur) > -1 : jQuery.find(sel, this, null, [
cur
Expand Down
2 changes: 1 addition & 1 deletion crates/swc_ecma_minifier/tests/benches-full/lodash.js
Original file line number Diff line number Diff line change
Expand Up @@ -3097,7 +3097,7 @@
* @returns {Function} Returns the new relational operation function.
*/ function createRelationalOperation(operator) {
return function(value, other) {
return 'string' == typeof value && 'string' == typeof other || (value = toNumber(value), other = toNumber(other)), operator(value, other);
return ('string' != typeof value || 'string' != typeof other) && (value = toNumber(value), other = toNumber(other)), operator(value, other);
};
}
/**
Expand Down
6 changes: 3 additions & 3 deletions crates/swc_ecma_minifier/tests/benches-full/react.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@
* @final
* @protected
*/ Component.prototype.setState = function(partialState, callback) {
if (!('object' == typeof partialState || 'function' == typeof partialState || null == partialState)) throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");
if ('object' != typeof partialState && 'function' != typeof partialState && null != partialState) throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");
this.updater.enqueueSetState(this, partialState, callback, 'setState');
}, /**
* Forces an update. This should only be invoked when it is known with
Expand Down Expand Up @@ -381,7 +381,7 @@
* Clone and return a new ReactElement using element as the starting point.
* See https://reactjs.org/docs/react-api.html#cloneelement
*/ function cloneElement(element, config, children) {
if (!(null != element)) throw Error("React.cloneElement(...): The argument must be a React element, but you passed " + element + ".");
if (null == element) throw Error("React.cloneElement(...): The argument must be a React element, but you passed " + element + ".");
var propName, defaultProps, props = _assign({}, element.props), key = element.key, ref = element.ref, self = element._self, source = element._source, owner = element._owner; // Reserved names are extracted
if (null != config) for(propName in hasValidRef(config) && (// Silently steal the ref from the parent.
ref = config.ref, owner = ReactCurrentOwner.current), hasValidKey(config) && (key = '' + config.key), element.type && element.type.defaultProps && (defaultProps = element.type.defaultProps), config)hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName) && (void 0 === config[propName] && void 0 !== defaultProps ? // Resolve default props
Expand Down Expand Up @@ -516,7 +516,7 @@
}
function resolveDispatcher() {
var dispatcher = ReactCurrentDispatcher.current;
if (!(null !== dispatcher)) throw Error("Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:\n1. You might have mismatching versions of React and the renderer (such as React DOM)\n2. You might be breaking the Rules of Hooks\n3. You might have more than one copy of React in the same app\nSee https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.");
if (null === dispatcher) throw Error("Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:\n1. You might have mismatching versions of React and the renderer (such as React DOM)\n2. You might be breaking the Rules of Hooks\n3. You might have more than one copy of React in the same app\nSee https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.");
return dispatcher;
}
// Helpers to patch console.logs to avoid logging during side-effect free
Expand Down
2 changes: 1 addition & 1 deletion crates/swc_ecma_minifier/tests/benches-full/vue.js
Original file line number Diff line number Diff line change
Expand Up @@ -4355,7 +4355,7 @@
// For a node to qualify as a static root, it should have children that
// are not just static text. Otherwise the cost of hoisting out will
// outweigh the benefits and it's better off to just always render it fresh.
if ((node.static || node.once) && (node.staticInFor = isInFor), node.static && node.children.length && !(1 === node.children.length && 3 === node.children[0].type)) {
if ((node.static || node.once) && (node.staticInFor = isInFor), node.static && node.children.length && (1 !== node.children.length || 3 !== node.children[0].type)) {
node.staticRoot = !0;
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1439,7 +1439,7 @@
return element.decorators && element.decorators.length;
}
function _isDataDescriptor(desc) {
return void 0 !== desc && !(void 0 === desc.value && void 0 === desc.writable);
return void 0 !== desc && (void 0 !== desc.value || void 0 !== desc.writable);
}
function _defineClassElement(receiver, element) {
var descriptor = element.descriptor;
Expand Down Expand Up @@ -11253,7 +11253,7 @@
}
function handlePopState(event) {
// Ignore extraneous popstate events in WebKit.
void 0 === event.state && -1 === navigator.userAgent.indexOf("CriOS") || handlePop(getDOMLocation(event.state));
(void 0 !== event.state || -1 !== navigator.userAgent.indexOf("CriOS")) && handlePop(getDOMLocation(event.state));
}
function handleHashChange() {
handlePop(getDOMLocation(getHistoryState()));
Expand Down Expand Up @@ -12481,7 +12481,7 @@
function ea(a, b) {
for(ca[a] = b, a = 0; a < b.length; a++)ba.add(b[a]);
}
var fa = !("undefined" == typeof window || void 0 === window.document || void 0 === window.document.createElement), ha = /^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/, ia = Object.prototype.hasOwnProperty, ja = {}, ka = {};
var fa = "undefined" != typeof window && void 0 !== window.document && void 0 !== window.document.createElement, ha = /^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/, ia = Object.prototype.hasOwnProperty, ja = {}, ka = {};
function B(a, b, c, d, e, f, g) {
this.acceptsBooleans = 2 === b || 3 === b || 4 === b, this.attributeName = d, this.attributeNamespace = e, this.mustUseProperty = c, this.propertyName = a, this.type = b, this.sanitizeURL = f, this.removeEmptyString = g;
}
Expand Down Expand Up @@ -12810,7 +12810,7 @@
function cb(a, b, c) {
if (b.hasOwnProperty("value") || b.hasOwnProperty("defaultValue")) {
var d = b.type;
if (!("submit" !== d && "reset" !== d || void 0 !== b.value && null !== b.value)) return;
if (("submit" === d || "reset" === d) && (void 0 === b.value || null === b.value)) return;
b = "" + a._wrapperState.initialValue, c || b === a.value || (a.value = b), a.defaultValue = b;
}
"" !== (c = a.name) && (a.name = ""), a.defaultChecked = !!a._wrapperState.initialChecked, "" !== c && (a.name = c);
Expand Down Expand Up @@ -13071,7 +13071,7 @@
case "onMouseUp":
case "onMouseUpCapture":
case "onMouseEnter":
(d = !d.disabled) || (d = !("button" === (a = a.type) || "input" === a || "select" === a || "textarea" === a)), a = !d;
(d = !d.disabled) || (d = "button" !== (a = a.type) && "input" !== a && "select" !== a && "textarea" !== a), a = !d;
break;
default:
a = !1;
Expand Down Expand Up @@ -19173,7 +19173,7 @@
/***/ 97044: /***/ function(module) {
"use strict";
module.exports = (string, separator)=>{
if (!("string" == typeof string && "string" == typeof separator)) throw TypeError("Expected the arguments to be of type `string`");
if ("string" != typeof string || "string" != typeof separator) throw TypeError("Expected the arguments to be of type `string`");
if ("" === separator) return [
string
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7068,7 +7068,7 @@
}
ho(t, e) {
return(// Always raise the first event when we're synced
!t.fromCache || (!this.options.fo || !("Offline" /* Offline */ !== e)) && (!t.docs.isEmpty() || "Offline" /* Offline */ === e));
!t.fromCache || (!this.options.fo || "Offline" /* Offline */ === e) && (!t.docs.isEmpty() || "Offline" /* Offline */ === e));
// Raise data from cache if we have any documents or we are offline
}
uo(t) {
Expand Down
Loading

0 comments on commit e8f23cf

Please sign in to comment.