babel / minify

:scissors: An ES6+ aware minifier based on the Babel toolchain (beta)
https://babeljs.io/repl
MIT License
4.39k stars 225 forks source link

remove subsequent nodes after 100% truthy condition #917

Open msn0 opened 6 years ago

msn0 commented 6 years ago

Fixes #898

Cyp commented 6 years ago

This version unconditionally eliminates anything after the if, even if there is no return; statement.

function f() {
  if (true) {
  }
  console.log('hi');
}
// With patched deadcode
"use strict";function f(){}
msn0 commented 6 years ago

What a fail 😄 I'll fix it.

Cyp commented 6 years ago

That seems better. It would be good if it could also handle this case somehow:

var v = 43;

function s() {
  s.func = func;
  if (true) {
    return;
  }
  console.log('no-hi');

  var v = (console.log('no-hi2'), 42);
  function func() {
    v = (v | 0) + 1;
    console.log('v is ' + v);
  }
  console.log('test2');
}

s();
s.func();

What happens:

"use strict";var v=43;function s(){s.func=func;function func(){v=(v|0)+1;console.log("v is "+v)}}s();s.func();
// prints v is 44

What should happen:

"use strict";var v=43;function s(){s.func=func;var v;function func(){v=(v|0)+1;console.log("v is "+v)}}s();s.func();
// prints v is 1

Should probably work with {var v} as well as just var v.

msn0 commented 6 years ago

ok, looks like all declarations should be hoisted somehow. I need to figure out how to do it ;)

msn0 commented 6 years ago

@Cyp could you please take a look in a meanwhile?

Cyp commented 6 years ago

It seems to work now, although some var declarations can be a bit redundant:

var g;
function a() {
  var h = ' ';
  g += h + i + j + k + l + m + n;
  if (true) {
    return;
    var i = 1;
  } else {
    var j = 2;
  }
  var k = 3;
  var l = 4;
  while (true) {
    var m = 5;
    if (false) {
      var n = 6;
    }
  }
}
"use strict";var g;function a(){var h=" ";g+=h+i+j+k+l+m+n;var i=1;var j;var h,i,j,k,l,m,n;
var h,i,j,h,i,j,k,l,m,n,l,m,n}

The function a() does declare all variables at least once and correctly set g = 'undefined undefinedundefinedundefinedundefinedundefinedundefined', so it's not wrong.

Swapping the if (true) A; else B; to if (false) B; else A;:

var g;
function a() {
  var h = ' ';
  g += h + i + j + k + l + m + n;
  if (false) {
    var j = 2;
  } else {
    return;
    var i = 1;
  }
  var k = 3;
  var l = 4;
  while (true) {
    var m = 5;
    if (false) {
      var n = 6;
    }
  }
}
"use strict";var g;function a(){g+=" "+void 0+void 0+void 0+void 0+m+n}

It inlines most of the vars, but misses m and n.

msn0 commented 6 years ago

With your last example it looks bad imho. var i should not be initialised with 1 and these redundant variable declarations should not happen.

msn0 commented 6 years ago

@Cyp I believe I've covered your recent examples ;) Could you please take a look?

Cyp commented 6 years ago

Getting harder to find testcases… Another case — a1 is similar to before, but with ++i instead of something = i. Not sure whether a2 and a3 fall into the scope of this patch, but including in case they're similar.

'use strict';
function a1() {
  ++i;
  if (true) {
    return;
    var i = meow();
  }
}

function a2() {
  ++i;
  while (false) {
    var i = meow();
  }
}

function a3() {
  for (++i; false; meow()) {
    var i = meow();
  }
}
"use strict";function a1(){++i;var i=meow()}function a2(){++i}function a3(){++i}
msn0 commented 6 years ago

@Cyp a1 seems to be fixed right now. a2 and a3 looks similar nevertheless it's not related to ifStatement. Anyway I'll try to look on it - maybe there's some simple fix for these.

msn0 commented 6 years ago

I'll create a separate issue(s) for a2 and a3 cases. I think it's unrelated to IfStatement. I've got a fix already for a2, rather simple.

Cyp commented 6 years ago

Not finding any remaining issues.

Not an issue (and nothing to do with the patch), but this isn't fully simplified:

function d1(x) {
  if (x) ; else ;
  if (x) {} else ;
  if (x) ; else {}
  if (x) {} else {}
}
"use strict";function d1(x){if(x);else;if(x){}else;if(x);if(x){}}
msn0 commented 6 years ago

@boopathi please take a look. Should I change something or abandon it?

coreyfarrell commented 5 years ago

What about throw? Examples:

function f1() {
  if (true) {
   throw new Error('throw');
  }

  return 'unreachable';
}

function f2() {
  if (true) {
    throw new Error('throw');
  } else {
    return 'return';
  }

  return 'unreachable';
}