Debugger.Script

一个 Debugger.Script 实例可能引用被调试程序中的字节码序列或 WebAssembly 代码块。对于前者,它是 Debugger API 对 JSAPI JSScript 对象的表示。这两种情况可以通过它们的 format 属性是 "js" 还是 "wasm" 来区分。

Debugger.Script 用于 JSScripts

对于引用 JSScriptDebugger.Script 实例,它们通过它们的 format 属性是 "js" 来区分。

以下每个都由一个 JSScript 对象表示

  • 函数体——即函数中所有不包含在任何嵌套函数中的代码。

  • 传递给 eval 的单个调用的代码,不包括该代码定义的任何函数体。

  • <script> 元素的内容。

  • DOM 事件处理程序,无论是在 HTML 中嵌入还是由其他 JavaScript 代码附加到元素上。

  • 出现在 javascript: URL 中的代码。

The Debugger 接口在调试器发现被调试代码的脚本时构造 Debugger.Script 对象:通过 onNewScript 处理程序方法;通过 Debugger.Framescript 属性;通过 Debugger.Object 实例的 functionScript 方法;等等。对于给定的 Debugger 实例,SpiderMonkey 为每个底层脚本对象构建一个 Debugger.Script 实例;调试器代码可以向脚本对象添加自己的属性并期望以后找到它们,使用 == 来确定两个表达式是否引用相同的脚本,等等。

(如果多个 Debugger 实例正在调试相同的代码,则每个 Debugger 为给定的脚本获取一个单独的 Debugger.Script 实例。这允许使用每个 Debugger 实例的代码在其 Debugger.Script 实例上放置任何它喜欢的属性,而无需担心干扰其他调试器。)

一个 Debugger.Script 实例是对 JSScript 对象的强引用;它保护它引用的脚本不被垃圾回收。

请注意,SpiderMonkey 可能会对等效的函数或已计算的代码使用相同的 Debugger.Script 实例——即表示相同源代码、在相同源文件中的相同位置、在相同词法环境中计算的脚本。

Debugger.Script 用于 WebAssembly

对于引用 WebAssembly 代码块的 Debugger.Script 实例,它们通过它们的 format 属性是 "wasm" 来区分。

目前仅表示通过 new WebAssembly.Module 计算的整个模块。

用于 WebAssembly 的 Debugger.Script 对象在实例化新的 WebAssembly 模块时通过 onNewScript 发现,并通过 Debugger 实例上的 findScripts 方法发现。SpiderMonkey 为每个底层 WebAssembly 模块(每个 Debugger 实例)构建一个 Debugger.Script

一个 Debugger.Script 实例是对底层 WebAssembly 模块的强引用;它保护它引用的模块不被垃圾回收。

请注意,在撰写本文时,对 WebAssembly 的支持还处于非常初期的阶段。下面许多属性和方法都会抛出异常。

约定

对于下面属性和方法的描述,如果属性或方法的行为在引用 JSScript 或 WebAssembly 代码块的实例之间存在差异,则文本将分为两个部分,分别以“如果实例引用 JSScript”和“如果实例引用 WebAssembly 代码”为标题。如果行为没有差异,则不会出现此类强调的标题。

Debugger.Script 原型对象的访问器属性

一个 Debugger.Script 实例从其原型继承以下访问器属性

isGeneratorFunction

如果此实例引用使用 function* 表达式或语句定义的函数的 JSScript,则为真。否则为假。

isAsyncFunction

如果此实例引用使用 async function 表达式或语句定义的异步函数的 JSScript,则为真。否则为假。

isFunction

如果此实例引用函数的 JSScript,则为真。否则为假。

isModule

如果此实例引用作为 ECMAScript 模块解析和加载的 JSScript,则为真。否则为假。

displayName

如果实例引用 JSScript,则为脚本的显示名称(如果存在)。如果脚本没有显示名称——例如,如果它是顶级 eval 脚本——则为 undefined

如果脚本的函数具有给定的名称,则其显示名称与其函数的给定名称相同。

如果脚本的函数没有名称,SpiderMonkey 会尝试根据其上下文推断一个合适的名称。例如

function f() {}          // display name: f (the given name)
var g = function () {};  // display name: g
o.p = function () {};    // display name: o.p
var q = {
  r: function () {}      // display name: q.r
};

请注意,显示名称可能不是有效的 JavaScript 标识符,甚至不是有效的表达式:我们尝试找到有用的名称,即使函数没有立即分配为某个变量或属性的值。因此,我们使用 a/b 来引用在 a 中定义的 b,并使用 a< 来引用出现在分配给 a 的表达式的某个位置的函数。例如

