Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Toto téma popisuje, jak pomocí JavaScriptu vytvářet skripty, které rozumí objektům ladicího programu a rozšiřují a přizpůsobují možnosti ladicího programu.
Přehled skriptování ladicího programu JavaScriptu
Zprostředkovatelé skriptů přemosťují skriptovací jazyk k internímu objektovému modelu ladicího programu. Zprostředkovatel skriptování ladicího programu JavaScriptu umožňuje použití JavaScriptu s ladicím programem.
Při načtení JavaScriptu pomocí příkazu .scriptload se spustí kořenový kód skriptu, názvy, které jsou přítomné ve skriptu, jsou překlenuty do kořenového oboru názvů ladicího programu (dx Debugger) a skript zůstane v paměti, dokud se nenačte a všechny odkazy na jeho objekty se uvolní. Skript může poskytovat nové funkce vyhodnocovače výrazů ladicího programu, upravit objektový model ladicího programu nebo může fungovat jako vizualizér stejným způsobem jako vizualizér NatVis.
Toto téma popisuje některé možnosti skriptování ladicího programu JavaScriptu.
Tato dvě témata poskytují další informace o práci s JavaScriptem v ladicím programu.
Ukázkové skripty ladicího programu JavaScriptu
Nativní objekty v rozšířeních JavaScriptu
Video skriptování JavaScriptu
Defrag Tools #170 – Andy a Bill demonstrují rozšiřitelnost JavaScriptu a možnosti skriptování v ladicím programu.
Poskytovatel ladicího programu JavaScript
Zprostředkovatel JavaScriptu, který je součástí ladicího programu, plně využívá nejnovější vylepšení objektů a tříd ECMAScript6. Další informace najdete v tématu ECMAScript 6 – nové funkce: Přehled a porovnání.
JsProvider.dll
JsProvider.dll je zprostředkovatel JavaScriptu, který se načte, aby podporoval skriptování ladicího programu Jazyka JavaScript.
Požadavky
Skriptování ladicího programu JavaScriptu je navržené tak, aby fungovalo se všemi podporovanými verzemi Windows.
Načtení zprostředkovatele skriptování JavaScriptu
Před použitím některého z příkazů .script je nutné načíst poskytovatele skriptování. Pomocí příkazu .scriptproviders potvrďte, že poskytovatel JavaScriptu je načtený.
0:000> .scriptproviders
Available Script Providers:
NatVis (extension '.NatVis')
JavaScript (extension '.js')
Skriptovací meta příkazy JavaScriptu
Pro práci se skriptováním ladicího programu JavaScriptu jsou k dispozici následující příkazy.
- .scriptproviders (seznam zprostředkovatelů skriptů)
- .scriptload (Načtení skriptu)
- .scriptunload (Unload Script)
- .scriptrun (spustit skript)
- .scriptlist (seznam načtených skriptů)
Požadavky
Než začnete používat některý z příkazů .script, je potřeba zavést zprostředkovatele skriptování. Použijte příkaz .scriptproviders k potvrzení, že je načten poskytovatel JavaScriptu.
0:000> .scriptproviders
Available Script Providers:
NatVis (extension '.NatVis')
JavaScript (extension '.js')
.scriptproviders (seznam zprostředkovatelů skriptů)
Příkaz .scriptproviders zobrazí seznam všech jazyků skriptů, které jsou v současnosti srozumitelné ladicím programem a rozšířením, pod kterým jsou registrovány.
V následujícím příkladu se načítají poskytovatelé pro JavaScript a NatVis.
0:000> .scriptproviders
Available Script Providers:
NatVis (extension '.NatVis')
JavaScript (extension '.js')
Jakýkoli soubor končící na ". NatVis" se rozumí jako skript NatVis a jakýkoli soubor končící na ".js" je považován za javascriptový skript. Pomocí příkazu .scriptload lze načíst některý typ skriptu.
Další informace naleznete v tématu .scriptproviders (seznam zprostředkovatelů skriptů)
.scriptload (načtení skriptu)
Příkaz .scriptload načte skript a spustí kořenový kód skriptu a funkci initializeScript . Pokud při počátečním načtení a spuštění skriptu dojde k nějakým chybám, zobrazí se v konzole chyby. Následující příkaz ukazuje úspěšné načtení TestScript.js.
0:000> .scriptload C:\WinDbg\Scripts\TestScript.js
JavaScript script successfully loaded from 'C:\WinDbg\Scripts\TestScript.js'
Všechny manipulace s objektovým modelem vytvořené skriptem zůstanou na místě, dokud se skript následně nenačte nebo se znovu spustí s jiným obsahem.
Další informace najdete v tématu .scriptload (načtení skriptu)
.scriptrun
Příkaz .scriptrun načte skript, spustí kořenový kód skriptu, inicializujeScript a funkci invokeScript . Pokud při počátečním načtení a spuštění skriptu dojde k nějakým chybám, zobrazí se v konzole chyby.
0:000> .scriptrun C:\WinDbg\Scripts\helloWorld.js
JavaScript script successfully loaded from 'C:\WinDbg\Scripts\helloWorld.js'
Hello World! We are in JavaScript!
Všechny manipulace s objektovým modelem ladicího programu vytvořené skriptem zůstanou na místě, dokud se skript následně nenačte nebo se znovu spustí s jiným obsahem.
Další informace naleznete v tématu .scriptrun (spustit skript).
.scriptunload (odstranit skript)
Příkaz .scriptunload odstraní načtený skript a zavolá funkci uninitializeScript. K uvolnění skriptu použijte následující syntaxi příkazu.
0:000:x86> .scriptunload C:\WinDbg\Scripts\TestScript.js
JavaScript script unloaded from 'C:\WinDbg\Scripts\TestScript.js'
Další informace naleznete v tématu .scriptunload (Unload Script).
.scriptlist (seznam načtených skriptů)
Příkaz .scriptlist zobrazí seznam všech skriptů, které byly načteny prostřednictvím .scriptload nebo příkazu .scriptrun. Pokud byl testScript úspěšně načten pomocí .scriptload, příkaz .scriptlist zobrazí název načteného skriptu.
0:000> .scriptlist
Command Loaded Scripts:
JavaScript script from 'C:\WinDbg\Scripts\TestScript.js'
Další informace naleznete v tématu .scriptlist (seznam načtených skriptů).
Začínáme se skriptováním ladicího programu JavaScriptu
Ukázkový skript HelloWorld
Tato část popisuje, jak vytvořit a spustit jednoduchý ladicí skript JavaScript, který vytiskne Hello World.
// WinDbg JavaScript sample
// Prints Hello World
function initializeScript()
{
host.diagnostics.debugLog("***> Hello World! \n");
}
Pomocí textového editoru, jako je Poznámkový blok, vytvořte textový soubor s názvem HelloWorld.js , který obsahuje výše uvedený kód JavaScriptu.
K načtení a spuštění skriptu použijte příkaz .scriptload. Protože jsme použili název funkce initializeScript, kód ve funkci se spustí při načtení skriptu.
0:000> .scriptload c:\WinDbg\Scripts\HelloWorld.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\HelloWorld.js'
***> Hello World!
Po načtení skriptu je v ladicím programu k dispozici další funkce. Pomocí příkazu dx (Display NatVis Expression) zobrazte Debugger.State.Scripts , abyste viděli, že náš skript je teď rezidentní.
0:000> dx Debugger.State.Scripts
Debugger.State.Scripts
HelloWorld
V dalším příkladu přidáme pojmenovanou funkci a zavoláme ji.
Přidání dvou hodnot – ukázkový skript
Tato část popisuje, jak vytvořit a spustit jednoduchý javascriptový ladicí program, který přidá vstup a přidá dvě čísla.
Tento jednoduchý skript poskytuje jednu funkci addTwoValues.
// WinDbg JavaScript sample
// Adds two functions
function addTwoValues(a, b)
{
return a + b;
}
Vytvoření textového souboru s názvem FirstSampleFunction.js pomocí textového editoru, jako je Poznámkový blok
K načtení skriptu použijte příkaz .scriptload.
0:000> .scriptload c:\WinDbg\Scripts\FirstSampleFunction.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\FirstSampleFunction.js'
Po načtení skriptu je v ladicím programu k dispozici další funkce. Pomocí příkazu dx (Display NatVis Expression) zobrazte Debugger.State.Scripts , abyste viděli, že náš skript je teď rezidentní.
0:000> dx Debugger.State.Scripts
Debugger.State.Scripts
FirstSampleFunction
Můžeme vybrat FirstSampleFunction, abychom viděli, jaké funkce poskytuje.
0:000> dx -r1 -v Debugger.State.Scripts.FirstSampleFunction.Contents
Debugger.State.Scripts.FirstSampleFunction.Contents : [object Object]
host : [object Object]
addTwoValues
...
Pokud chcete, aby byl skript trochu pohodlnější pro práci, přiřaďte proměnnou v ladicím programu pro uložení obsahu skriptu pomocí příkazu dx.
0:000> dx @$myScript = Debugger.State.Scripts.FirstSampleFunction.Contents
Pomocí vyhodnocovače výrazů dx volejte funkci addTwoValues.
0:000> dx @$myScript.addTwoValues(10, 41),d
@$myScript.addTwoValues(10, 41),d : 51
Můžete také použít @$scriptContents předdefinovaný alias pro práci se skripty. Alias @$scriptContents sloučí všechny .Obsahy všech načtených skriptů.
0:001> dx @$scriptContents.addTwoValues(10, 40),d
@$scriptContents.addTwoValues(10, 40),d : 50
Až budete s skriptem hotovi, použijte k uvolnění skriptu příkaz .scriptunload.
0:000> .scriptunload c:\WinDbg\Scripts\FirstSampleFunction.js
JavaScript script successfully unloaded from 'c:\WinDbg\Scripts\FirstSampleFunction.js'
Automatizace příkazů ladicího programu
Tato část popisuje, jak vytvořit a spustit jednoduchý skript ladicího programu JavaScriptu, který automatizuje odesílání příkazu u (Unassemble). Příklad rovněž demonstruje, jak shromáždit a zobrazit výstup příkazu ve smyčce.
Tento skript poskytuje jednu funkci RunCommands().
// WinDbg JavaScript sample
// Shows how to call a debugger command and display results
"use strict";
function RunCommands()
{
var ctl = host.namespace.Debugger.Utility.Control;
var output = ctl.ExecuteCommand("u");
host.diagnostics.debugLog("***> Displaying command output \n");
for (var line of output)
{
host.diagnostics.debugLog(" ", line, "\n");
}
host.diagnostics.debugLog("***> Exiting RunCommands Function \n");
}
Vytvoření textového souboru s názvem RunCommands.js pomocí textového editoru, jako je Poznámkový blok
Pomocí příkazu .scriptload načtěte skript RunCommands.
0:000> .scriptload c:\WinDbg\Scripts\RunCommands.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\RunCommands.js'
Po načtení skriptu je v ladicím programu k dispozici další funkce. Pomocí příkazu dx (Display NatVis Expression) zobrazte Debugger.State.Scripts.RunCommands , abyste viděli, že náš skript je nyní rezidentní.
0:000>dx -r3 Debugger.State.Scripts.RunCommands
Debugger.State.Scripts.RunCommands
Contents : [object Object]
host : [object Object]
diagnostics : [object Object]
namespace
currentSession : Live user mode: <Local>
currentProcess : notepad.exe
currentThread : ntdll!DbgUiRemoteBreakin (00007ffd`87f2f440)
memory : [object Object]
Pomocí příkazu dx volejte funkci RunCommands ve skriptu RunCommands.
0:000> dx Debugger.State.Scripts.RunCommands.Contents.RunCommands()
***> Displaying command output
ntdll!ExpInterlockedPopEntrySListEnd+0x17 [d:\rs1\minkernel\ntos\rtl\amd64\slist.asm @ 196]:
00007ffd`87f06e67 cc int 3
00007ffd`87f06e68 cc int 3
00007ffd`87f06e69 0f1f8000000000 nop dword ptr [rax]
ntdll!RtlpInterlockedPushEntrySList [d:\rs1\minkernel\ntos\rtl\amd64\slist.asm @ 229]:
00007ffd`87f06e70 0f0d09 prefetchw [rcx]
00007ffd`87f06e73 53 push rbx
00007ffd`87f06e74 4c8bd1 mov r10,rcx
00007ffd`87f06e77 488bca mov rcx,rdx
00007ffd`87f06e7a 4c8bda mov r11,rdx
***> Exiting RunCommands Function
Speciální funkce ladicího programu JavaScriptu
V javascriptovém skriptu, který volá samotný poskytovatel skriptu, existuje několik speciálních funkcí.
initializeScript
Když se skript JavaScriptu načte a spustí, prochází řadou kroků, než proměnné, funkce a další objekty ve skriptu ovlivní objektový model ladicího programu.
- Skript se načte do paměti a parsuje se.
- Spustí se kořenový kód ve skriptu.
- Pokud skript má metodu s názvem initializeScript, tato metoda je volána.
- Návratová hodnota z initializeScript se používá k určení, jak automaticky upravit objektový model ladicího programu.
- Názvy ve skriptu se přespojují do prostoru názvů ladicího programu.
Jak už bylo zmíněno, inicializaceScriptu bude volána okamžitě po spuštění kořenového kódu skriptu. Jeho úlohou je vrátit javascriptové pole registračních objektů zprostředkovateli označující, jak upravit objektový model ladicího programu.
function initializeScript()
{
// Add code here that you want to run every time the script is loaded.
// We will just send a message to indicate that function was called.
host.diagnostics.debugLog("***> initializeScript was called\n");
}
invokeScript
InvokeScript je primární metoda skriptu a je volána při spuštění .scriptload a .scriptrun.
function invokeScript()
{
// Add code here that you want to run every time the script is executed.
// We will just send a message to indicate that function was called.
host.diagnostics.debugLog("***> invokeScript was called\n");
}
uninitializeScript
Metoda uninitializeScript je behaviorálním opakem initializeScript. Volá se, když je skript odpojený a připravuje se k uvolnění. Jeho úlohou je vrátit zpět všechny změny objektového modelu, které skript provedl imperativním způsobem během provádění a/nebo zničit všechny objekty, které skript ukládá do mezipaměti.
Pokud skript neprovádí imperativní manipulaci s objektovým modelem ani ukládá výsledky do mezipaměti, nemusí mít neinicializovatscriptovou metodu. Všechny změny objektového modelu provedené podle návratové hodnoty initializeScript se automaticky zruší poskytovatelem. Takové změny nevyžadují explicitní metodu uninitializeScript.
function uninitializeScript()
{
// Add code here that you want to run every time the script is unloaded.
// We will just send a message to indicate that function was called.
host.diagnostics.debugLog("***> uninitialize was called\n");
}
Shrnutí funkcí volaných příkazy skriptu
Tato tabulka shrnuje, které funkce volají příkazy skriptu.
| Příkaz | .scriptload | .scriptrun (spustit skript) | .scriptunload (Unload Script) |
|---|---|---|---|
| kořen | Ano | Ano | |
| initializeScript | Ano | Ano | |
| invokeScript | Ano | ||
| uninitializeScript | Ano |
Pomocí tohoto ukázkového kódu zjistíte, kdy je každá funkce volána při načítání, spouštění a uvolnění skriptu.
// Root of Script
host.diagnostics.debugLog("***>; Code at the very top (root) of the script is always run \n");
function initializeScript()
{
// Add code here that you want to run every time the script is loaded.
// We will just send a message to indicate that function was called.
host.diagnostics.debugLog("***>; initializeScript was called \n");
}
function invokeScript()
{
// Add code here that you want to run every time the script is executed.
// We will just send a message to indicate that function was called.
host.diagnostics.debugLog("***>; invokeScript was called \n");
}
function uninitializeScript()
{
// Add code here that you want to run every time the script is unloaded.
// We will just send a message to indicate that function was called.
host.diagnostics.debugLog("***>; uninitialize was called\n");
}
function main()
{
// main is just another function name in JavaScript
// main is not called by .scriptload or .scriptrun
host.diagnostics.debugLog("***>; main was called \n");
}
Vytvoření vizualizéru ladicího programu v JavaScriptu
Vlastní soubory vizualizací umožňují seskupit a uspořádat data ve struktuře vizualizace, která lépe odráží vztahy dat a obsah. Rozšíření ladicího programu JavaScriptu můžete použít k zápisu vizualizérů ladicího programu, které fungují podobným způsobem jako NatVis. Toho se dosahuje prostřednictvím vytváření prototypu javascriptového objektu (nebo třídy ES6), který funguje jako vizualizér pro daný datový typ. Další informace o NatVis a ladicím programu viz dx (Display NatVis Expression).
Příklad třídy – Simple1DArray
Představte si příklad třídy C++, která představuje jednorozměrné pole. Tato třída má dva členy, m_size což je celková velikost pole a m_pValues, což je ukazatel na počet int v paměti rovna m_size pole.
class Simple1DArray
{
private:
ULONG64 m_size;
int *m_pValues;
};
Pomocí příkazu dx se můžeme podívat na výchozí vykreslování datové struktury.
0:000> dx g_array1D
g_array1D [Type: Simple1DArray]
[+0x000] m_size : 0x5 [Type: unsigned __int64]
[+0x008] m_pValues : 0x8be32449e0 : 0 [Type: int *]
Vizualizér JavaScriptu
Abychom mohli tento typ vizualizovat, musíme vytvořit prototyp třídy (nebo ES6), která má všechna pole a vlastnosti, které chce ladicí program zobrazit. Potřebujeme také, aby metoda initializeScript vrátila objekt, který říká poskytovateli JavaScriptu, aby pro daný typ propojil náš prototyp jako visualizer.
function initializeScript()
{
//
// Define a visualizer class for the object.
//
class myVisualizer
{
//
// Create an ES6 generator function which yields back all the values in the array.
//
*[Symbol.iterator]()
{
var size = this.m_size;
var ptr = this.m_pValues;
for (var i = 0; i < size; ++i)
{
yield ptr.dereference();
//
// Note that the .add(1) method here is effectively doing pointer arithmetic on
// the underlying pointer. It is moving forward by the size of 1 object.
//
ptr = ptr.add(1);
}
}
}
return [new host.typeSignatureRegistration(myVisualizer, "Simple1DArray")];
}
Uložte skript do souboru s názvem arrayVisualizer.js.
K načtení zprostředkovatele JavaScriptu použijte příkaz .load (Load Extension DLL).
0:000> .load C:\ScriptProviders\jsprovider.dll
Použijte .scriptload k načtení skriptu vizualizace pole.
0:000> .scriptload c:\WinDbg\Scripts\arrayVisualizer.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\arrayVisualizer.js'
Když se teď použije příkaz dx, vizualizér skriptů zobrazí řádky s obsahem pole.
0:000> dx g_array1D
g_array1D : [object Object] [Type: Simple1DArray]
[<Raw View>] [Type: Simple1DArray]
[0x0] : 0x0
[0x1] : 0x1
[0x2] : 0x2
[0x3] : 0x3
[0x4] : 0x4
Kromě toho tato vizualizace v JavaScriptu poskytuje funkce LINQ, například Vybrat.
0:000> dx g_array1D.Select(n => n * 3),d
g_array1D.Select(n => n * 3),d
[0] : 0
[1] : 3
[2] : 6
[3] : 9
[4] : 12
Co ovlivňuje vizualizaci
Prototyp nebo třída, která je vizualizérem pro nativní typ prostřednictvím návratu host.typeSignatureRegistration objektu z initializeScriptu, bude mít všechny vlastnosti a metody v JavaScriptu přidané do nativního typu. Kromě toho platí následující sémantika:
Ve vizualizaci bude k dispozici libovolný název, který nezačíná dvěma podtržítky (__).
Názvy, které jsou součástí standardních javascriptových objektů nebo jsou součástí protokolů, které zprostředkovatel JavaScriptu vytvoří, se ve vizualizaci nezobrazí.
Objekt lze učinit iterovatelným prostřednictvím [Symbol.iterator].
Objekt lze indexovat pomocí podpory vlastního protokolu sestávajícího z několika funkcí: getDimensionality, getValueAt a volitelně setValueAt.
Nativní a javascriptový objektový most
Most mezi JavaScriptem a objektovým modelem ladicího programu je obousměrný. Nativní objekty lze předat do JavaScriptu a javascriptové objekty lze předat do vyhodnocovače výrazů ladicího programu. Jako příklad vezměte v úvahu přidání následující metody v našem skriptu:
function multiplyBySeven(val)
{
return val * 7;
}
Tuto metodu teď můžete využít v příkladu dotazu LINQ výše. Nejprve načteme vizualizaci JavaScriptu.
0:000> .scriptload c:\WinDbg\Scripts\arrayVisualizer2.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\arrayVisualizer2.js'
0:000> dx @$myScript = Debugger.State.Scripts.arrayVisualizer2.Contents
Potom můžeme použít funkci multiplyBySeven přímo vepsanou, jak je ukázáno níže.
0:000> dx g_array1D.Select(@$myScript.multiplyBySeven),d
g_array1D.Select(@$myScript.multiplyBySeven),d
[0] : 0
[1] : 7
[2] : 14
[3] : 21
[4] : 28
Podmíněné zarážky pomocí JavaScriptu
Pomocí JavaScriptu můžete provést doplňkové zpracování po dosažení zarážky. Skript lze například použít k prozkoumání jiných hodnot doby běhu a pak určit, jestli chcete automaticky pokračovat v provádění kódu nebo zastavit a provést další ruční ladění.
Obecné informace o práci se zarážkami naleznete v tématu Metody řízení zarážek.
DebugHandler.js ukázkový skript pro zpracování zarážek
Tento příklad vyhodnotí dialogové okno pro otevření a uložení v Poznámkovém bloku: notepad!ShowOpenSaveDialog. Tento skript vyhodnotí proměnnou pszCaption a určí, jestli je aktuální dialog dialog "Otevřít", nebo pokud se jedná o dialogové okno Uložit jako. Pokud se jedná o otevřené dialogové okno, provádění kódu bude pokračovat. Pokud se jedná o dialogové okno Uložit jako, spuštění kódu se zastaví a ladicí program zasáhne.
// Use JavaScript strict mode
"use strict";
// Define the invokeScript method to handle breakpoints
function invokeScript()
{
var ctl = host.namespace.Debugger.Utility.Control;
//Get the address of my string
var address = host.evaluateExpression("pszCaption");
// The open and save dialogs use the same function
// When we hit the open dialog, continue.
// When we hit the save dialog, break.
if (host.memory.readWideString(address) == "Open") {
// host.diagnostics.debugLog("We're opening, let's continue!\n");
ctl.ExecuteCommand("gc");
}
else
{
//host.diagnostics.debugLog("We're saving, let's break!\n");
}
}
Tento příkaz nastaví zlom na notepad!ShowOpenSaveDialog a při každém dosažení tohoto zlomu spustí výše uvedený skript.
bp notepad!ShowOpenSaveDialog ".scriptrun C:\\WinDbg\\Scripts\\DebugHandler.js"
Když je v poznámkovém bloku vybrána možnost Uložit soubor > , skript se spustí, příkaz g se neodesílá a dojde k přerušení provádění kódu.
JavaScript script successfully loaded from 'C:\WinDbg\Scripts\DebugHandler.js'
notepad!ShowOpenSaveDialog:
00007ff6`f9761884 48895c2408 mov qword ptr [rsp+8],rbx ss:000000db`d2a9f2f0=0000021985fe2060
Práce s 64bitovými hodnotami v rozšířeních JavaScriptu
Tato část popisuje, jak se 64bitové hodnoty předané do rozšíření ladicího programu JavaScriptu chovají. K tomuto problému dochází, protože JavaScript má možnost ukládat čísla pouze pomocí 53bitových bitů.
64bitové a 53bitové úložiště JavaScriptu
Pořadové hodnoty předané do JavaScriptu se obvykle převádí jako čísla v JavaScriptu. Problém spočívá v tom, že čísla JavaScriptu jsou 64bitová čísla s pohyblivou řádovou čárkou s dvojitou přesností. Jakákoli ordinální čísla přesahující 53bitovou hodnotu ztratí přesnost při vstupu do JavaScriptu. To představuje problém s 64bitovými ukazateli a dalšími 64bitovými řadovými hodnotami, které můžou mít příznaky v nejvyšších bajtech. Aby bylo možné to vyřešit, jakákoli 64bitová nativní hodnota (ať už pochází z nativního kódu nebo datového modelu), která vstupuje do JavaScriptu, vstupuje jako typ knihovny – ne jako číslo JavaScriptu. Tento typ knihovny se vrátí zpět do nativního kódu, aniž by se ztratila číselná přesnost.
Automatický převod
Typ knihovny pro 64bitové ordinální hodnoty podporuje převod pomocí metody valueOf v JavaScriptu. Pokud se objekt používá v matematické operaci nebo jiném konstruktoru, který vyžaduje převod hodnoty, převede se automaticky na číslo JavaScriptu. Pokud došlo ke ztrátě přesnosti (hodnota využívá více než 53 bitů pořadové přesnosti), zprostředkovatel JavaScriptu vyvolá výjimku.
Všimněte si, že pokud v JavaScriptu používáte bitové operátory, budete dále omezeni na 32 bitů pořadové přesnosti.
Tento vzorový kód sečte dvě čísla a použije se k otestování převodu 64bitových hodnot.
function playWith64BitValues(a64, b64)
{
// Sum two numbers to demonstrate 64-bit behavior.
//
// Imagine a64==100, b64==1000
// The below would result in sum==1100 as a JavaScript number. No exception is thrown. The values auto-convert.
//
// Imagine a64==2^56, b64=1
// The below will **Throw an Exception**. Conversion to numeric results in loss of precision!
//
var sum = a64 + b64;
host.diagnostics.debugLog("Sum >> ", sum, "\n");
}
function performOp64BitValues(a64, b64, op)
{
//
// Call a data model method passing 64-bit value. There is no loss of precision here. This round trips perfectly.
// For example:
// 0:000> dx @$myScript.playWith64BitValues(0x4444444444444444ull, 0x3333333333333333ull, (x, y) => x + y)
// @$myScript.playWith64BitValues(0x4444444444444444ull, 0x3333333333333333ull, (x, y) => x + y) : 0x7777777777777777
//
return op(a64, b64);
}
Vytvoření textového souboru s názvem PlayWith64BitValues.js pomocí textového editoru, jako je Poznámkový blok
K načtení skriptu použijte příkaz .scriptload.
0:000> .scriptload c:\WinDbg\Scripts\PlayWith64BitValues.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\PlayWith64BitValues.js'
Pokud chcete, aby byl skript trochu pohodlnější pro práci, přiřaďte proměnnou v ladicím programu pro uložení obsahu skriptu pomocí příkazu dx.
0:000> dx @$myScript = Debugger.State.Scripts.PlayWith64BitValues.Contents
Pomocí vyhodnocovače výrazů dx volejte funkci addTwoValues.
Nejprve vypočítáme hodnotu 2^53 =9007199254740992 (hexadecimální 0x20000000000000).
Nejprve k otestování použijeme (2^53) – 2 a zjistíme, že vrátí správnou hodnotu pro součet.
0:000> dx @$myScript.playWith64BitValues(9007199254740990, 9007199254740990)
Sum >> 18014398509481980
Pak vypočítáme (2^53) -1 =9007199254740991. Tato chyba vrátí chybu, která značí, že proces převodu ztratí přesnost, takže se jedná o největší hodnotu, kterou lze použít s metodou sum v javascriptovém kódu.
0:000> dx @$myScript.playWith64BitValues(9007199254740990, 9007199254740991)
Error: 64 bit value loses precision on conversion to number
Volání metody datového modelu s předáváním 64bitových hodnot Tady není žádná ztráta přesnosti.
0:001> dx @$myScript.performOp64BitValues( 0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, (x, y) => x + y)
@$myScript.performOp64BitValues( 0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, (x, y) => x + y) : 0xfffffffffffffffe
Porovnání
64bitový typ knihovny je javascriptový objekt, nikoli typ hodnoty, například číslo JavaScriptu. To má určité důsledky pro operace porovnání. Za normálních okolností by rovnost (==) u objektu značí, že operandy odkazují na stejný objekt a ne na stejnou hodnotu. Zprostředkovatel JavaScriptu to zmírňuje sledováním aktivních odkazů na 64bitové hodnoty a vrácením stejného "neměnného" objektu pro nesbírané 64bitové hodnoty. To znamená, že pro porovnání by došlo k následujícímu.
// Comparison with 64 Bit Values
function comparisonWith64BitValues(a64, b64)
{
//
// No auto-conversion occurs here. This is an *EFFECTIVE* value comparison. This works with ordinals with above 53-bits of precision.
//
var areEqual = (a64 == b64);
host.diagnostics.debugLog("areEqual >> ", areEqual, "\n");
var areNotEqual = (a64 != b64);
host.diagnostics.debugLog("areNotEqual >> ", areNotEqual, "\n");
//
// Auto-conversion occurs here. This will throw if a64 does not pack into a JavaScript number with no loss of precision.
//
var isEqualTo42 = (a64 == 42);
host.diagnostics.debugLog("isEqualTo42 >> ", isEqualTo42, "\n");
var isLess = (a64 < b64);
host.diagnostics.debugLog("isLess >> ", isLess, "\n");
Vytvoření textového souboru s názvem ComparisonWith64BitValues.js pomocí textového editoru, jako je Poznámkový blok
K načtení skriptu použijte příkaz .scriptload.
0:000> .scriptload c:\WinDbg\Scripts\ComparisonWith64BitValues.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\ComparisonWith64BitValues.js'
Pokud chcete, aby byl skript trochu pohodlnější pro práci, přiřaďte proměnnou v ladicím programu pro uložení obsahu skriptu pomocí příkazu dx.
0:000> dx @$myScript = Debugger.State.Scripts.comparisonWith64BitValues.Contents
Nejprve k otestování použijeme (2^53) – 2 a zjistíme, že vrátí očekávané hodnoty.
0:001> dx @$myScript.comparisonWith64BitValues(9007199254740990, 9007199254740990)
areEqual >> true
areNotEqual >> false
isEqualTo42 >> false
isLess >> false
Vyzkoušíme také číslo 42 jako první hodnotu, abychom ověřili, že operátor porovnání funguje tak, jak by měl.
0:001> dx @$myScript.comparisonWith64BitValues(42, 9007199254740990)
areEqual >> false
areNotEqual >> true
isEqualTo42 >> true
isLess >> true
Pak vypočítáme (2^53) -1 =9007199254740991. Tato hodnota vrátí chybu označující, že proces převodu ztratí přesnost, takže se jedná o největší hodnotu, kterou lze použít s relačními operátory v kódu JavaScriptu.
0:000> dx @$myScript.playWith64BitValues(9007199254740990, 9007199254740991)
Error: 64 bit value loses precision on conversion to number
Udržování přesnosti v operacích
Aby bylo možné rozšíření ladicího programu udržovat přesnost, je sada matematických funkcí promítnuta nad 64bitovým typem knihovny. Pokud rozšíření potřebuje (nebo možná bude potřebovat) přesnost nad 53 bitů pro příchozí 64bitové hodnoty, měli byste místo použití standardních operátorů využít následující metody:
| Název metody | Podpis | Description |
|---|---|---|
| jakoČíslo | .asNumber() | Převede 64bitovou hodnotu na číslo JavaScriptu. Pokud dojde ke ztrátě přesnosti, **VYVOLÁ SE VÝJIMKA** |
| convertToNumber | .convertToNumber() | Převede 64bitovou hodnotu na číslo JavaScriptu. Pokud dojde ke ztrátě přesnosti, **NENÍ VYVOLÁN ŽÁDNÁ VÝJIMKA** |
| getLowPart (získat dolní část) | .getLowPart() | Převede nižších 32 bitů 64bitové hodnoty na číslo JavaScriptu. |
| getHighPart | .getHighPart() | Převede nejvyšších 32 bitů 64bitové hodnoty na číslo v JavaScriptu. |
| přidat | .add(value) | Přidá hodnotu k 64bitové hodnotě a vrátí výsledek. |
| odečíst | .subtract(value) | Odečte hodnotu od 64bitové hodnoty a vrátí výsledek. |
| násobit | .multiply(value) | Vynásobí 64bitovou hodnotu zadanou hodnotou a vrátí výsledek. |
| rozdělit | .divide(value) | Vydělí 64bitovou hodnotu zadanou hodnotou a vrátí výsledek. |
| bitwiseAnd | .bitwiseAnd(value) | Vypočítá bitovou operaci AND mezi 64bitovou hodnotou a zadanou hodnotou a vrátí výsledek. |
| bitwiseOr | .bitwiseOr(value) | Vypočítá bitové NEBO 64bitové hodnoty se zadanou hodnotou a vrátí výsledek. |
| bitwiseXor | .bitwiseXor(value) | Vypočítá bitový xor 64bitové hodnoty se zadanou hodnotou a vrátí výsledek. |
| bitwiseShiftLeft | .bitwiseShiftLeft(value) | Posune 64-bitovou hodnotu doleva o daný počet bitů a vrátí výsledek. |
| bitwiseShiftRight | .bitwiseShiftRight(value) | Posune 64bitovou hodnotu doprava o danou hodnotu a vrátí výsledek. |
| toString | .toString([radix]) | Převede 64bitovou hodnotu na zobrazovaný řetězec ve výchozím radixu (nebo volitelně zadaném radixu). |
Tato metoda je také k dispozici.
| Název metody | Podpis | Description |
|---|---|---|
| compareTo | .compareTo(value) | Porovná 64bitovou hodnotu s jinou 64bitovou hodnotou. |
Ladění JavaScriptu
Tato část popisuje, jak používat možnosti ladění skriptů ladicího programu. Ladicí program má integrovanou podporu ladění javascriptových skriptů pomocí příkazu .scriptdebug (Debug JavaScript).
Poznámka:
Pokud chcete použít ladění JavaScriptu s WinDbg, spusťte ladicí program jako správce.
Pomocí tohoto ukázkového kódu můžete prozkoumat ladění JavaScriptu. Pro účely tohoto návodu ho pojmenujeme DebuggableSample.js a uložíme ho do adresáře C:\MyScripts.
"use strict";
class myObj
{
toString()
{
var x = undefined[42];
host.diagnostics.debugLog("BOO!\n");
}
}
class iterObj
{
*[Symbol.iterator]()
{
throw new Error("Oopsies!");
}
}
function foo()
{
return new myObj();
}
function iter()
{
return new iterObj();
}
function throwAndCatch()
{
var outer = undefined;
var someObj = {a : 99, b : {c : 32, d: "Hello World"} };
var curProc = host.currentProcess;
var curThread = host.currentThread;
try
{
var x = undefined[42];
} catch(e)
{
outer = e;
}
host.diagnostics.debugLog("This is a fun test\n");
host.diagnostics.debugLog("Of the script debugger\n");
var foo = {a : 99, b : 72};
host.diagnostics.debugLog("foo.a = ", foo.a, "\n");
return outer;
}
function throwUnhandled()
{
var proc = host.currentProcess;
var thread = host.currentThread;
host.diagnostics.debugLog("Hello... About to throw an exception!\n");
throw new Error("Oh me oh my! This is an unhandled exception!\n");
host.diagnostics.debugLog("Oh... this will never be hit!\n");
return proc;
}
function outer()
{
host.diagnostics.debugLog("inside outer!\n");
var foo = throwAndCatch();
host.diagnostics.debugLog("Caught and returned!\n");
return foo;
}
function outermost()
{
var x = 99;
var result = outer();
var y = 32;
host.diagnostics.debugLog("Test\n");
return result;
}
function initializeScript()
{
//
// Return an array of registration objects to modify the object model of the debugger
// See the following for more details:
//
// https://aka.ms/JsDbgExt
//
}
Načtěte ukázkový skript.
.scriptload C:\MyScripts\DebuggableSample.js
Spusťte aktivně ladění skriptu pomocí příkazu .scriptdebug .
0:000> .scriptdebug C:\MyScripts\DebuggableSample.js
>>> ****** DEBUGGER ENTRY DebuggableSample ******
No active debug event!
>>> Debug [DebuggableSample <No Position>] >
Jakmile se zobrazí výzva >>> Debug [DebuggableSample <No Position>] > a požadavek na vstup, nacházíte se v ladicím skriptu programu.
Pomocí příkazu .help zobrazte seznam příkazů v prostředí ladění JavaScriptu.
>>> Debug [DebuggableSample <No Position>] >.help
Script Debugger Commands (*NOTE* IDs are **PER SCRIPT**):
? .................................. Get help
? <expr> .......................... Evaluate expression <expr> and display result
?? <expr> ......................... Evaluate expression <expr> and display result
| ................................. List available scripts
|<scriptid>s ...................... Switch context to the given script
bc \<bpid\> ......................... Clear breakpoint by specified \<bpid\>
bd \<bpid\> ......................... Disable breakpoint by specified \<bpid\>
be \<bpid\> ......................... Enable breakpoint by specified \<bpid\>
bl ................................ List breakpoints
bp <line>:<column> ................ Set breakpoint at the specified line and column
bp <function-name> ................ Set breakpoint at the (global) function specified by the given name
bpc ............................... Set breakpoint at current location
dv ................................ Display local variables of current frame
g ................................. Continue script
gu ............................... Step out
k ................................. Get stack trace
p ................................. Step over
q ................................. Exit script debugger (resume execution)
sx ................................ Display available events/exceptions to break on
sxe <event> ....................... Enable break on <event>
sxd <event> ....................... Disable break on <event>
t ................................. Step in
.attach <scriptId> ................ Attach debugger to the script specified by <scriptId>
.detach [<scriptId>] .............. Detach debugger from the script specified by <scriptId>
.frame <index> .................... Switch to frame number <index>
.f+ ............................... Switch to next stack frame
.f- ............................... Switch to previous stack frame
.help ............................. Get help
Pomocí příkazu ladicího programu sx pro skripty zobrazte seznam událostí, které můžeme zachytit.
>>> Debug [DebuggableSample <No Position>] >sx
sx
ab [ inactive] .... Break on script abort
eh [ inactive] .... Break on any thrown exception
en [ inactive] .... Break on entry to the script
uh [ active] .... Break on unhandled exception
Pomocí příkazu ladicího programu skriptu sxe zapněte přerušení při vstupu, aby skript zachytil ladicí program, jakmile se v něm spustí jakýkoli kód.
>>> Debug [DebuggableSample <No Position>] >sxe en
sxe en
Event filter 'en' is now active
Ukončete ladicí program skriptu a provedeme volání funkce do skriptu, který spustí ladicí program.
>>> Debug [DebuggableSample <No Position>] >q
V tuto chvíli se vrátíte do normálního ladicího programu. Spuštěním následujícího příkazu zavolejte skript.
dx @$scriptContents.outermost()
Teď jste zpátky v ladicím programu skriptu a jste zastaveni na prvním řádku nejvnější JavaScriptové funkce.
>>> ****** SCRIPT BREAK DebuggableSample [BreakIn] ******
Location: line = 73, column = 5
Text: var x = 99
>>> Debug [DebuggableSample 73:5] >
Kromě zobrazení přerušení v ladicím programu získáte informace o řádku (73) a sloupci (5), kde došlo k přerušení, a také relevantní fragment zdrojového kódu: var x = 99.
Pojďme krok po kroku se posunout na jiné místo ve skriptu.
p
t
p
t
p
p
V tomto okamžiku byste měli být rozděleni do metody throwAndCatch na řádku 34.
...
>>> ****** SCRIPT BREAK DebuggableSample [Step Complete] ******
Location: line = 34, column = 5
Text: var curProc = host.currentProcess
Můžete to ověřit spuštěním trasování zásobníku.
>>> Debug [DebuggableSample 34:5] >k
k
## Function Pos Source Snippet
-> [00] throwAndCatch 034:05 (var curProc = host.currentProcess)
[01] outer 066:05 (var foo = throwAndCatch())
[02] outermost 074:05 (var result = outer())
Tady můžete prozkoumat hodnotu proměnných.
>>> Debug [DebuggableSample 34:5] >??someObj
??someObj
someObj : {...}
__proto__ : {...}
a : 0x63
b : {...}
>>> Debug [DebuggableSample 34:5] >??someObj.b
??someObj.b
someObj.b : {...}
__proto__ : {...}
c : 0x20
d : Hello World
Pojďme nastavit zarážku na aktuálním řádku kódu a podívat se, jaké zarážky jsou teď nastavené.
>>> Debug [DebuggableSample 34:5] >bpc
bpc
Breakpoint 1 set at 34:5
>>> Debug [DebuggableSample 34:5] >bl
bl
Id State Pos
1 enabled 34:5
Odsud zakážeme událost entry (en) pomocí příkazu ladicího programu skriptu sxd .
>>> Debug [DebuggableSample 34:5] >sxd en
sxd en
Event filter 'en' is now inactive
A pak už jen nechat skript pokračovat až do konce.
>>> Debug [DebuggableSample 34:5] >g
g
This is a fun test
Of the script debugger
foo.a = 99
Caught and returned!
Test
...
Spusťte znovu skriptovou metodu a sledujte, jak je naše zarážka zasažena.
0:000> dx @$scriptContents.outermost()
inside outer!
>>> ****** SCRIPT BREAK DebuggableSample [Breakpoint 1] ******
Location: line = 34, column = 5
Text: var curProc = host.currentProcess
Zobraz zásobník volání
>>> Debug [DebuggableSample 34:5] >k
k
## Function Pos Source Snippet
-> [00] throwAndCatch 034:05 (var curProc = host.currentProcess)
[01] outer 066:05 (var foo = throwAndCatch())
[02] outermost 074:05 (var result = outer())
V tuto chvíli chceme zastavit ladění tohoto skriptu, takže se od něj odpojíme.
>>> Debug [DebuggableSample 34:5] >.detach
.detach
Debugger has been detached from script!
A pak zadejte q k ukončení.
q
This is a fun test
Of the script debugger
foo.a = 99
Caught and returned!
Test
Opětovné spuštění funkce již neprolomí ladicí program.
0:007> dx @$scriptContents.outermost()
inside outer!
This is a fun test
Of the script debugger
foo.a = 99
Caught and returned!
Test
JavaScript ve VS Code – Přidání IntelliSense
Pokud chcete pracovat s objekty datového modelu ladicího programu ve VS Code, můžete použít definiční soubor, který je k dispozici ve vývojových sadách Windows. Definiční soubor IntelliSense poskytuje podporu pro všechna rozhraní API objektu ladicího programu host.*. Pokud jste sadu nainstalovali do výchozího adresáře na 64bitovém počítači, nachází se tady:
C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\winext\JsProvider.d.ts
Použití definičního souboru IntelliSense ve VS Code:
Vyhledání definičního souboru – JSProvider.d.ts
Zkopírujte definiční soubor do stejné složky jako váš skript.
Přidejte
/// <reference path="JSProvider.d.ts" />na začátek souboru skriptu JavaScriptu.
S tímto odkazem ve vašem JavaScript souboru vám VS Code poskytne automaticky IntelliSense nad hostitelskými rozhraními API poskytovanými JSProviderem, kromě struktur definovaných ve vašem skriptu. Zadejte například "host". a zobrazí se IntelliSense pro všechna dostupná rozhraní API modelu ladicího programu.
Prostředky JavaScriptu
Následují JavaScriptové prostředky, které mohou být užitečné při vývoji rozšíření pro ladění JavaScriptu.