This repository was archived by the owner on Feb 12, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 412
Leaking of __empty variable and highly inefficient optimization #2573
Copy link
Copy link
Open
Labels
Description
Whilst working with React children, one of the operations we do is "flatten" arrays. This is a very common use-case in JS applications and it seems to be one that Prepack struggles to do when the abstract contains abstract elements/lengths.
Take the below case:
(function() {
function makeArr(x) {
var arr = [1, 2, 3];
if (x) {
arr.push(4);
} else {
arr.pop();
}
return arr;
}
function recursivelyFlatten(arr, baseArr) {
for (var i = 0; i < arr.length; i++) {
var elem = arr[i];
if (Array.isArray(elem)) {
recursivelyFlatten(elem, baseArr);
} else {
baseArr.push(elem);
}
}
}
function flatten(arr) {
var baseArr = [];
recursivelyFlatten(arr, baseArr);
return baseArr;
}
global.fn = function fn(x) {
var arr1 = makeArr(x);
var arr2 = makeArr(x);
return flatten([arr1, arr2]);
};
global.__optimize && __optimize(fn);
})();The output for this is:
(function () {
var _$Q = this;
var _1 = function (x) {
var __get_scope_binding_0 = function (__selector) {
var __captured;
switch (__selector) {
case 0:
case 1:
__captured = [void 0, void 0];
break;
}
__scope_0[__selector] = __captured;
return __captured;
};
var __scope_0 = new Array(2);
var __leaked_6, __leaked_7, __leaked_4, __leaked_5;
var _W = function () {
var __captured__scope_1 = __scope_0[0] || __get_scope_binding_0(0);
for (__captured__scope_1[0] = 0; __captured__scope_1[0] < __leaked_6.length; __captured__scope_1[0]++) {
__captured__scope_1[1] = __leaked_6[__captured__scope_1[0]];
if (Array.isArray(__captured__scope_1[1])) {
_Z(__captured__scope_1[1], __leaked_7);
} else {
__leaked_7.push(__captured__scope_1[1]);
}
}
};
var _X = function () {
var __captured__scope_2 = __scope_0[1] || __get_scope_binding_0(1);
for (__captured__scope_2[0] = 0; __captured__scope_2[0] < __leaked_4.length; __captured__scope_2[0]++) {
__captured__scope_2[1] = __leaked_4[__captured__scope_2[0]];
if (Array.isArray(__captured__scope_2[1])) {
_Z(__captured__scope_2[1], __leaked_5);
} else {
__leaked_5.push(__captured__scope_2[1]);
}
}
};
var _2 = [1, 2,,,];
_2.length = x ? 4 : 2;
_2[0] = 1;
_2[1] = 2;
var _5 = x ? 3 : __empty;
if (x) _2[2] = 3;else delete _2[2];
var _9 = x ? 4 : __empty;
if (x) _2[3] = 4;else delete _2[3];
__leaked_6 = _2;
var _I = [,,];
_I.length = 0;
delete _I[0];
delete _I[1];
__leaked_7 = _I;
var _$0 = _W();
var _M = [1, 2,,,];
_M.length = x ? 4 : 2;
_M[0] = 1;
_M[1] = 2;
if (x) _M[2] = 3;else delete _M[2];
if (x) _M[3] = 4;else delete _M[3];
__leaked_4 = _M;
__leaked_5 = _I;
var _$5 = _X();
return _I;
};
var _Z = function (arr, baseArr) {
for (var i = 0; i < arr.length; i++) {
var elem = arr[i];
if (Array.isArray(elem)) {
_Z(elem, baseArr);
} else {
baseArr.push(elem);
}
}
};
var __empty = {};
_$Q.fn = _1;
}).call(this);You can see we have __empty serialized out above, which accordingly to @NTillmann, should never happen.
The output that Prepack creates is not optimal at all and definitely will hit the slow paths in all JS VMs. Ideally what we should be outputting in the base-case is this:
function _1(x) {
var _$1 = x ? [3, 4] : [];
var _$2 = x ? [3, 4] : [];
return [1, 2, ..._$0, 1, 2, ..._$1];
}Reactions are currently unavailable