SpiderMonkey¶
SpiderMonkey 是 Mozilla Firefox 网页浏览器的 JavaScript 和 WebAssembly 实现库。实现行为由 ECMAScript 和 WebAssembly 规范定义。
引擎的大部分内部技术文档可以在源文件本身中找到,方法是查找标有 [SMDOC] 的注释。有关团队、我们的流程以及在您自己的项目中嵌入 SpiderMonkey 的信息,请访问 https://spidermonkey.dev。
一些特定主题的文档可在以下位置找到:
SpiderMonkey 的组成部分¶
🧹 垃圾回收器¶
JavaScript 是一种垃圾回收语言,在 SpiderMonkey 的核心,我们管理着一个垃圾回收的内存堆。此堆的元素具有 gc::Cell 的基本 C++ 类型。每一轮垃圾回收都会释放任何未被 根节点 或其他活动 Cell 引用到的 Cell。
有关更多详细信息,请参阅 GC 概述。
📦 JS::Value 和 JSObject¶
JavaScript 值分为对象或原始值(Undefined、Null、Boolean、Number、BigInt、String 或 Symbol)。值使用 JS::Value 类型表示,该类型可能依次指向一个扩展自 JSObject 类型的对象。对象包括普通 JavaScript 对象和表示各种事物的奇异对象,从函数到 ArrayBuffers 到 HTML 元素等等。
大多数对象扩展 NativeObject
(它是 JSObject
的子类型),它提供了一种以类似于哈希表的方式将属性存储为键值对的方法。这些对象保存它们的 值 并指向一个 Shape,该 Shape 表示 键 的集合。类似的对象指向同一个 Shape,从而节省内存并允许 JIT 快速处理类似于之前见过的对象。有关更多详细信息,请参阅 [SMDOC] 形状 注释。
C++(和 Rust)代码可以使用我们传统上称为 JSAPI 的接口集合创建和操作这些对象。
🗃️ JavaScript 解析器¶
为了评估脚本文本,我们使用 解析器 将其解析为一个 抽象语法树 (AST),然后运行 BytecodeEmitter (BCE) 生成 字节码 和相关元数据。我们将此结果格式称为 Stencil,它具有不使用垃圾回收器的有用特性。然后可以将 Stencil 实例化为一系列 GC Cell,这些 Cell 可以被下面描述的执行引擎修改和理解。
每个函数以及顶层本身都会生成一个单独的脚本。这是执行粒度的单位,因为函数可以设置为主机稍后运行的回调。脚本有 ScriptStencil
和 js::BaseScript
两种形式。
默认情况下,解析器以称为 语法 或 延迟 解析的模式运行,在这种模式下,我们避免为正在解析的源代码中的函数生成完整的字节码。此延迟解析仍然需要检查规范中描述的所有 早期错误。当第一次执行这样的延迟编译的内部函数时,我们通过一个称为 去延迟化 的过程重新编译该函数。延迟解析避免分配 AST 和字节码,从而节省了 CPU 时间和内存。在实践中,许多函数在网页的给定加载过程中从未执行过,因此这种延迟解析可能非常有利。
⚙️ JavaScript 解释器¶
解析器生成的 字节码 可以由用 C++ 编写的解释器执行,该解释器操作 GC 堆中的对象并调用主机的本机代码(例如,Web 浏览器)。有关每个字节码操作码的描述,请参阅 [SMDOC] 字节码定义,有关其实现,请参阅 js/src/vm/Interpreter.cpp
。
⚡ JavaScript JIT¶
为了加快 字节码 的执行速度,我们使用一系列 Just-In-Time (JIT) 编译器来生成针对正在运行的 JavaScript 和正在处理的数据量身定制的专用机器代码(例如,x86、ARM 等)。
当单个脚本运行更多次(或具有运行多次的循环)时,我们将其描述为变得 更热,并且在某些阈值下,我们通过 JIT 编译它来 升级。每个后续的 JIT 层花费更多时间进行编译,但旨在获得更好的执行性能。
基线解释器¶
基线解释器 是一种混合解释器/JIT,它一次解释一个操作码的 字节码,但附加了一些称为 内联缓存 (IC) 的小代码片段,这些代码片段可以快速加快下次执行相同操作码的速度(如果数据足够相似)。有关更多详细信息,请参阅 [SMDOC] JIT 内联缓存 注释。
基线编译器¶
基线编译器 使用与 基线解释器 相同的 内联缓存 机制,但此外还将整个字节码转换为本地机器代码。这消除了调度开销并进行了一些小的局部优化。此机器代码仍会回调到 C++ 以执行复杂操作。转换非常快,但 BaselineScript
使用内存并需要 mprotect
和刷新 CPU 缓存。
WarpMonkey¶
WarpMonkey JIT 取代了之前的 IonMonkey 引擎,并且是针对最常运行的脚本的最高级别的优化。它能够内联其他脚本并根据正在处理的数据和参数专门化代码。
我们将 字节码 和 内联缓存 数据转换为中间级 中间表示 (Ion MIR) 表示。在 降低 到低级中间表示 (Ion LIR) 之前,转换并优化此图。此 LIR 执行寄存器分配,然后在一个称为 代码生成 的过程中生成本地机器代码。
有关 MIR 优化的概述,请参阅 MIR 优化。
这里的优化假设脚本继续看到与之前看到的数据类似的数据。基线 JIT 在这里至关重要,因为它们生成与观察到的数据匹配的 IC。如果在用 Warp 编译脚本后,它遇到它没有准备处理的数据,它会执行 回退。回退 机制重建本地机器堆栈帧以匹配 基线解释器 使用的布局,然后分支到该解释器,就好像我们一直在运行它一样。构建此堆栈帧可能会使用 Warp 保存的特殊旁表来重建否则不可用的值。
🟪 WebAssembly¶
除了 JavaScript 之外,引擎还能够执行 WebAssembly (WASM) 源代码。
WASM-基线 (RabaldrMonkey)¶
此引擎执行快速转换为机器代码,以最大程度地减少到第一次执行的延迟。
WASM-Ion (BaldrMonkey)¶
此引擎将 WASM 输入转换为 WarpMonkey 使用的相同 MIR 形式,并使用 IonBackend 进行优化。这些优化(尤其是寄存器分配)生成非常快的本地机器代码。