function h() {
  var i = function() {};    // display name: h/i
  f(function () {});        // display name: h/<
}
var s = f(function () {});  // display name: s<

如果实例引用 WebAssembly 代码,则抛出 TypeError

parameterNames

如果实例引用 JSScript,则为其参数的名称,作为字符串数组。如果脚本不是函数脚本,则为 undefined

如果函数使用解构参数,则相应的数组元素为 undefined。例如,如果引用是一个以这种方式声明的函数脚本

function f(a, [b, c], {d, e:f}) { ... }

那么这个 Debugger.Script 实例的 parameterNames 属性将具有以下值

["a", undefined, undefined]

如果实例引用 WebAssembly 代码,则抛出 TypeError

url

如果实例引用 JSScript,则为加载此脚本代码的文件名或 URL。对于由 evalFunction 构造函数创建的脚本,这可能是一个合成的文件名,以有效的 URL 开头,后跟跟踪代码如何引入系统的信息;整个字符串不是有效的 URL。对于 Function.prototype 的脚本,这为 null。如果此 Debugger.Scriptsource 属性非 null,则这等于 source.url

如果实例引用 WebAssembly 代码,则抛出 TypeError

startLine

如果实例引用的是 JSScript,则表示此脚本代码在由 url 指定的文件或文档中开始的行号(从 1 开始)。

startColumn

如果实例引用的是 JSScript,则表示此脚本代码在由 url 指定的文件或文档中开始的列号(从 1 开始)。对于函数,这是函数参数的起始位置。

function f() { ... }
//        ^ start (column 11)
let g = x => x*x;
//      ^ start (column 9)
let h = (x) => x*x;
//      ^ start (column 9)

对于默认类构造函数,它是 class 关键字的起始位置。

let MyClass = class { };
//            ^ start (column 15)

对于来自其他来源的脚本,例如 evalFunction 构造函数,它通常为 0。

let f = new Function("  console.log('hello world');");
//                    ^ start (column 1, from the string's perspective)

lineCount

如果实例引用的是 JSScript,则表示此脚本代码在由 url 指定的文件或文档中所占的行数(从 1 开始)。

source

如果实例引用的是 JSScript,则表示 Debugger.Source 实例,代表生成此脚本的源代码。如果未保留源代码,则为 null

如果实例引用的是 WebAssembly 代码,则表示 Debugger.Source 实例,代表 WebAssembly 代码的序列化文本格式。

sourceStart

如果实例引用的是 JSScript,则表示由 source 给出的 Debugger.Source 实例中此脚本代码开始的字符位置(从 0 开始)。如果是函数的脚本,则表示源代码中 function 标记的起始索引。

如果实例引用 WebAssembly 代码,则抛出 TypeError

sourceLength

如果实例引用的是 JSScript,则表示此脚本代码在由 source 给出的 Debugger.Source 实例中的长度(以字符为单位)。

如果实例引用 WebAssembly 代码,则抛出 TypeError

mainOffset

如果实例引用的是 JSScript,则表示脚本主入口点的偏移量(从 0 开始),不包括任何序言。

如果实例引用 WebAssembly 代码,则抛出 TypeError

global

如果实例引用的是 JSScript,则表示 Debugger.Object 实例,引用此脚本在其作用域内运行的全局对象。结果直接引用全局对象,而不是通过包装器或 WindowProxy(在 Firefox 中称为“外部窗口”)。

如果实例引用 WebAssembly 代码,则抛出 TypeError

format

如果实例引用的是 JSScript,则为 "js"

如果实例引用的是 WebAssembly 代码,则为 "wasm"

Debugger.Script 原型对象的功能属性

下面描述的函数只能在 this 值引用 Debugger.Script 实例的情况下调用;不能用作其他类型对象的函数。

getChildScripts()

如果实例引用的是 JSScript,则返回一个新数组,其元素是此脚本中每个函数的 Debugger.Script 对象。只包含直接子节点;嵌套子节点可以通过遍历树来访问。

如果实例引用 WebAssembly 代码,则抛出 TypeError

getPossibleBreakpoints(query)

查询 SpiderMonkey 中可用的推荐断点位置。返回一个结果数组,其中包含以下属性的对象

  • offset: number - 断点的偏移量(从 0 开始)。

  • lineNumber: number - 断点的行号(从 1 开始)。

  • columnNumber: number - 断点的列号(从 1 开始)。

  • isStepStart: boolean - 如果 SpiderMonkey 建议在调试器用户单步执行到下一个项目时将断点视为单步执行位置,则为 true。这大致对应于每个语句的开头,但并非完全如此。

