JavaScript 扩展中的本机调试器对象 - 调试器对象详细信息

本主题介绍有关在 JavaScript 扩展中使用本机调试器对象的其他详细信息。

本机调试器对象表示调试器环境的各种构造和行为。 这些对象可以传递到 (,也可以在) JavaScript 扩展中获取,以操作调试器的状态。

有关调试器对象 JavaScript 扩展的信息,请参阅 JavaScript 扩展中的本机调试器对象

有关使用 JavaScript 的一般信息,请参阅 JavaScript 调试器脚本

例如 JavaScript 脚本和扩展,调试器团队在 https://github.com/Microsoft/WinDbg-Samples托管 GitHub 存储库。

JavaScript 扩展中的调试器对象

传递本机对象

调试器对象可以通过多种方式传入 JavaScript 扩展或在 JavaScript 扩展中获取。

  • 它们可以传递到 JavaScript 函数或方法
  • 它们可以是 JavaScript 原型的实例对象, (作为可视化工具,例如)
  • 可以从旨在创建本机调试器对象的主机方法返回它们
  • 可以从旨在创建调试器本机对象的主机方法返回它们

传递给 JavaScript 扩展的调试器对象具有本部分中介绍的一组功能。

  • 属性访问
  • 投影名称
  • 与本机调试器对象相关的特殊类型
  • 其他属性

属性访问

虽然对象上有一些属性由 JavaScript 提供程序本身放置,但进入 JavaScript 的本机对象上的大多数属性都由数据模型提供。 这意味着,对于 object.propertyName 或 object[propertyName] ---属性访问,将发生以下情况。

  • 如果 propertyName 是 JavaScript 提供程序本身投影到对象上的属性的名称,则它将首先解析为:否则
  • 如果 propertyName 是数据模型 (另一个可视化工具) 投影到对象上的键的名称,则它将解析为第二个名称:否则
  • 如果 propertyName 是本机对象的字段的名称,它将解析为第三个名称:否则
  • 如果对象是指针,则将取消引用指针,并且上述循环将继续 (取消引用对象的投影属性,后跟键,后跟本机字段)

JavaScript 中的正常属性访问方式 -- object.propertyName 和 object[propertyName] -- 将访问对象的底层本机字段,就像调试器中的“dx”命令一样。

投影名称

以下属性 (和方法) 投影到进入 JavaScript 的本机对象上。

