Mangle redeclares/reuses the same variable name that's in outer scope
My code was compiled to:
Outermost scope:
(function() {
var e = Math.ceil
, n = String.fromCharCode
, r = Math.min
, l = Math.pow
, o = Math.PI
, h = Number.isInteger
...
Middle scope:
var o, r, l, d, y, m, h, ...
...
h = void 0 === (ve = n.fillMode) ? "both" : ve,
...
Inner scope:
...
0 <= ["forwards", "both"].indexOf(h) ? (()=>(r = h(y) && !n
...
When running in browser, it threw the error:
Uncaught TypeError: h is not a function
(Since this is a private repo I couldn't disclose more of my code)
Can you try with builtIns: false and get back.
I'm sorry. I've already switched to "terser" to do minification. It's difficult to trigger the condition that causes this bug. My code has changed a lot since the report.
Got the same error.
Describe the bug
Function parameter and outer scope variable names mangled into the same literal, hence parameter erroneously shadows variable.
Bug happens when plugin babel-plugin-minify-mangle-names used with option keepFnName: true.
To Reproduce
input code - 110 lines (bug disapperars if further isolation attempts taken)
var scope_0;
(function (scope_0) {
var scope_1;
(function (scope_1) {
SomeFn({
method5: function (param30) {
var _this = this;
var me = this, xVar0 = me.model, xVar1 = [], xVar2 = me.field0;
(function () { return __awaiter(_this, void 0, void 0, function () {
var _a, xVar3, xVar4, xVar5, xVar6, xStart, xVar7, xVar8, xVar9, r, xVar10, c, xVar11, _b, _c;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
if (!!(me.xVar8 && me.xVar7)) return [3, 2];
return [4, scope_0.method0(xVar2, function (field0) { return [field0.field5,
scope_0.method1(field0.field1, scope_1.SomeFn1)
]; })];
case 1:
(_d.sent())(function (field0) {
me.field1 = field0.field5;
var xFields = xVar0.method2(), xFieldNames = me.field4 || xFields.map(function (field) { return field.getName(); });
var xVar19 = scope_1.SomeFn3(xFieldNames, field0.field1);
var xVar8 = me.xVar8 = [], xVar7 = me.xVar7 = [];
for (var i = 0, xCount = xVar19.length; i < xCount; i++) {
var xVar20 = xVar19[i];
if (xVar20 < 0)
continue;
var xName = xFieldNames[i], xField = xFields.find(function (field) { return field.getName() === xName; });
xVar7.push(xName);
xVar8.push(StaticGetter.Get(xField && xField.getType())(xVar2, xVar20));
}
});
return [3, 4];
case 2:
if (!!me.field3) return [3, 4];
_a = me;
return [4, xVar2.field5];
case 3:
_a.field1 = _d.sent();
_d.label = 4;
case 4:
xVar3 = (me.field6 == null) ? me.field1 : Math.min(me.field6, me.field1), xVar4 = 0, xVar5 = xVar3;
xVar6 = param30.getLimit(), xStart = param30.getStart();
if (me.enablePaging && xStart !== undefined && xVar6 !== undefined) {
xVar4 = xStart;
if (xVar4 + xVar6 < xVar5)
xVar5 = xVar4 + xVar6;
}
xVar7 = me.xVar7, xVar8 = me.xVar8, xVar9 = xVar8.length;
Debug.assert(xVar7.length == xVar8.length);
Debug.assert(xVar7.every(function (name) { return !!name; }));
Debug.assert(xVar8.every(function (getter) { return !!getter; }));
r = xVar4;
_d.label = 5;
case 5:
if (!(r < xVar5)) return [3, 11];
xVar10 = {};
c = 0;
_d.label = 6;
case 6:
if (!(c < xVar9)) return [3, 9];
xVar11 = xVar8[c];
_b = xVar10;
_c = xVar7[c];
return [4, xVar11(r)];
case 7:
_b[_c] = _d.sent();
_d.label = 8;
case 8:
c++;
return [3, 6];
}
});
}); })().done(function () {
param30.setSuccessful(true);
}, function (e) {
param30.setException(e);
});
return {
param30: param30
};
}
});
var StaticGetter = (function () {
function StaticGetter() {
}
StaticGetter.string = function (field0, param40) {
var xValue = new Out();
return function (row) {
return field0.method6(row, param40, xValue) ? xValue.value : undefined;
};
};
StaticGetter.auto = function (field0, param40) {
return function (row) {
var xValue = field0.AsVariant(row, param40);
return (xValue !== undefined && xValue !== null) ? xValue : undefined;
};
};
StaticGetter.Get = function (type) {
return this[SomeFn5(type)] || StaticGetter.auto;
};
return StaticGetter;
}());
scope_1.StaticGetter = StaticGetter;
function SomeFn5(type) {
return type ? (typeof type === "string" ? type : type.type) : "auto";
}
scope_1.SomeFn5 = SomeFn5;
})(scope_1 = scope_0.scope_1 || (scope_0.scope_1 = {}));
})(scope_0 || (scope_0 = {}));
Actual Output
output - 86 lines, prettified
var scope_0;
(function(a) {
var b;
(function(b) {
function SomeFn5(a) {
return a ? "string" == typeof a ? a : a.type : "auto"
}
SomeFn({
method5(d) {
var e = this,
f = this,
g = f.model,
h = f.field0;
return function() {
return __awaiter(e, void 0, void 0, function() {
var e, i, j, k, l, m, n, o, p, q, s, t, u, v, w;
return __generator(this, function(c) {
switch (c.label) {
case 0:
return f.xVar8 && f.xVar7 ? [3, 2] : [4, a.method0(h, function(c) {
return [c.field5, a.method1(c.field1, b.SomeFn1)]
})];
case 1:
return c.sent()(function(a) {
f.field1 = a.field5;
for (var d, e = g.method2(), j = f.field4 || e.map(function(a) {
return a.getName()
}), k = b.SomeFn3(j, a.field1), l = f.xVar8 = [], m = f.xVar7 = [], n = 0, o = k.length; n < o; n++)
if (d = k[n], !(0 > d)) {
var p = j[n],
q = e.find(function(a) {
return a.getName() === p
});
m.push(p), l.push(c.Get(q && q.getType())(h, d))
}
}), [3, 4];
case 2:
return f.field3 ? [3, 4] : (e = f, [4, h.field5]);
case 3:
e.field1 = c.sent(), c.label = 4;
case 4:
i = null == f.field6 ? f.field1 : Math.min(f.field6, f.field1), j = 0, k = i, l = d.getLimit(), m = d.getStart(), f.enablePaging && void 0 !== m && void 0 !== l && (j = m, j + l < k && (k = j + l)), n = f.xVar7, o = f.xVar8, p = o.length, Debug.assert(n.length == o.length), Debug.assert(n.every(function(a) {
return !!a
})), Debug.assert(o.every(function(a) {
return !!a
})), q = j, c.label = 5;
case 5:
if (!(q < k)) return [3, 11];
s = {}, t = 0, c.label = 6;
case 6:
return t < p ? (u = o[t], v = s, w = n[t], [4, u(q)]) : [3, 9];
case 7:
v[w] = c.sent(), c.label = 8;
case 8:
return t++, [3, 6];
}
})
})
}().done(function() {
d.setSuccessful(!0)
}, function(a) {
d.setException(a)
}), {
param30: d
}
}
});
var c = function() {
function StaticGetter() {}
return StaticGetter.string = function(a, b) {
var c = new Out;
return function(d) {
return a.method6(d, b, c) ? c.value : void 0
}
}, StaticGetter.auto = function(a, b) {
return function(c) {
var d = a.AsVariant(c, b);
return void 0 !== d && null !== d ? d : void 0
}
}, StaticGetter.Get = function(a) {
return this[SomeFn5(a)] || StaticGetter.auto
}, StaticGetter
}();
b.StaticGetter = c, b.SomeFn5 = SomeFn5
})(b = a.scope_1 || (a.scope_1 = {}))
})(scope_0 || (scope_0 = {}));
Names `_d` and `StaticGetter` are translated into `c`:
line 17: return __generator(this, function(c) {
line 86: var c = function() {
because of this shadowing call c.Get fails in runtime:
line 34: m.push(p), l.push(c.Get(q && q.getType())(h, d))
Expected Output
Names StaticGetter and _d are translated into different names in output.
Configuration
from package.json:
"@babel/core": "^7.2.2",
"@babel/types": "^7.2.2",
"@babel/preset-env": "^7.2.2",
"babel-preset-minify": "^0.5.0",
"gulp": "^3.9.1",
"gulp-babel": "^8.0.0",
minify config:
{
"builtIns": false,
"mangle": { keepFnName: true }
}
gulp task
function () {
return gulp.src(SRC)
.pipe(babel({ // @see https://github.com/babel/gulp-babel
generatorOpts: {
jsescOption: {
minimal: true // @see https://github.com/babel/babel/pull/8143
}
},
presets: [
[
"minify", {
"builtIns": false,
"mangle": { /*keepClassName: true,*/ keepFnName: true }
}
]
],
comments: false
}))
.on("error", function (error) { console.log(error); })
.pipe(gulp.dest(DEST_DIR));
}