query 参数可用于筛选断点集。query 对象可以包含以下属性

  • minOffset: number - 要包含的 offset 值的下界(包含下界,从 0 开始)。

  • maxOffset: number - 要包含的 offset 值的上界(不包含上界,从 0 开始)。

  • line: number - 限制在给定行上的断点(从 1 开始)。

  • minLine: number - 要包含的行数的下界(包含下界,从 1 开始)。

  • minColumn: number - 要包含的行/minLine 列的下界(包含下界,从 1 开始)。

  • maxLine: number - 要包含的行数的上界(不包含上界,从 1 开始)。

  • maxColumn: number - 要包含的行/maxLine 列的上界(不包含上界,从 1 开始)。

getPossibleBreakpointOffsets(query)

查询 SpiderMonkey 中可用的推荐断点位置。与 getPossibleBreakpoints 相同,只是返回的是 offset 值的数组,而不是偏移量元数据对象。

getOffsetMetadata(offset)

获取给定字节码偏移量(从 0 开始)的元数据。返回一个包含以下属性的对象

  • lineNumber: number - 断点的行号(从 1 开始)。

  • columnNumber: number - 断点的列号(从 1 开始)。

  • isBreakpoint: boolean - 如果此偏移量符合断点条件,则为 true,定义方式与 getPossibleBreakpoints() 中使用的语义相同。

  • isStepStart: boolean - 如果 SpiderMonkey 建议在调试器用户单步执行到下一个项目时将断点视为单步执行位置,则为 true。这大致对应于每个语句的开头,但并非完全如此。

setBreakpoint(offset, handler)

如果实例引用的是 JSScript,则在此脚本的offset(从 0 开始)处的字节码指令处设置断点,并将命中情况报告给handlerhit 方法。如果offset在此脚本中不是有效的偏移量,则抛出错误。此外,即使offset在此脚本中是有效的偏移量,引擎内部操作的一些指令(例如,生成器函数初始化中的 SetAliasedVar)也不允许设置断点,在这种情况下,也会抛出错误。

当执行到达给定指令时,SpiderMonkey 会调用handlerhit 方法,并传递一个 Debugger.Frame 实例,表示当前正在执行的堆栈帧。hit 方法的返回值应为 恢复值,确定执行应如何继续。

可以在一个位置设置任意数量的断点;当控制权到达该点时,SpiderMonkey 会以未指定的顺序调用它们的处理程序。

任意数量的断点可以使用相同的handler对象。

断点处理程序方法调用是跨隔间、线程内调用:调用发生在命中断点的同一线程中,以及包含处理程序函数的隔间(通常是调试器的隔间)。

新断点属于此脚本所属的 Debugger 实例。从 Debugger 实例的被调试者集中删除全局对象会清除该 Debugger 实例在该全局的脚本中设置的所有断点。

getBreakpoints([offset])

如果实例引用的是 JSScript,则返回一个数组,其中包含在此脚本的offset(从 0 开始)处设置的所有断点的处理程序对象。如果省略了offset,则返回在此脚本中任何位置设置的所有断点的处理程序。如果offset存在,但不是此脚本中的有效偏移量,则抛出错误。

如果实例引用 WebAssembly 代码,则抛出 TypeError

clearBreakpoint(handler, [offset])

如果实例引用的是 JSScript,则删除在此 Debugger 实例中使用handler作为其处理程序的所有断点。如果给出了offset(从 0 开始),则仅删除在offset处设置并使用handler的断点;如果offset不是此脚本中的有效偏移量,则抛出错误。

请注意,如果使用其他处理程序对象的断点与handler在同一位置设置,则它们将保留。

clearAllBreakpoints([offset])

如果实例引用的是 JSScript,则删除在此脚本中设置的所有断点。如果存在offset(从 0 开始),则删除在此脚本的该偏移量处设置的所有断点;如果offset不是此脚本中的有效字节码偏移量,则抛出错误。

getEffectfulOffsets()

如果实例引用的是 JSScript,则返回一个数组,其中包含脚本中所有可能产生直接副作用(在当前执行的帧之外可见)的字节码的偏移量(从 0 开始)。例如,这包括设置对象属性或元素的操作,或可能在帧外部创建的环境中设置名称的操作。

