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

For loops declaration sometimes shadows loop condition variables #943

Open LinusU opened 5 years ago

LinusU commented 5 years ago

Describe the bug

When outputting for loops, it seems like sometimes the loop initializer will shadow outside variables that are also used in the loop condition. e.g.

const e = 100
for (let r = 0, e; r < e; r++) {
  e = 'foobar'
}

I'm actually unsure on what the JavaScript spec says that the e in the loop condition (r < e) should refer to, and it seems like Safari are referring to the e created by the let statement in the loop initializer, whereas Chrome is referring to the outer e created by the const declaration.

This is causing the provided code to fail in Safari for me (the loop won't run and the function will return an empty string), so it would be great if babel-minify could avoid shadowing outer variables that are used in the loop condidtion ☺️

To Reproduce

Minimal code to reproduce the bug

window.decodeUtf8 = function(e) {
  const t = function(e) {
          if (e instanceof Uint8Array) return e;
          if (e instanceof ArrayBuffer) return new Uint8Array(e);
          throw new TypeError('Expected "input" to be an ArrayBuffer or Uint8Array')
      }(e),
      r = t.length;
  let n = "";
  for (let e = 0; e < r; e++) {
      let r = t[e];
      if (r < 128) n += String.fromCodePoint(r);
      else if (192 != (224 & r))
          if (224 != (240 & r))
              if (240 != (248 & r));
              else {
                  let o = 63 & t[++e],
                      i = 63 & t[++e],
                      f = 63 & t[++e];
                  n += String.fromCodePoint((7 & r) << 18 | o << 12 | i << 6 | f)
              }
      else {
          let o = 63 & t[++e],
              i = 63 & t[++e];
          n += String.fromCodePoint((15 & r) << 12 | o << 6 | i)
      } else {
          let o = 63 & t[++e];
          n += String.fromCodePoint((31 & r) << 6 | o)
      }
  }
  return n
}

Actual Output

If there is no Error thrown,

window.decodeUtf8=function(a){var b=String.fromCodePoint;const c=function(a){if(a instanceof Uint8Array)return a;if(a instanceof ArrayBuffer)return new Uint8Array(a);throw new TypeError("Expected \"input\" to be an ArrayBuffer or Uint8Array")}(a),d=c.length;let g="";for(let d,h=0;h<d;h++)if(d=c[h],128>d)g+=b(d);else if(192==(224&d)){let a=63&c[++h];g+=b((31&d)<<6|a)}else if(224==(240&d)){let a=63&c[++h],e=63&c[++h];g+=b((15&d)<<12|a<<6|e)}else if(240!=(248&d));else{let a=63&c[++h],e=63&c[++h],i=63&c[++h];g+=b((7&d)<<18|a<<12|e<<6|i)}return g};

relevant part, deminified:

    const c = /* ... */,
        d = c.length;
    let g = "";
    for (let d, h = 0; h < d; h++)
        if (d = c[h], 128 > d) g += b(d);

Expected Output

I would have expected to not reuse the same outer and inner variable name:

    const c = /* ... */,
        d = c.length;
    let g = "";
    for (let X, h = 0; h < d; h++)
        if (X = c[h], 128 > X) g += b(X);

Configuration

How are you using babel-minify?

babel-preset-minify in babelrc

babel-minify version: 0.5.0

babel version : 7.2.3

babel-minify-config:

{}

babelrc:

{
  "presets": ["minify"],
  "comments": false
}
LinusU commented 5 years ago

Created a repo with minimal reproduction 👉 LinusU/babel-minify-for-loop-issue