Debugger
接口¶
Mozilla 的 JavaScript 引擎 SpiderMonkey 提供了一个名为 Debugger
的调试接口,它允许 JavaScript 代码观察和操作其他 JavaScript 代码的执行。Firefox 内置的开发者工具和 Firebug 扩展都使用 Debugger
来实现它们的 JavaScript 调试器。但是,Debugger
非常通用,可以用于实现其他类型的工具,例如跟踪器、覆盖率分析、补丁和继续等等。
Debugger
具有三个基本特性
它是一个源代码级接口:它根据 JavaScript 语言进行操作,而不是机器语言。它操作 JavaScript 对象、堆栈帧、环境和代码,并且无论被调试者是解释、编译还是优化,都提供一致的接口。如果你对 JavaScript 语言有很好的掌握,那么你应该具备使用
Debugger
所需的所有背景知识,即使你从未了解过该语言的实现。它供JavaScript 代码使用。JavaScript 既是被调试语言,也是工具实现语言,因此使 JavaScript 在 Web 上有效的特性可以用于为开发人员创建工具。正如预期的那样,
Debugger
是一个健全的接口:使用(甚至滥用)Debugger
绝不应该导致 Gecko 崩溃。错误会抛出正确的 JavaScript 异常。它是一个线程内调试 API。被调试者和使用
Debugger
观察它的代码必须在同一个线程中运行。跨线程、跨进程和跨设备的工具必须使用Debugger
从同一线程内观察被调试者,然后自行处理任何必要的通信。(Firefox 的内置工具为此目的定义了一个协议。)
在 Gecko 中,Debugger
API 仅对 chrome 代码可用。根据设计,它不应该引入安全漏洞,因此原则上也可以提供给内容;但很难证明增加攻击面的安全风险是合理的。
Debugger
API 目前无法观察自托管 JavaScript。这并非 API 设计固有的,而仅仅是因为自托管基础设施尚未准备好应对 Debugger
API 可以执行的入侵。
调试器实例和影子对象¶
Debugger
将被调试者状态的每个方面都反映为 JavaScript 值——不仅是像对象和基本类型这样的实际 JavaScript 值,还包括堆栈帧、环境、脚本和编译单元,这些通常无法作为它们自身的对象访问。
这是一个正在运行计时器回调函数的 JavaScript 程序
此图显示了构成 Debugger API 的各种类型的影子对象(它们都遵循一些通用约定)
Debugger.Object
表示一个被调试者对象,提供了一个面向反射的 API,可以保护调试器避免意外调用 getter、setter、代理陷阱等等。Debugger.Script
表示一段 JavaScript 代码——函数体或顶级脚本。给定一个Debugger.Script
,可以设置断点,在源位置和字节码偏移量之间转换(偏离“源代码级”设计原则),以及查找代码的其他静态特性。Debugger.Frame
表示一个正在运行的堆栈帧。可以使用它们遍历堆栈并查找每个帧的脚本和环境。还可以为帧设置onStep
和onPop
处理程序。Debugger.Environment
表示一个环境,将变量名与存储位置关联起来。环境可能属于正在运行的堆栈帧、被函数闭包捕获,或者将某个全局对象的属性反映为变量。
Debugger
实例本身并不是被调试者中任何内容的影子;相反,它维护着一组要被视为被调试者的全局对象。一个 Debugger
只观察在这些全局对象的范围内发生的执行。可以设置函数,以便在推送新的堆栈帧时调用;加载新代码时调用;等等。
此图中省略了 Debugger.Source
实例,它们表示 JavaScript 编译单元。一个 Debugger.Source
可以提供其源代码的完整副本,并解释代码是如何进入系统的,无论是通过调用 eval
、<script>
元素还是其他方式。一个 Debugger.Script
指向它派生的 Debugger.Source
。
同样省略的是 Debugger
的 Debugger.Memory
实例,它包含用于观察被调试者内存使用情况的方法和访问器。
所有这些类型都遵循一些通用约定,在深入研究任何特定类型的规范之前,你应该先浏览一下。
所有影子对象对于每个 Debugger
和每个引用都是唯一的。对于给定的 Debugger
,只有一个 Debugger.Object
引用特定的被调试者对象;对于特定的堆栈帧,只有一个 Debugger.Frame
;等等。因此,工具可以将引用的元数据存储为影子本身上的属性,并在再次遇到相同的引用时找到该元数据。并且由于影子是每个 Debugger
的,因此工具可以在不担心干扰使用其自身 Debugger
实例的其他工具的情况下这样做。
示例¶
以下是一些你可以自己尝试的事情,它们展示了 Debugger
的一些功能
在页面中设置断点,并在命中时运行一个处理程序函数,该函数在页面的上下文中计算表达式。