facebookarchive / prepack

A JavaScript bundle optimizer.
http://prepack.io
Other
14.22k stars 424 forks source link

Eliminate unnecessary object allocations for residual functions #495

Open NTillmann opened 7 years ago

NTillmann commented 7 years ago

Prepacking

function() {
    function f() {
        let obj = { f: 42 };
        return function() { return obj.f; }
    }
    g = f();
})();

currently yields the following:

(function () {
    var _1 = {
        f: 42
    };

    function _0() {
        return _1.f;
    }

    g = _0;
})();

The object allocation for _1 is silly. #494 suggests delaying the allocation, but in this example, it could be completely eliminated. Prepack could generate the following code:

(function () {
    let _1_f = 42;
    function _0() {
        return _1_f;
    }

    g = _0;
})();

Or even better:

(function () {
    function _0() {
        return 42;
    }

    g = _0;
})();

If instead of 42 the field obj.f may either hold an object reference or may be mutated by the residual code, then another variable similar to _1 would be needed.

jeffreytan81 commented 7 years ago

This optimization will require computing a fixed point in the ResidualHeapVisitor.

Consider the following:

(function() {
    function f() {
        let obj = {
           g1: function() {},
           g2: function() {} };
        return function() { return obj.g2; }
    }
    global_g2 = f();
})();
jeffreytan81 commented 7 years ago

Be careful: Different declarative bindings might point to the same value.

(function() {
    function f(obj) {
        return function() { return obj.g2; }
    }
    let obj = {
       g1: function() {},
       g2: function() {} };
    global_g2_1 = f(obj);
    global_g2_2 = f(obj);
})();
jeffreytan81 commented 7 years ago

Note that we need to traverse all residual functions to discover if we can apply this transformation at all:


(function() {
  function f(obj) { return obj.p; }
  function g(obj) { Object.assign(obj, { p: 42 }); }
  let obj = { p: 23 };
  global.f = f;
  global.g = g;
})();