madskristensen / BundlerMinifier

Visual Studio extension
Other
611 stars 171 forks source link

Boolean Logic using arrow function with && gets minified to || with some logic inversed. #599

Open pitus opened 1 year ago

pitus commented 1 year ago

Describe the bug I have some code that's calculating a boolean expression based on multiple negated tests that are && together. Once minified, the logic is converted to || while negation is removed from all but the last boolean component. If it was optimized using De Morgan's law then it's missing an extra ! in front that should negate the entire expression but it also shouldn't leave the ! in front of the last element ... Instead of converting !A && !B && !C to !(A || B || C) it converts it to A || B || !C which is a serious problem as I may have many instances of similar logic in my code and I can no longer trust that the minifier didn't mess up any of it.

To Reproduce Steps to reproduce the behavior:

  1. Create a Test.js file
  2. Add the following code to it: var a = true; var b = true; var bug = () => !a && !b; Or simply () => !a && !b;
  3. Have it minified to Test.min.js by the Bundler and Minifier (if not automatically done so based on bundleconfig.json)
  4. Review the Text.min.js file, it now contains an invalid code (messed up boolean logic) var a=!0,b=!0,bug=()=>a||!b; Or simply ()=>a||!b;

Expected behavior The content of Test.min.js should have an extra !(_expression_) added and no ! on the last element: var a=!0,b=!0,bug=()=>!(a||b); Or it should have left it unchanged as: var a=!0,b=!0,bug=()=>!a&&!b;

Observation This seems to only happen when there's an arrow function involved in the logic. So var bug = !a && !b; compiles correctly to bug=!a&&!b; Also, it only happens when all but the last element are negated using ! - the last one doesn't have to be negated, it'll simply be copied as is, but if any of the expressions prior to it are not negated, the whole thing is compiled correctly. Interesting test: () => !!!a || !b; gets compiled as ()=>!!a&&!b; so it simply removes one of the ! in all but last element. Another simple example that fails: !a || !b; gets compiled as a&&!b; and !a && !b gets compiled as a||!b so it's not just with arrow functions after all - but it works correctly when it's an expression returned from a regular function or class method: function x( a, b ) { return !a && !b; } gets compiled correctly as function x(n,t){return!n&&!t}

Same problem when using || initially When the original expression is () => !a || !b; It gets compiled to ()=>a&&!b; So the Minifier seems to have an issue with boolean expression elements that are negated within an arrow function.

Additional context () => !a && b; gets compiled to ()=>a||b;

Work-around I had to convert my expression using De Morgan's law manually so that it doesn't attempt to do this on its own. So if I enter in my original source the following: var a = true; var b = true; var bug = () => !(a || b); It gets properly minimized as: var a=!0,b=!0,bug=()=>!(a||b);

Note that if there are more than 2 elements, only the last one has the ! in front of it. I've also seen it compile with no ! left at the last element, but I can't reproduce that anymore (don't remember the exact code).