Debugger.Frame¶
一个 Debugger.Frame
实例表示一个可见的堆栈帧。给定一个 Debugger.Frame
实例,您可以找到该帧正在执行的脚本,遍历堆栈到旧的帧,找到正在执行的词法环境,等等。
对于给定的 Debugger
实例,SpiderMonkey 仅为给定的可见帧创建一个 Debugger.Frame
实例。在调试对象在给定帧中运行时调用的每个处理程序方法都将获得相同的帧对象。类似地,回溯到之前访问的帧会产生与之前相同的帧对象。调试器代码可以向帧对象添加自己的属性,并期望以后找到它们,使用 ==
来确定两个表达式是否引用同一个帧,等等。
(如果多个 Debugger
实例调试相同的代码,则每个 Debugger
为给定的帧获取一个单独的 Debugger.Frame
实例。这允许使用每个 Debugger
实例的代码在其 Debugger.Frame
实例上放置任何它喜欢的属性,而无需担心干扰其他调试器。)
当调试对象弹出堆栈帧(例如,因为函数调用已返回或从中抛出异常)时,引用该帧的 Debugger.Frame
实例变为非活动状态:其 onStack
属性变为 false
,并且访问其许多属性或调用其方法都会抛出异常。请注意,帧仅在调试器可预测的时间变为非活动状态:当调试对象运行时,或当调试器自己从堆栈中删除帧时。
可见帧¶
检查调用堆栈时,Debugger
不会显示堆栈上实际存在的全部帧:虽然它确实显示了所有运行调试对象代码的帧,但它会忽略运行调试器自身代码的帧,并且会忽略大多数运行非调试对象代码的帧。我们称这些堆栈帧为 Debugger
确实显示的可见帧。
如果以下任何一项为真,则该帧为可见帧
它正在运行 调试对象代码;
其直接调用者是运行调试对象代码的帧;或者
它是一个
"debugger"
帧,表示由调试器调用的调试对象代码的延续。
“直接调用者”规则意味着,当调试对象代码调用非调试对象函数时,它看起来像是对原语的调用:您会看到一个对调试对象可访问的非调试对象函数的帧,但该函数进行的任何进一步调用都被视为内部细节,并从堆栈跟踪中省略。如果非调试对象函数最终回调到调试对象代码,则这些帧是可见的。
(请注意,调试对象不被认为是其触发的处理程序方法的“直接调用者”。即使调试对象和调试器共享相同的 JavaScript 堆栈,为 SpiderMonkey 调用处理程序方法以报告调试对象中的事件而推送的帧绝不被视为可见帧。)
调用函数和“debugger”帧¶
调用函数是此接口中任何允许调试器在调试对象中调用代码的函数:Debugger.Object.prototype.call
、Debugger.Frame.prototype.eval
,等等。
虽然调用函数在要运行的代码和如何向其传递值方面有所不同,但它们都遵循以下一般过程
令older 为堆栈上最年轻的可见帧,如果不存在此类帧,则为
null
。(这绝不是调试器自己的帧之一;这些帧永远不会显示为Debugger.Frame
实例。)在堆栈上推送一个
"debugger"
帧,其older
属性为older。根据给定调用函数的需要调用调试对象代码,并将
"debugger"
帧作为其延续。例如,Debugger.Frame.prototype.eval
为其运行的代码推送一个"eval"
帧,而Debugger.Object.prototype.call
推送一个"call"
帧。当调试对象代码完成时,无论是通过返回、抛出异常还是终止,都弹出
"debugger"
帧,并从调用函数返回适当的 完成值 到调试器。
当调试器调用调用函数以运行调试对象代码时,该代码的延续是调试器,而不是下一个调试对象代码帧。推送 "debugger"
帧使这种延续明确,并使其更容易找到为调用创建的堆栈的范围。
挂起的帧¶
某些帧可以挂起。
当生成器 yield
值时,或者当异步函数 await
值时,当前帧将被挂起并从堆栈中移除,其他 JS 代码有机会运行。稍后(例如,如果 await
的 Promise 变为已解析),SpiderMonkey 将恢复该帧。它将被放回堆栈,执行将从中断的地方继续。只有生成器和异步函数调用帧可以被挂起和恢复。
目前,帧的 onStack
属性在被挂起时为 false
(bug 1448880)。
每次将生成器或异步函数调用放回堆栈时,SpiderMonkey 都使用相同的 Debugger.Frame
对象。这意味着 onStep
处理程序可用于跳过 yield
和 await
。
每次帧被挂起时都会调用 frame.onPop
处理程序,每次帧被恢复时都会调用 Debugger.onEnterFrame
处理程序。(这意味着这些事件可以对同一个 Frame
对象触发多次,这很奇怪,但准确地传达了正在发生的事情。)
传递给挂起的 frame.onPop
处理程序的 完成值 包含其他属性以阐明正在发生的事情。有关详细信息,请参阅完成值的文档。
进入生成器: “初始 yield”¶
当调用调试对象生成器时,会发生一些奇怪的事情。.onEnterFrame
钩子会触发,就像我们正在进入生成器一样。但是生成器内部的代码不会运行。相反,它会立即返回。然后,我们有时会为同一个生成器获得另一个 .onEnterFrame
事件。发生了什么事?
为了解释这一点,我们首先必须描述根据 ECMAScript 语言规范生成器调用是如何工作的。请注意,除了步骤 3 之外,它与常规函数调用完全相同。
“执行上下文”(我们称之为
Frame
)被推送到堆栈。创建一个环境(用于参数和局部变量)。如果有任何参数默认值表达式,则对其进行计算。
创建一个生成器对象,最初在生成器主体开始时被挂起。
堆栈帧被弹出,生成器对象被返回给调用者。
JavaScript 引擎实际上按此顺序执行这些步骤。因此,当调用调试对象生成器时,您将观察到以下情况
debugger.onEnterFrame
钩子触发。调试器可以遍历参数默认值代码(如果有)。
生成器的主体尚未运行。相反,将创建一个生成器对象并将其挂起(不会触发任何调试器事件)。
frame.onPop
钩子触发,完成值为{return:
(新的生成器对象)}
。
在 SpiderMonkey 中,此挂起和返回新生成器对象的流程称为“初始 yield”。
如果调用者随后使用生成器的 .next()
方法,这可能会立即发生,也可能不会立即发生,具体取决于调试对象代码,则挂起的生成器将被恢复,再次触发 .onEnterFrame
。
Debugger.Frame 原型对象的访问器属性¶
一个 Debugger.Frame
实例从其原型继承以下访问器属性
type
¶
描述此帧类型的字符串
"call"
:运行函数调用的帧。(我们可能无法获取对主机函数调用的帧。)"eval"
:运行传递给eval
的代码的帧。"global"
:运行全局代码的帧(既不是上述两种情况的 JavaScript)。"module"
:在模块顶层运行代码的帧。"wasmcall"
:运行 WebAssembly 函数调用的帧。"debugger"
:调试器调用的用户代码调用帧(请参阅下面的eval
方法)。
如果 .terminated == true
,则访问此属性将抛出错误。
implementation
¶
描述此帧在 JavaScript 引擎的哪个层级中执行的字符串。
"interpreter"
:在解释器中运行的帧。"baseline"
:在非优化基线 JIT 中运行的帧。"ion"
:在优化 JIT 中运行的帧。"wasm"
:在 WebAssembly 基线 JIT 中运行的帧。
如果 .onStack == false
,则访问此属性将抛出错误。
this
¶
此帧的 this
值(一个调试目标值)。对于 wasmcall
帧,此属性会抛出 TypeError
。
如果 .terminated == true
,则访问此属性将抛出错误。
older
¶
下一个较旧的可视帧的 Debugger.Frame
,当此帧完成时,控制权将恢复到该帧。如果没有较旧的帧,则为 null
。如果在此帧上方显式插入了异步堆栈跟踪,则为 null
,因为显式保存的帧优先。如果此帧是挂起的生成器或异步调用,则这也将为 null
。
如果 .terminated == true
,则访问此属性将抛出错误。
olderSavedFrame
¶
如果此帧没有 older
帧,则此字段可能保存一个 SavedFrame
对象,该对象表示触发此 Debugger.Frame
实例执行的已保存异步堆栈。
如果 .terminated == true
,则访问此属性将抛出错误。
onStack
¶
如果此 Debugger.Frame
实例引用的帧仍在堆栈上,则为 true;如果它已完成执行或以其他方式弹出,则为 false。请注意,无论帧处于什么状态,都可以访问此属性,因此可用于验证是否可以安全地访问需要堆栈上帧的其他属性。
terminated
¶
如果此 Debugger.Frame
实例引用的帧将永远不会再次运行,则为 true;如果它在堆栈上或是一个稍后可能恢复的挂起生成器/异步调用,则为 false。请注意,无论帧处于什么状态,都可以访问此属性,因此可用于验证是否可以安全地访问需要非终止帧的其他属性。
script
¶
在此帧中执行的脚本(一个 Debugger.Script
实例),或在不表示对调试目标代码调用的帧上为 null
。在 callee
属性不为 null 的帧上,这等于 callee.script
。
如果 .terminated == true
,则访问此属性将抛出错误。
offset
¶
当前在 script
中执行的字节码指令的偏移量,或者如果帧的 script
属性为 null
,则为 undefined
。对于 wasmcall
帧,此属性会抛出 TypeError
。
如果这用于挂起的函数帧,则偏移量将引用帧将恢复到的偏移量。
如果 .terminated == true
,则访问此属性将抛出错误。
environment
¶
正在进行评估的词法环境(一个 Debugger.Environment
实例),或在不表示调试目标代码评估的帧上为 null
,例如对非调试目标函数、主机函数或 "debugger"
帧的调用。
如果 .terminated == true
,则访问此属性将抛出错误。
callee
¶
创建此帧的函数应用,作为调试目标值,或者如果这不是 "call"
帧,则为 null
。
如果 .terminated == true
,则访问此属性将抛出错误。
constructing
¶
如果此帧用于作为构造函数调用的函数,则为 true,否则为 false。
如果 .terminated == true
,则访问此属性将抛出错误。
arguments
¶
传递给当前帧的参数,或者如果这不是 "call"
帧,则为 null
。当不为 null
时,这是一个对象,分配在与调试器相同的全局范围内,其原型链上具有 Array.prototype
,一个不可写的 length
属性,以及名称为数组索引的属性。每个属性都是一个只读访问器属性,其 getter 返回相应参数的当前值。当引用帧被弹出时,参数值的属性的 getter 会抛出错误。
如果 .onStack == false
,则访问此属性将抛出错误。
asyncPromise
¶
如果帧不是异步(生成器)函数,则这将是 undefined
。
对于异步函数,这将是一个 Debugger.Object
,其引用是异步函数调用的返回值的 Promise。请注意,如果在 onEnterFrame
期间访问此属性,则此属性将为 null
,因为此时 Promise 还不存在。
对于异步生成器函数,这将是一个 Debugger.Object
,其引用是当前迭代的“value”+“done”对象的 Promise,该 Promise 将在生成器下次抛出/yield/返回时解析。请注意,如果在初始生成器 onEnterFrame
/onPop
(在第一次 .next
调用之前)期间访问此属性,则这将为 null
,因为此时还没有 Promise。
如果 .terminated == true
,则访问此属性将抛出错误。
Debugger.Frame 实例的处理程序方法¶
每个 Debugger.Frame
实例都继承访问器属性,这些属性保存处理程序函数,供 SpiderMonkey 在帧中发生给定事件时调用。
对帧的处理程序方法的调用是跨区间的线程内调用:调用发生在帧所属的线程中,并在处理程序方法所属的区间中运行。
Debugger.Frame
实例继承以下处理程序方法属性
onStep
¶
此属性必须是 undefined
或函数。如果它是一个函数,则当此帧中的执行取得少量进展时,SpiderMonkey 会调用它,不传递任何参数,并将此 Debugger.Frame
实例作为 this
值提供。该函数应返回一个 恢复值,指定调试目标的执行应如何继续。
“少量进展”的构成因实现而异,但它足够细粒度以实现有用的“步进”和“下一步”行为。
如果多个 Debugger
实例都具有给定堆栈帧的 Debugger.Frame
实例,并且都设置了 onStep
处理程序,则它们的处理程序将以未指定的顺序运行。如果任何 onStep
处理程序强制帧提前返回(通过返回除 undefined
之外的恢复值),则任何剩余的调试器的 onStep
处理程序都不会运行。
此属性在不执行调试目标代码的帧上被忽略,例如对主机函数的调用 "call"
帧和 "debugger"
帧。
无论帧当前是否在堆栈上/挂起/终止,都可以访问和重新分配此属性。
onPop
¶
此属性必须是 undefined
或函数。如果它是一个函数,则 SpiderMonkey 会在弹出或挂起此帧之前调用它,传递一个 完成值 指示原因,并将此 Debugger.Frame
实例作为 this
值提供。该函数应返回一个 恢复值 指示执行应如何继续。在新创建的帧上,此属性的值为 undefined
。
当调用此处理程序时,此帧的当前执行位置(如其 offset
和 environment
属性中反映的那样)是导致其展开的操作。在返回或抛出异常的帧中,该位置通常是 return 或 throw 语句。在传播异常的帧中,该位置是调用。在生成器或异步函数帧中,该位置可能是 yield
或 await
表达式。
当 onPop
调用报告构造调用(即通过 new
运算符调用的函数)的完成时,传递给处理程序的完成值描述了函数体返回的值。如果此值不是对象,则它可能与 new
表达式生成的值不同,后者将是帧的 this
属性的值。(在 ECMAScript 术语中,onPop
处理程序接收 [[Call]]
方法返回的值,而不是 [[Construct]]
方法返回的值。)
当调试器处理程序函数通过返回 { return:... }
、{ throw:... }
或 null
恢复值来强制帧提前完成时,SpiderMonkey 会调用帧的 onPop
处理程序(如果有)。在这种情况下传递的完成值反映导致帧完成的恢复值。
当 SpiderMonkey 调用正在抛出异常或被终止的帧的 onPop
处理程序,并且处理程序返回 undefined
时,则 SpiderMonkey 会继续进行异常或终止。也就是说,undefined
恢复值不会干扰帧的抛出和终止过程。
如果多个 Debugger
实例都具有给定堆栈帧的 Debugger.Frame
实例,并且都设置了 onPop
处理程序,则它们的处理程序将以未指定的顺序运行。每个处理程序返回的恢复值都会为下一个处理程序建立报告的完成值。
对于给定的 Debugger.Frame
,onPop
处理程序通常只调用一次,之后帧变为非活动状态。但是,在 生成器和异步函数 的情况下,每次挂起帧时,onPop
都会触发。
此处理器不会在 "debugger"
帧上调用。当由于过度递归或内存不足异常而展开帧时,也不会调用它。
无论帧当前是否在堆栈上/挂起/终止,都可以访问和重新分配此属性。
调试器.帧原型对象的函数属性¶
下面描述的函数只能使用一个 this
值来调用,该值引用 Debugger.Frame
实例;它们不能用作其他类型对象的的方法。
eval(code, [options])
¶
在该帧的执行上下文中评估code,并返回一个 完成值,描述其完成方式。Code 是一个字符串。如果此帧的 environment
属性为 null
或 type
属性为 wasmcall
,则抛出 TypeError
。在调用期间,所有现有的处理程序方法、断点等都保持活动状态。此函数遵循 调用函数约定。
当code包含 Use Strict 指令或在此帧中执行的代码为严格模式代码时,Code被解释为严格模式代码。
如果code不是严格模式代码,则code中的变量声明会影响此帧的环境。(在 ECMAScript 规范中使用的术语中,eval 代码的执行上下文的 VariableEnvironment
是此帧表示的执行上下文的 VariableEnvironment
。)如果实现限制阻止 SpiderMonkey 按要求扩展此帧的环境,则此调用会抛出 Error 异常。
如果给出,options应该是一个对象,其属性指定评估应如何发生的细节。eval
方法识别以下属性
url
我们应该将code归因于的文件名或 URL。如果省略此属性,则 URL 默认为
"debugger eval code"
。lineNumber
评估代码应在url中声称开始的行号。
如果 .onStack == false
,则访问此属性将抛出错误。
evalWithBindings(code, bindings, [options])
¶
类似于 eval
,但在该帧的环境中评估code,并使用来自对象bindings的绑定扩展。对于bindings命名的每个自己的可枚举属性name,其值为value,在评估code的环境中包含一个名为name的变量,其值为value。每个value必须是调试器值。(这不像 with
语句:code可以访问、分配和删除引入的绑定,而不会对bindings对象产生任何影响。)
此方法允许调试器代码引入对给定调试器代码可见的临时绑定,并且这些绑定引用调试器持有的调试器值,并且不会修改任何现有的调试器环境。
请注意,与 eval
类似,传递给 evalWithBindings
的code中的声明会影响此帧的环境,即使该环境通过code中可见的绑定扩展也是如此。(在 ECMAScript 规范中使用的术语中,eval 代码的执行上下文的 VariableEnvironment
是此帧表示的执行上下文的 VariableEnvironment
,并且bindings出现在一个新的声明性环境中,它是 eval 代码的 LexicalEnvironment
。)如果实现限制阻止 SpiderMonkey 按要求扩展此帧的环境,则此调用会抛出 Error
异常。
options参数与上面描述的 Debugger.Frame.prototype.eval
相同。也像 eval
一样,如果此帧的 environment
属性为 null
或 type
属性为 wasmcall
,则抛出 TypeError
。
注意:如果此方法在拥有 Debugger 对象的对象上调用,并且该对象具有 onNativeCall 处理程序,则在评估期间只会调用与该调试器关联的对象上的钩子。
如果 .onStack == false
,则访问此属性将抛出错误。