它有什么作用?
Prepack是一个优化JavaScript源代码的工具: 可以在编译时而不是运行时完成的计算被消除。 预打包将 JavaScript 包的全局代码替换为等效代码,这些代码是简单的赋值序列。这摆脱了大多数中间计算和对象分配。
例子
世界您好
(function () {
function hello() { return 'hello'; }
function world() { return 'world'; }
global.s = hello() + ' ' + world();
})();
s = "hello world";
取消抽象税
(function () {
var self = this;
['A', 'B', 42].forEach(function(x) {
var name = '_' + x.toString()[0].toLowerCase();
var y = parseInt(x);
self[name] = y ? y : x;
});
})();
_a = "A";
_b = "B";
_4 = 42;
斐波那契
(function () {
function fibonacci(x) {
return x <= 1 ? x : fibonacci(x - 1) + fibonacci(x - 2);
}
global.x = fibonacci(15);
})();
x = 610;
模块初始化
(function () {
let moduleTable = {};
function define(id, f) { moduleTable[id] = f; }
function require(id) {
let x = moduleTable[id];
return x instanceof Function ? (moduleTable[id] = x()) : x;
}
global.require = require;
define("one", function() { return 1; });
define("two", function() { return require("one") + require("one"); });
define("three", function() { return require("two") + require("one"); });
define("four", function() { return require("three") + require("one"); });
})();
three = require("three");
(function () {
var _0 = function (id) {
let x = _1[id];
return x instanceof Function ? _1[id] = x() : x;
};
var _5 = function () {
return _0("three") + _0("one");
};
var _1 = {
one: 1,
two: 2,
three: 3,
four: _5
};
require = _0;
three = 3;
})();
请注意大多数计算是如何预先初始化的。但是,计算四个 (_5) 的函数保留在残差程序中,因为它在初始化时未被调用。
环境交互和分支
(function(){
function fib(x) { return x <= 1 ? x : fib(x - 1) + fib(x - 2); }
let x = Date.now();
if (x === 0) x = fib(10);
global.result = x;
})();
(function () {
var _$1 = this;
var _$0 = _$1.Date.now();
var _1 = 0 === _$0;
var _0 = _1 ? 55 : _$0;
result = _0;
})();
它是如何工作的?
要实现预包装,必须将几件事结合在一起:
-
抽象语法树 (AST)
Prepack在AST级别运行,使用Babel来解析和生成JavaScript源代码。
-
具体执行
Prepack 的核心是一个几乎兼容 ECMAScript 5 的解释器——用 JavaScript 实现! 解释器严格遵循ECMAScript 2016 语言规范,重点关注正确性和规范一致性。 你可以将 Prepack 中的解释器视为 JavaScript 的干净参考实现。
解释器能够跟踪和撤消所有效果,包括所有对象突变。这将启用推测优化。
-
符号执行
除了计算具体值外,Prepack的解释器还能够对通常由环境交互产生的抽象值进行操作。例如,可以返回一个抽象值。您还可以通过辅助帮助程序函数手动注入抽象值,例如。预打包跟踪对抽象值执行的所有操作。当分支抽象值时,Prepack 将分叉执行并探索所有可能性。因此,Prepack 为 JavaScript 实现了一个符号执行引擎。
Date.now``__abstract() -
抽象解释
符号执行在遇到抽象值上的分支时将分叉。在控制流合并点,预打包将加入发散的执行,实现一种抽象解释的形式。联接变量和堆属性可能会产生条件抽象值。预打包跟踪有关抽象值的值和类型域的信息。
-
堆序列化
在初始化阶段结束时,当全局代码返回时,预打包将捕获最终堆。 Prepack 按顺序遍历堆,生成新鲜直接的 JavaScript 代码,用于创建并链接初始化堆中可访问的所有对象。堆中的某些值可能是对抽象值进行计算的结果。对于这些值,