如何使用 JS compiler/parser/transformer 进行积极的内联?
How to do aggressive inlining with JS compiler/parser/transformer?
我正在尝试弄清楚如何对 JS 代码进行积极的内联。这是一个例子:
// Source code
var fruits = {
'a': { name: 'apple' },
'b': { name: 'banana' }
};
function getFruit(n) {
return fruits[n];
}
console.log(getFruit('a'));
console.log(getFruit('b'));
如果我在此代码上使用 Google Closure Compiler 并将 optimization
设置为 advanced
,我将得到以下结果:
// Closure output
var a = {a:{name:"apple"}, b:{name:"banana"}};
console.log(a.a);
console.log(a.b);
这很好,因为函数 getFruit
是内联的,但对象 a
仍然存在,我想要这个:
console.log({a:{name:"apple"}, b:{name:"banana"}}.a);
console.log({a:{name:"apple"}, b:{name:"banana"}}.b);
当然 Closure 的目的也是缩小,所以如果对象 a
被多次使用,它不会被内联。如果我删除 console.log(getFruit('b'));
然后我得到我想要的。
Prepack 也没什么用:
// Prepack output
var getFruit, fruits;
(function () {
var _[=14=] = this;
var _9 = function (n) {
return fruits[n];
};
_[=14=].getFruit = _9;
var _2 = {
name: "apple"
};
var _4 = {
name: "banana"
};
_[=14=].fruits = {
a: _2,
b: _4
};
console.log(_2);
console.log(_4);
}).call(this);
所以我的问题是,有没有什么方法可以将 compiler/parser 与临时规则一起使用,该规则可以积极地内联任何其计算结果在编译时可知的表达式?换句话说,它应该尽可能地去除所有的间接寻址。
为什么:
因为我想知道 console.log
函数是否在 compile
时使用特定参数值调用。编译时我的意思是只静态地查看代码而不必 运行 因为我已经有了这些信息。 fruits
对象在我 运行 这段代码后没有改变。
恐怕任务范围太窄,找不到现成的工具。如您所见,Google Closure Compiler 优化了代码大小(好吧,它可以更好地优化这个特定示例,比如
console.log({name:"apple"});
console.log({name:"banana"});
您或许可以与作者讨论)而不是变量的数量(非常不常见的指标)。此外,对象初始化甚至可以改变脚本的行为({a:1} != {a:1}
,对吗?而 var o = {a:1}
是这样:o == o
),这使得理想的结果更加奇特。
但是,您可以通过实际分析 JS 语法和操作描述它的结构来创建这样的工具。这种结构称为 abstract syntax trees and probably the most well-known tool(*) to manipulate those (creating so called codemods) is https://github.com/facebook/jscodeshift .
注意:创建 codemod 并非易事,所以我认为要创建其中一个,您应该有一个 非常 充分的理由,或者有兴趣学习和做一些复杂的事情好玩的东西。关于该主题的文档或文章也不多,但也许已经足够了。 This may be helpful as an introduction to ASTs and this 包含许多不错的 codemod 示例以及指向 "how to write your own codemod" 事物的链接。
(*) 我的意思是你自定义的操作; JS 世界中最著名的基于 AST 的工具可能是 Babel(或者可能是 TypeScript 或其他 parses/produces JS 的工具)
我正在尝试弄清楚如何对 JS 代码进行积极的内联。这是一个例子:
// Source code
var fruits = {
'a': { name: 'apple' },
'b': { name: 'banana' }
};
function getFruit(n) {
return fruits[n];
}
console.log(getFruit('a'));
console.log(getFruit('b'));
如果我在此代码上使用 Google Closure Compiler 并将 optimization
设置为 advanced
,我将得到以下结果:
// Closure output
var a = {a:{name:"apple"}, b:{name:"banana"}};
console.log(a.a);
console.log(a.b);
这很好,因为函数 getFruit
是内联的,但对象 a
仍然存在,我想要这个:
console.log({a:{name:"apple"}, b:{name:"banana"}}.a);
console.log({a:{name:"apple"}, b:{name:"banana"}}.b);
当然 Closure 的目的也是缩小,所以如果对象 a
被多次使用,它不会被内联。如果我删除 console.log(getFruit('b'));
然后我得到我想要的。
Prepack 也没什么用:
// Prepack output
var getFruit, fruits;
(function () {
var _[=14=] = this;
var _9 = function (n) {
return fruits[n];
};
_[=14=].getFruit = _9;
var _2 = {
name: "apple"
};
var _4 = {
name: "banana"
};
_[=14=].fruits = {
a: _2,
b: _4
};
console.log(_2);
console.log(_4);
}).call(this);
所以我的问题是,有没有什么方法可以将 compiler/parser 与临时规则一起使用,该规则可以积极地内联任何其计算结果在编译时可知的表达式?换句话说,它应该尽可能地去除所有的间接寻址。
为什么:
因为我想知道 console.log
函数是否在 compile
时使用特定参数值调用。编译时我的意思是只静态地查看代码而不必 运行 因为我已经有了这些信息。 fruits
对象在我 运行 这段代码后没有改变。
恐怕任务范围太窄,找不到现成的工具。如您所见,Google Closure Compiler 优化了代码大小(好吧,它可以更好地优化这个特定示例,比如
console.log({name:"apple"});
console.log({name:"banana"});
您或许可以与作者讨论)而不是变量的数量(非常不常见的指标)。此外,对象初始化甚至可以改变脚本的行为({a:1} != {a:1}
,对吗?而 var o = {a:1}
是这样:o == o
),这使得理想的结果更加奇特。
但是,您可以通过实际分析 JS 语法和操作描述它的结构来创建这样的工具。这种结构称为 abstract syntax trees and probably the most well-known tool(*) to manipulate those (creating so called codemods) is https://github.com/facebook/jscodeshift .
注意:创建 codemod 并非易事,所以我认为要创建其中一个,您应该有一个 非常 充分的理由,或者有兴趣学习和做一些复杂的事情好玩的东西。关于该主题的文档或文章也不多,但也许已经足够了。 This may be helpful as an introduction to ASTs and this 包含许多不错的 codemod 示例以及指向 "how to write your own codemod" 事物的链接。
(*) 我的意思是你自定义的操作; JS 世界中最著名的基于 AST 的工具可能是 Babel(或者可能是 TypeScript 或其他 parses/produces JS 的工具)