Search code examples
javascriptparsinginlining

How to do aggressive inlining with JS compiler/parser/transformer?


I'm trying to figure out how to do aggressive inlining of JS code. Here is an example:

// Source code
var fruits = {
  'a': { name: 'apple' },
  'b': { name: 'banana' }
};


function getFruit(n) {
  return fruits[n];
}

console.log(getFruit('a'));
console.log(getFruit('b'));

If I use Google Closure Compiler on this code with optimization set to advanced, I get the following:

// Closure output
var a = {a:{name:"apple"}, b:{name:"banana"}};
console.log(a.a);
console.log(a.b);

This is great as the function getFruit is inlined but the object a is still there and I want this:

console.log({a:{name:"apple"}, b:{name:"banana"}}.a);
console.log({a:{name:"apple"}, b:{name:"banana"}}.b);

Of course the purpose of Closure is to minify as well so if the object a is used more than once, it doesn't get inlined. If I remove the console.log(getFruit('b')); then I get what I want.

Prepack doesn't help much either:

// Prepack output
var getFruit, fruits;
(function () {
  var _$0 = this;

  var _9 = function (n) {
    return fruits[n];
  };

  _$0.getFruit = _9;
  var _2 = {
    name: "apple"
  };
  var _4 = {
    name: "banana"
  };
  _$0.fruits = {
    a: _2,
    b: _4
  };
  console.log(_2);
  console.log(_4);
}).call(this);

So my question is, is there any way I can use a compiler/parser with an ad-hoc rule that can aggressively inline any expression whose evaluation result can be known at compile time? In other word, it should remove all the indirections as much as possible.

WHY:

Because I wanna know if the console.log function is called with a specific argument value at compile time. By compile time I mean by just looking at the code statically without having to run it cause I have that information already. The fruits object doesn't change after I run this code.


Solution

  • I'm afraid the task is too narrow to find a ready-to-use tool. As you can see, Google Closure Compiler optimizes code size (well, it could optimize this particular example better, like to

    console.log({name:"apple"});
    console.log({name:"banana"});
    

    which you can perhaps discuss with authors) and not the number of variables (quite an uncommon metric). Also, object initialization can even change script's behavior ({a:1} != {a:1}, right? while with var o = {a:1} this is true: o == o), which makes the desirable result even more exotic.

    However, you can create such tool by actually analysing the JS syntax and manipulating structures which describe it. Such structures are called abstract syntax trees and probably the most well-known tool(*) to manipulate those (creating so called codemods) is https://github.com/facebook/jscodeshift .

    Note: creating codemods is not an easy ride, so I think to create one of those you should either have a very good reason or be interested to learn and do some complicated stuff for fun. There's not much documentation or articles on the topic either, but perhaps enough already. This may be helpful as an introduction to ASTs and this contains a number of nice codemod examples as well as links to "how to write your own codemod" things.

    (*) I mean manipulation customized by you; the most well-known tool in JS world that is based on ASTs is probably Babel (or may be TypeScript or some other thing that parses/produces JS)