方法 签名 说明
hostContext 属性 返回一个 对象,该对象表示对象在地址空间、调试目标等 (上下文...)
targetLocation 属性 返回一个 对象,该对象是对象在地址空间中的抽象, (虚拟地址、寄存器、子寄存器等...)
targetSize 属性 有效地返回对象的大小 (:sizeof (<TYPE OF OBJECT>)
addParentModel .addParentModel (对象) 将新的父模型 (类似于 JavaScript 原型,但在数据模型端) 对象
removeParentModel .removeParentModel (对象) 从 对象中删除给定的父模型
runtimeTypedObject properties 对 对象执行分析,并尝试将其转换为运行时 (派生) 类型
targetType properties JavaScript 扩展可直接访问基础语言的类型系统。 此访问通过类型对象的概念表示。 有关详细信息,请参阅 JavaScript 扩展中的本机调试器对象 - 类型对象

如果对象是指针,则以下属性 (和方法) 投影到进入 JavaScript 的指针上:

属性名称 签名 说明
add .add (值) 在指针和指定值之间执行指针数学加法
address properties 以 64 位序号对象的形式返回指针的地址, (库类型)
引用 .dereference () 取消引用指针并返回基础对象
isNull 属性 返回指针值是否为 nullptr (0)

与本机调试器对象相关的特殊类型

位置对象

从本机对象的 targetLocation 属性返回的位置对象包含以下属性 (和方法) 。

属性名称 签名 说明
add .add (值) 将绝对字节偏移量添加到位置。
减 (subtract) .减 (值) 从位置减去绝对字节偏移量。

其他属性

可迭代性

任何被数据模型理解为可迭代的对象, (它是一个本机数组,或者它有一个可视化工具 (NatVis 或其他) 使其可迭代) 将具有一个迭代器函数, (通过 ES6 标准 Symbol.iterator) 为其编制索引。 这意味着可以在 JavaScript 中循环访问本机对象,如下所示。

function iterateNative(nativeObject)
{
    for (var val of nativeObject)
    {
        // 
        // val will contain each element iterated from the native object.  This would be each element of an array,
        // each element of an STL structure which is made iterable through NatVis, each element of a data structure
        // which has a JavaScript iterator accessible via [Symbol.iterator], or each element of something
        // which is made iterable via support of IIterableConcept in C/C++.
        //
    }
}

可索引性

通过序号在一个维度中被理解为可编制索引的对象 (例如:本机数组) 将通过标准属性访问运算符 -- object[index] 在 JavaScript 中编制索引。 如果对象可按名称编制索引或在多个维度中可编制索引,则 getValueAt 和 setValueAt 方法将投影到对象上,以便 JavaScript 代码可以利用索引器。

function indexNative(nativeArray)
{
    var first = nativeArray[0];
}

字符串转换

任何通过支持 IStringDisplayableConcept 或 NatVis DisplayString 元素进行显示字符串转换的本机对象都可以通过标准 JavaScript toString 方法访问该字符串转换。

function stringifyNative(nativeObject)
{
    var myString = nativeObject.toString();
}

创建本机调试器对象

如前所述,JavaScript 脚本可以通过多种方式之一将其传递到 JavaScript 中来访问本机对象,也可以通过调用主机库来创建本机对象。 使用以下函数创建本机调试器对象。

方法 签名 说明

host.getModuleSymbol

getModuleSymbol (moduleName, symbolName, [contextInheritor])

getModuleSymbol (moduleName, symbolName, [typeName], [contextInheritor])

返回特定模块中全局符号的 对象。 模块名称和符号名称是字符串。

如果提供了可选的 contextInheritor 参数,则将在同一上下文中查找模块和符号, (地址空间、调试目标) 作为传递的对象。 如果未提供参数,则将在调试器的当前上下文中查找模块和符号。 不是一次性测试脚本的 JavaScript 扩展应始终提供显式上下文。

如果提供了可选的 typeName 参数,则将假定符号为传递的类型,并且符号 () 中指示的类型将被忽略。 请注意,任何预期对模块的公共符号进行操作的调用方都应始终提供显式类型名称。

host.getModuleContainingSymbol

getModuleContainingSymbol (location, [contextInheritor])

返回符号 (例如:包含给定地址的函数或数据) 。 请注意,仅当模块具有包含给定地址的 专用 符号时,此功能才起作用。

如果提供了可选的 contextInheritor 参数,则将在同一上下文中查找模块和符号, (地址空间、调试目标) 作为传递的对象。 如果未提供参数,则将在调试器的当前上下文中查找模块和符号。 不是一次性测试脚本的 JavaScript 扩展应始终提供显式上下文。

host.createPointerObject

createPointerObject (address, moduleName, typeName, [contextInheritor])

在指定的地址或位置创建指针对象。 模块名称和类型名称是字符串。

如果提供了可选的 contextInheritor 参数,则将在同一上下文中查找模块和符号, (地址空间、调试目标) 作为传递的对象。 如果未提供参数,则将在调试器的当前上下文中查找模块和符号。 不是一次性测试脚本的 JavaScript 扩展应始终提供显式上下文。

host.createTypedObject

createTypedObject (location, moduleName, typeName, [contextInheritor])

创建一个 对象,该对象表示指定位置的调试目标的地址空间内的本机类型化对象。 模块名称和类型名称是字符串。

如果提供了可选的 contextInheritor 参数,则将在同一上下文中查找模块和符号, (地址空间、调试目标) 作为传递的对象。 如果未提供参数,则将在调试器的当前上下文中查找模块和符号。 不是一次性测试脚本的 JavaScript 扩展应始终提供显式上下文。

适用于 JavaScript 扩展的主机 API

JavaScript 提供程序将名为 host 的对象插入到它加载的每个脚本的全局命名空间中。 此对象提供对脚本关键功能的访问权限以及对调试器命名空间的访问。 它分为两个阶段进行设置。

  • 阶段 1:在执行任何脚本之前,主机对象仅包含脚本初始化自身和注册其扩展点所需的最少功能集, (同时作为生成者和使用者) 。 根和初始化代码不用于操作调试目标的状态或执行复杂操作,因此,直到 initializeScript 方法返回之后,主机才完全填充。

  • 阶段 2:在 initializeScript 返回后,主机对象将填充操作调试目标状态所需的一切。

主机对象级别

一些关键功能直接位于主机对象下。 其余部分为子命名空间。 命名空间包括以下内容。

命名空间 说明
诊断 有助于诊断和调试脚本代码的功能
内存 用于在调试目标中启用内存读取和写入的功能

根级别

直接在主机对象中,可以找到以下属性、方法和构造函数。

名称 签名 阶段呈现 说明
createPointerObject

createPointerObject (address, moduleName, typeName, [contextInheritor])

2 在指定的地址或位置创建指针对象。 模块名称和类型名称是字符串。 可选的 contextInheritor 参数与 getModuleSymbol 一样工作。
createTypedObject

createTypedObject (location, moduleName, typeName, [contextInheritor])

2 创建一个 对象,该对象表示指定位置的调试目标的地址空间内的本机类型化对象。 模块名称和类型名称是字符串。 可选的 contextInheritor 参数与 getModuleSymbol 一样工作。
currentProcess

属性

2 返回表示调试器当前进程的 对象
currentSession

属性

2 返回表示调试器当前会话的对象 (正在调试的目标、转储等...)
currentThread

属性

2 返回表示调试器当前线程的 对象
evaluateExpression

evaluateExpression (表达式,[contextInheritor])

2 这会调用调试主机,以仅使用调试目标的语言计算表达式。 如果提供了可选的 contextInheritor 参数,则将在上下文中计算表达式 (例如:参数的地址空间和调试目标) ;否则,将在调试器的当前上下文中评估它
evaluateExpressionInContext

evaluateExpressionInContext (context, expression)

2 这会调用调试主机,以仅使用调试目标的语言计算表达式。 context 参数指示用于计算的隐式此指针。 表达式将在上下文中计算 (例如:由 context 参数指示的地址空间和调试目标) 。
getModuleSymbol

getModuleSymbol (moduleName, symbolName, [contextInheritor])

2 返回特定模块中全局符号的 对象。 模块名称和符号名称是字符串。 如果提供了可选的 contextInheritor 参数,则将在同一上下文中查找模块和符号, (地址空间、调试目标) 作为传递的对象。 如果未提供参数,则将在调试器的当前上下文中查找模块和符号。 不是一次性脚本的 JavaScript 扩展应始终提供显式上下文
getNamedModel

getNamedModel (modelName)

2 返回针对给定名称注册的数据模型。 请注意,对尚未注册的名称调用此名称是完全合法的。 这样做将为该名称创建存根,并在注册时对实际对象执行存根操作
indexedValue

new indexedValue (值,indexies)

2 对象的构造函数,可以从 JavaScript 迭代器返回,以便将一组默认的索引分配给迭代值。 索引集必须表示为 JavaScript 数组。
Int64

new Int64 (值,[highValue])

1 这将构造一个 Int64 类型的库。 单个参数版本将采用任何可以打包到 Int64 (中且无需转换) 的值,并将其放入此类值中。 如果提供了可选的第二个参数,则第一个参数的转换将打包到较低的 32 位中,第二个参数的转换将打包到较高的 32 位中。
namedModelParent

new namedModelParent (对象,name)

1 对象的构造函数,用于放置在从 initializeScript 返回的数组中,这表示使用 JavaScript 原型或 ES6 类作为具有给定名称的数据模型父扩展
namedModelRegistration

new namedModelRegistration (对象,name)

1 对象的构造函数,旨在放置在从 initializeScript 返回的数组中,这表示通过已知名称将 JavaScript 原型或 ES6 类注册为数据模型,以便其他扩展可以查找和扩展
命名空间

属性

2 允许直接访问调试器的根命名空间。 例如,可以通过 host.namespace.Debugger.Sessions.First () 访问第一个调试目标的进程列表。使用此属性的进程
registerNamedModel

registerNamedModel (对象,modelName)

2 这会将 JavaScript 原型或 ES6 类注册为给定名称下的数据模型。 此类注册允许由其他脚本或其他调试器扩展定位和扩展原型或类。 请注意,脚本应倾向于从其 initializeScript 方法返回 namedModelRegistration 对象,而不是以命令性方式执行此操作。 任何以命令性方式进行更改的脚本都需要具有 initializeScript 方法才能进行清理。
registerExtensionForTypeSignature

registerExtensionForTypeSignature (对象,typeSignature)

2 这会将 JavaScript 原型或 ES6 类注册为本机类型的扩展数据模型,由所提供的类型签名提供。 请注意,脚本应倾向于从其 initializeScript 方法返回 typeSignatureExtension 对象,而不是以命令性方式执行此操作。 任何以命令性方式进行更改的脚本都需要具有 initializeScript 方法才能进行清理。
registerPrototypeForTypeSignature

registerPrototypeForTypeSignature (对象,typeSignature)

2 这会将 JavaScript 原型或 ES6 类注册为规范数据模型, (例如:提供的类型签名提供的本机类型的可视化工具) 。 请注意,脚本应倾向于从其 initializeScript 方法返回 typeSignatureRegistration 对象,而不是以命令方式执行此操作。 任何以命令性方式进行更改的脚本都需要具有 uninitializeScript方法才能进行清理。
parseInt64

parseInt64 (字符串,[radix])

1 此方法与标准 JavaScript parseInt 方法类似,只不过它返回 Int64 类型库。 如果提供了基数,则分析将按指示在基 2、8、10 或 16 中发生。
typeSignatureExtension

new typeSignatureExtension (object, typeSignature, [moduleName], [minVersion], [maxVersion])

1 对象的构造函数,旨在放置在从 initializeScript 返回的数组中,这表示通过 JavaScript 原型或 ES6 类的类型签名描述的本机类型的扩展。 此类注册“添加字段”到调试器的任何匹配签名类型的可视化效果中,而不是完全接管它。 可选的模块名称和版本可以限制注册。 版本指定为“1.2.3.4”样式字符串。
typeSignatureRegistration

new typeSignatureRegistration (object, typeSignature, [moduleName], [minVersion], [maxVersion])

1 对象的构造函数,用于放置在从 initializeScript 返回的数组中,它表示针对本机类型签名的 JavaScript 原型或 ES6 类的规范注册。 此类注册“接管”调试器对与签名匹配的任何类型的可视化效果,而不仅仅是扩展签名。 可选的模块名称和版本可以限制注册。 版本指定为“1.2.3.4”样式字符串。
unregisterNamedModel

unregisterNamedModel (modelName)

2 这会通过给定名称取消注册数据模型,从而撤消 registerNamedModel 执行的任何操作
unregisterExtensionForTypeSignature

unregisterExtensionForTypeSignature (object, typeSignature, [moduleName], [minVersion], [maxVersion])

2 这会取消注册 JavaScript 原型或 ES6 类,因为该类是提供的类型签名提供的本机类型的扩展数据模型。 它是 registerExtensionForTypeSignature 的逻辑撤消。 请注意,脚本应倾向于从其 initializeScript 方法返回 typeSignatureExtension 对象,而不是以命令性方式执行此操作。 任何以命令性方式进行更改的脚本都需要具有 initializeScript 方法才能进行清理。 可选的模块名称和版本可以限制注册。 版本指定为“1.2.3.4”样式字符串。
unregisterPrototypeForTypeSignature

unregisterPrototypeForTypeSignature (object, typeSignature, [moduleName], [minVersion], [maxVersion])

2 这会取消注册 JavaScript 原型或 ES6 类作为规范数据模型 (例如:由所提供的类型签名提供的本机类型的可视化工具) 。 它是 registerPrototypeForTypeSignature 的逻辑撤消。 请注意,脚本应倾向于从其 initializeScript 方法返回 typeSignatureRegistration 对象,而不是以命令方式执行此操作。 任何以命令性方式进行更改的脚本都需要具有 uninitializeScript 方法才能进行清理。 可选的模块名称和版本可以限制注册。 版本指定为“1.2.3.4”样式字符串。

诊断功能

主机对象的诊断子命名空间包含以下内容。

名称 签名 阶段呈现 说明
debugLog debugLog (对象...) 1 这为脚本扩展提供 printf 样式调试。 目前,debugLog 的输出已路由到调试器的输出控制台。 在以后的某个时间点,计划提供此输出路由的灵活性。 注意:不应将其用作将用户输出打印到控制台的方式。 将来可能不会路由到那里。

内存功能

主机对象的内存子命名空间包含以下内容。

名称 签名 阶段呈现 说明
readMemoryValues

readMemoryValues (location, numElements, [elementSize], [isSigned], [contextInheritor])

2 这会从调试目标的地址空间中读取值的原始数组,并将类型化数组放置在此内存视图的顶部。 提供的位置可以是地址 (64 位值) 、位置对象或本机指针。 数组的大小由 numElements 参数指示。 数组中每个元素的大小 (和类型) 由可选的 elementSizeisSigned 参数提供。 如果未提供此类参数,则默认值为无符号 (字节/1 字节) 。 如果提供了可选的 contextInheritor 参数,则会在上下文 (读取内存,例如:参数指示的地址空间和调试目标) ;否则,将从调试器的当前上下文中读取它。 请注意,对 8 位、16 位和 32 位值使用此方法会导致在读取内存上放置快速类型化视图。 对 64 位值使用此方法会导致构造 64 位库类型的数组,这要高得多!
readString

readString (location, [contextInheritor])

readString (location, [length], [contextInheritor])

2 这会从调试目标的地址空间中读取窄 (当前代码页) 字符串,将其转换为 UTF-16,并将结果作为 JavaScript 字符串返回。 如果无法读取内存,可能会引发异常。 提供的位置可以是地址 (64 位值) 、位置对象或本机字符。 如果提供了可选的 contextInheritor 参数,则会在上下文 (读取内存,例如:参数指示的地址空间和调试目标) ;否则,将从调试器的当前上下文中读取它。 如果提供了可选的 length 参数,则读取字符串将采用指定的长度。
readWideString

readWideString (location, [contextInheritor])

readWideString (location, [length], [contextInheritor])

2 这会从调试目标的地址空间读取宽 (UTF-16) 字符串,并将结果作为 JavaScript 字符串返回。 如果无法读取内存,可能会引发异常。 提供的位置可以是地址 (64 位值) 、位置对象或本机wchar_t。 如果提供了可选的 contextInheritor 参数,则会在上下文 (读取内存,例如:参数指示的地址空间和调试目标) ;否则,将从调试器的当前上下文中读取它。 如果提供了可选的 length 参数,则读取字符串将采用指定的长度。

JavaScript 中的数据模型概念

数据模型映射

以下数据模型概念映射到 JavaScript。

概念 本机接口 JavaScript 等效项
字符串转换 IStringDisplayableConcept standard:toString (...) {...}
可迭代性 IIterableConcept standard: [Symbol.iterator] () {...}
可索引性 IIndexableConcept 协议:getDimensionality (...) / getValueAt (...) / setValueAt (...)
运行时类型转换 IPreferredRuntimeTypeConcept 协议:getPreferredRuntimeTypedObject (...)

字符串转换

(IStringDisplayableConcept) 字符串转换概念直接转换为标准 JavaScript toString 方法。 由于所有 JavaScript 对象都具有由 Object.prototype 提供的字符串转换 ((如果未在) 的其他位置提供),因此返回到数据模型的每个 JavaScript 对象都可以转换为显示字符串。 重写字符串转换只需实现自己的 toString。

class myObject
{
    //
    // This method will be called whenever any native code calls IStringDisplayableConcept::ToDisplayString(...)
    //
    toString()
    { 
        return "This is my own string conversion!";
    }
}

可迭代性

数据模型关于对象是否可迭代的概念直接映射到对象是否可迭代的 ES6 协议。 任何具有 [Symbol.iterator] 方法的对象都被视为可迭代。 实现此类将使对象可迭代。

仅可迭代的对象可以具有如下所示的实现。

class myObject
{
    //
    // This method will be called whenever any native code calls IIterableConcept::GetIterator
    //
    *[Symbol.iterator]()
    {
        yield "First Value";
        yield "Second Value";
        yield "Third Value";
    }
}

对于既可迭代又可索引的对象,必须给予特殊考虑,因为从迭代器返回的对象必须包含索引以及通过特殊返回类型的值。

可迭代和可索引

可迭代且可索引的对象需要迭代器的特殊返回值。 迭代器生成 indexedValue 的实例,而不是生成值。 索引作为第二个参数中的数组传递给 indexedValue 构造函数。 它们可以是多维的,但必须与索引器协议中返回的维数匹配。

此代码显示一个示例实现。

class myObject
{
    //
    // This method will be called whenever any native code calls IIterableConcept::GetIterator
    //
    *[Symbol.iterator]()
    {
        //
        // Consider this a map which mapped 42->"First Value", 99->"Second Value", and 107->"Third Value"
        //
        yield new host.indexedValue("First Value", [42]);
        yield new host.indexedValue("Second Value", [99]);
        yield new host.indexedValue("Third Value", [107]);
    }
}

可索引性

与 JavaScript 不同,数据模型在属性访问和索引之间进行了非常明确的区分。 任何希望在数据模型中显示为可编制索引的 JavaScript 对象都必须实现一个协议,该协议由 getDimensionality 方法组成,该方法返回索引器的维度,以及一对可选 getValueAt 和 setValueAt 方法,这些方法在提供的索引处执行对象的读取和写入。 如果对象是只读的或只写的,可以省略 getValueAt 或 setValueAt 方法

class myObject
{
    //
    // This method will be called whenever any native code calls IIndexableConcept::GetDimensionality or IIterableConcept::GetDefaultIndexDimensionality
    //
    getDimensionality()
    {
        //
        // Pretend we are a two dimensional array.
        //
        return 2;
    } 

    //
    // This method will be called whenever any native code calls IIndexableConcept::GetAt
    //
    getValueAt(row, column)
    {
        return this.__values[row * this.__columnCount + column];
    }

    //
    // This method will be called whenever any native code calls IIndexableConcept::SetAt
    //
    setValueAt(value, row, column)
    {
        this.__values[row * this.__columnCount + column] = value;
    }
}

运行时类型转换

这仅适用于针对类型系统注册的 JavaScript 原型/类 (本机) 类型。 调试器通常能够执行分析 (例如Run-Time类型信息 (RTTI) /v-table 分析) ,以便从用代码表示的静态类型确定对象的真实运行时类型。 针对本机类型注册的数据模型可以通过 IPreferredRuntimeTypeConcept 的实现来替代此行为。 同样,针对本机对象注册的 JavaScript 类或原型可以通过实现由 getPreferredRuntimeTypedObject 方法组成的协议来提供自己的实现。

请注意,虽然此方法在技术上可以返回任何内容,但它返回的内容实际上不是运行时类型或派生类型,这被视为错误的形式。 这可能会导致调试器用户的严重混淆。 但是,重写此方法对于实现的 C 样式标头+对象样式等内容非常有用。

class myNativeModel
{
    //
    // This method will be called whenever the data model calls IPreferredRuntimeTypeConcept::CastToPreferredRuntimeType
    //
    getPreferredRuntimeTypedObject()
    {
        var loc = this.targetLocation;

        //
        // Perform analysis...
        //
        var runtimeLoc = loc.Add(runtimeObjectOffset);
  
        return host.createTypedObject(runtimeLoc, runtimeModule, runtimeTypeName);
    }
}

另请参阅

JavaScript 扩展中的本机调试器对象

JavaScript 扩展中的本机调试器对象 - 设计和测试注意事项

JavaScript 调试器脚本

JavaScript 调试器示例脚本