这并不包括引擎内部操作的一些指令(例如,生成器函数初始化中的 SetAliasedVar)。这些指令在引擎内部方面可能是有效果的,但这对用户不可见,在这里可以被视为没有效果。

getOffsetsCoverage():

如果实例引用的是 JSScript,则返回 null 或一个包含所有操作码覆盖率信息的数组。数组的元素是对象,每个对象描述一个操作码,并包含以下属性

  • lineNumber: 当前操作码的行号(从 1 开始)。

  • columnNumber: 当前操作码的列号(从 1 开始)。

  • offset: 当前操作码的字节码指令偏移量(从 0 开始)。

  • count: 当前操作码执行的次数。

如果此脚本没有覆盖率,或者没有被检测,则此函数将返回 null。要确保被调试者被检测,应将标志 Debugger.collectCoverageInfo 设置为 true

如果实例引用 WebAssembly 代码,则抛出 TypeError

isInCatchScope([offset])

如果实例引用的是 JSScript,如果此偏移量(从 0 开始)位于 try 块的作用域内,则为 true,否则为 false

如果实例引用 WebAssembly 代码,则抛出 TypeError

已弃用的 Debugger.Script 原型函数

以下函数均已弃用,建议使用 getOffsetMetadatagetPossibleBreakpointsgetPossibleBreakpointOffsets。这些函数对结果中包含哪些偏移量以及不包含哪些偏移量都存在定义不明确的问题。

getAllOffsets()

如果实例引用的是一个 JSScript,则返回一个数组 L,描述字节码指令偏移量(从 0 开始)与脚本中源代码位置之间的关系。L 是稀疏数组,并以源代码行号为索引。如果某个源代码行号 line 没有代码,则 L 中没有 line 属性。如果 line 存在代码,则 L[line] 是一个数组,包含指向该行的字节码指令的入口点的偏移量。

例如,假设我们有一个脚本,其源代码如下所示:

a=[]
for (i=1; i < 10; i++)
    // It's hip to be square.
    a[i] = i*i;

对该代码调用 getAllOffsets() 可能得到如下所示的数组:

[, [0], [16, 75], , [52]]

此数组表示:

  • 第一行代码从脚本中的偏移量 0 开始;

  • for 语句头有两个入口点,偏移量分别为 16 和 75(分别对应于仅执行一次的初始化和每次迭代开始时执行的循环测试);

  • 第三行没有代码;

  • 第四行从偏移量 52 开始。

如果实例引用 WebAssembly 代码,则抛出 TypeError

getAllColumnOffsets()

如果实例引用的是一个 JSScript,则返回一个数组,描述字节码指令偏移量与脚本中源代码位置之间的关系。与返回每行所有入口点偏移量的 getAllOffsets() 不同,getAllColumnOffsets() 返回每个 (行,列) 对的所有入口点偏移量。

数组的元素是对象,每个对象描述一个入口点,并包含以下属性:

  • lineNumber:偏移量为入口点的行号(从 1 开始);

  • columnNumber:偏移量为入口点的列号(从 1 开始);

  • offset:入口点的字节码指令偏移量(从 0 开始)。

例如,假设我们有一个脚本,其源代码如下所示:

a=[]
for (i=1; i < 10; i++)
    // It's hip to be square.
    a[i] = i*i;

对该代码调用 getAllColumnOffsets() 可能得到如下所示的数组:

[{ lineNumber: 1, columnNumber: 1, offset: 0 },
 { lineNumber: 2, columnNumber: 6, offset: 16 },
 { lineNumber: 2, columnNumber: 11, offset: 28 },
 { lineNumber: 4, columnNumber: 5, offset: 52 },
 { lineNumber: 4, columnNumber: 14, offset: 67 },
 { lineNumber: 2, columnNumber: 19, offset: 75 }]

如果实例引用 WebAssembly 代码,则抛出 TypeError

getLineOffsets(line)

如果实例引用的是一个 JSScript,则返回一个字节码指令偏移量数组,表示源代码行 line(从 1 开始)的入口点。如果脚本在该行没有可执行代码,则返回的数组为空。

getOffsetLocation(offset)

如果实例引用的是一个 JSScript,则返回一个对象,描述脚本中 offset 处的字节码对应的源代码位置。该对象具有以下属性:

  • lineNumber:偏移量为入口点的行号(从 1 开始);

  • columnNumber:偏移量为入口点的列号(从 1 开始);

  • isEntryPoint:如果偏移量是列入口点(如 getAllColumnOffsets() 所报告的那样),则为 true;否则为 false。