Script du débogueur JavaScript

Cette rubrique explique comment utiliser JavaScript pour créer des scripts qui comprennent les objets du débogueur, et qui étendent et personnalisent les fonctionnalités du débogueur.

Vue d’ensemble du script du débogueur JavaScript

Les fournisseurs de script intègrent un langage de script dans le modèle d’objet interne du débogueur. Le fournisseur de script du débogueur JavaScript permet l’utilisation de JavaScript avec le débogueur.

Lorsque Javascript est chargé à l’aide de la commande .scriptload, le code racine du script est exécuté, les noms qui sont présents dans le script sont intégrés dans l’espace de noms racine du débogueur (dx Debugger) et le script demeure en mémoire jusqu’à ce qu’il soit déchargé et que toutes les références à ses objets soient libérées. Le script peut fournir de nouvelles fonctions à l’évaluateur d’expressions du débogueur, modifier le modèle d’objet du débogueur ou agir en tant que visualiseur de la même manière qu’un visualiseur NatVis.

Cette rubrique décrit ce que vous pouvez faire avec le script du débogueur JavaScript.

Ces deux rubriques fournissent des informations supplémentaires sur l’utilisation de JavaScript dans le débogueur.

Exemples de scripts du débogueur JavaScript

Objets natifs dans les extensions JavaScript

Vidéo de script JavaScript

Defrag Tools (Outils de défragmentation) #170 – Andy et Bill illustrent les capacités d’extensibilité et de script de JavaScript dans le débogueur.

Le fournisseur JavaScript du débogueur

Le fournisseur JavaScript inclus dans le débogueur tire pleinement parti des dernières améliorations de l’objet et de la classe ECMAScript6. Pour plus d’informations, consultez ECMAScript 6 — New Features : Overview & Comparison (Nouvelles fonctionnalités : Vue d’ensemble et comparaison).

JsProvider.dll

JsProvider.dll est le fournisseur JavaScript qui est chargé pour prendre en charge le script du débogueur JavaScript.

Exigences

Le script de débogueur JavaScript est conçu pour fonctionner avec toutes les versions de Windows prises en charge.

Chargement du fournisseur de script JavaScript

Avant d’utiliser l’une des commandes .script, un fournisseur de script doit être chargé. Utilisez la commande .scriptproviders pour confirmer que le fournisseur JavaScript est chargé.

0:000> .scriptproviders
Available Script Providers:
    NatVis (extension '.NatVis')
    JavaScript (extension '.js')

Métacommandes de script JavaScript

Les commandes suivantes sont disponibles pour utiliser le script du débogueur JavaScript.

Exigences

Avant d’utiliser l’une des commandes .script, un fournisseur de script doit être chargé. Utilisez la commande .scriptproviders pour confirmer que le fournisseur JavaScript est chargé.

0:000> .scriptproviders
Available Script Providers:
    NatVis (extension '.NatVis')
    JavaScript (extension '.js')

.scriptproviders (Lister les fournisseurs de script)

La commande .scriptproviders répertorie tous les langages de script actuellement compris par le débogueur et l’extension sous laquelle ils sont enregistrés.

Dans l’exemple ci-dessous, les fournisseurs JavaScript et NatVis sont chargés.

0:000> .scriptproviders
Available Script Providers:
    NatVis (extension '.NatVis')
    JavaScript (extension '.js')

Tout fichier se terminant par « .NatVis » est compris comme un script NatVis, et tout fichier se terminant par « .js » est compris comme un script JavaScript. Les deux types de scripts peuvent être chargés avec la commande .scriptload.

Pour plus d’informations, consultez .scriptproviders (Lister les fournisseurs de script)

.scriptload (Charger le script)

La commande .scriptload charge un script et exécute le code racine d’un script ainsi que la fonction initializeScript. S’il existe des erreurs dans la charge initiale et dans l’exécution du script, les erreurs s’affichent dans la console. La commande suivante montre le chargement réussi de TestScript.js.

0:000> .scriptload C:\WinDbg\Scripts\TestScript.js
JavaScript script successfully loaded from 'C:\WinDbg\Scripts\TestScript.js'

Toutes les manipulations du modèle objet effectuées par le script resteront en place jusqu’à ce que le script soit ultérieurement déchargé ou soit exécuté à nouveau avec un contenu différent.

Pour plus d’informations, consultez .scriptload (Charger le script)

.scriptrun

La commande .scriptrun charge un script et exécute le code racine du script ainsi que les fonctions initializeScript et invokeScript. S’il existe des erreurs dans la charge initiale et dans l’exécution du script, les erreurs s’affichent dans la console.

0:000> .scriptrun C:\WinDbg\Scripts\helloWorld.js
JavaScript script successfully loaded from 'C:\WinDbg\Scripts\helloWorld.js'
Hello World!  We are in JavaScript!

Toutes les manipulations du modèle objet du débogueur effectuées par le script resteront en place jusqu’à ce que le script soit ultérieurement déchargé ou soit exécuté à nouveau avec un contenu différent.

Pour plus d’informations, consultez .scriptrun (Exécuter le script).

.scriptunload (Décharger le script)

La commande .scriptunload décharge un script chargé et appelle la function uninitializeScript. Utilisez la syntaxe de commande suivante pour décharger un script

0:000:x86> .scriptunload C:\WinDbg\Scripts\TestScript.js
JavaScript script unloaded from 'C:\WinDbg\Scripts\TestScript.js'

Pour plus d’informations, consultez .scriptunload (Décharger le script).

.scriptlist (Lister les scripts chargés)

La commande .scriptlist répertorie tous les scripts qui ont été chargés via les commandes .scriptload ou .scriptrun. Si le TestScript a été correctement chargé à l’aide de .scriptload, la commande .scriptlist affiche le nom du script chargé.

0:000> .scriptlist
Command Loaded Scripts:
    JavaScript script from 'C:\WinDbg\Scripts\TestScript.js'

Pour plus d’informations, consultez .scriptlist (Lister les scripts chargés).

Démarrer avec le script du débogueur JavaScript

Exemple de script HelloWorld

Cette section explique comment créer et exécuter un script de débogueur JavaScript simple qui affiche « Hello World ».

// WinDbg JavaScript sample
// Prints Hello World
function initializeScript()
{
    host.diagnostics.debugLog("***> Hello World! \n");
}

Utilisez un éditeur de texte tel que Bloc-notes pour créer un fichier texte nommé HelloWorld.js qui contient le code JavaScript ci-dessus.

Utilisez la commande .scriptload pour charger et exécuter le script. Étant donné que nous avons utilisé le nom de la fonction initializeScript, le code de la fonction est exécuté lorsque le script est chargé.

0:000> .scriptload c:\WinDbg\Scripts\HelloWorld.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\HelloWorld.js'
***> Hello World! 

Une fois le script chargé, les fonctionnalités supplémentaires sont disponibles dans le débogueur. Utilisez la commande dx (Afficher l’expression NatVis) pour afficher Debugger.State.Scripts et voir que le script est alors résident.

0:000> dx Debugger.State.Scripts
Debugger.State.Scripts                
    HelloWorld 

Dans l’exemple suivant, nous allons ajouter et appeler une fonction nommée.

Exemple de script pour l’ajout de deux valeurs

Cette section explique comment créer et exécuter un script de débogueur JavaScript simple qui ajoute une entrée et deux nombres.

Ce script simple utilise une fonction unique, addTwoValues.

// WinDbg JavaScript sample
// Adds two functions
function addTwoValues(a, b)
 {
     return a + b;
 }

Utilisez un éditeur de texte tel que Bloc-notes pour créer un fichier texte nommé FirstSampleFunction.js

Utilisez la commande .scriptload pour charger le script.

0:000> .scriptload c:\WinDbg\Scripts\FirstSampleFunction.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\FirstSampleFunction.js'

Une fois le script chargé, les fonctionnalités supplémentaires sont disponibles dans le débogueur. Utilisez la commande dx (Afficher l’expression NatVis) pour afficher Debugger.State.Scripts et voir que le script est alors résident.

0:000> dx Debugger.State.Scripts
Debugger.State.Scripts                
    FirstSampleFunction    

Nous pouvons sélectionner FirstSampleFunction pour voir les fonctions qu’il fournit.

0:000> dx -r1 -v Debugger.State.Scripts.FirstSampleFunction.Contents
Debugger.State.Scripts.FirstSampleFunction.Contents                 : [object Object]
    host             : [object Object]
    addTwoValues    
 ... 

Pour faciliter l’utilisation du script, attribuez une variable dans le débogueur pour contenir le contenu du script à l’aide de la commande dx.

0:000> dx @$myScript = Debugger.State.Scripts.FirstSampleFunction.Contents

Utilisez l’évaluateur d’expression dx pour appeler la fonction addTwoValues.

0:000> dx @$myScript.addTwoValues(10, 41),d
@$myScript.addTwoValues(10, 41),d : 51

Vous pouvez également utiliser l’alias intégré $@scriptContents pour travailler avec les scripts. L’alias $@scriptContents fusionne tous les .Content de tous les scripts chargés.

0:001> dx @$scriptContents.addTwoValues(10, 40),d
@$scriptContents.addTwoValues(10, 40),d : 50

Lorsque vous avez terminé d’utiliser le script, utilisez la commande .scriptunload pour le décharger.

0:000> .scriptunload c:\WinDbg\Scripts\FirstSampleFunction.js
JavaScript script successfully unloaded from 'c:\WinDbg\Scripts\FirstSampleFunction.js'

Automatisation des commandes du débogueur

Cette section explique comment créer et exécuter un script de débogueur JavaScript simple qui automatise l’envoi de la commande u (Désassembler). L’exemple montre également comment collecter et afficher la sortie de commande dans une boucle.

Ce script utilise une seule fonction, 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");

}

Utilisez un éditeur de texte tel que Bloc-notes pour créer un fichier texte nommé RunCommands.js

Utilisez la commande .scriptload pour charger le script RunCommands.

0:000> .scriptload c:\WinDbg\Scripts\RunCommands.js 
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\RunCommands.js'

Une fois le script chargé, les fonctionnalités supplémentaires sont disponibles dans le débogueur. Utilisez la commande dx (Afficher l’expression NatVis) pour afficher le Debugger.State.Scripts.RunCommands et voir que le script est alors résident.

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]

Utilisez la commande dx pour appeler la fonction RunCommands dans le script 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

Fonctions spéciales du débogueur JavaScript

Il existe plusieurs fonctions spéciales dans un script JavaScript appelées par le fournisseur de script lui-même.

initializeScript

Lorsqu’un script JavaScript charge et est exécuté, il passe par une série d’étapes avant que le modèle objet du débogueur soit affecté par les variables, les fonctions et les autres objets du script.

  • Le script est chargé en mémoire et analysé.
  • Le code racine du script est exécuté.
  • Si le script a une méthode nommée initializeScript, celle-ci est appelée.
  • La valeur renvoyée par initializeScript est utilisée pour déterminer comment modifier automatiquement le modèle objet du débogueur.
  • Les noms du script sont intégrés dans l’espace de noms du débogueur.

Comme mentionné, initializeScript est appelé immédiatement après l’exécution du code racine du script. Son travail consiste à envoyer au fournisseur un tableau JavaScript d’objets d’inscription qui indique comment modifier le modèle objet du débogueur.

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

La méthode invokeScript est la méthode de script principale. Elle est appelée lorsque .scriptload et .scriptrun sont exécutés.

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

La méthode uninitializeScript fonctionne à l’opposé de initializeScript. Elle est appelée lorsqu’un script est dissocié et qu’il s’apprête à être déchargé. Son travail consiste à annuler les modifications apportées au modèle objet que le script a impérativement effectuées pendant l’exécution, et/ou à détruire les objets mis en cache par le script.

Si un script n’effectue pas manipulations impératives pour le modèle objet et ne met pas en cache des résultats, il n’a pas besoin d’une méthode uninitializeScript. Le fournisseur annule automatiquement toutes les modifications apportées au modèle objet comme indiqué par la valeur de retour d’initializeScript. Ces modifications ne nécessitent pas de méthode uninitializeScript explicite.

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");
}

Résumé des fonctions appelées par des commandes de script

Ce tableau récapitule les fonctions appelées par les commandes de script

Commande .scriptload .scriptrun (Exécuter le script) .scriptunload (Décharger le script)
root Oui Oui
initializeScript Oui Oui
invokeScript Oui
uninitializeScript Oui

Utilisez cet extrait de code pour voir quand chaque fonction est appelée lorsque le script est chargé, exécuté et déchargé.

// 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");
}

Créer un visualiseur de débogueur dans JavaScript

Les fichiers de visualisation personnalisés vous permettent de regrouper et d’organiser les données dans une structure de visualisation qui affiche plus clairement les relations et le contenu des données. Vous pouvez utiliser les extensions du débogueur JavaScript pour écrire des visualiseurs de débogueur qui agissent d’une manière très semblable à NatVis. Cela s’effectue via la création d’un objet prototype JavaScript (ou d’une classe ES6) qui agit comme visualiseur pour un type de données choisi. Pour plus d’informations sur NatVis et le débogueur, consultez dx (Display NatVis Expression).

Classe d’exemple – Simple1DArray

Prenons un exemple de classe C++ qui représente un tableau à une seule dimension. Cette classe possède deux membres : m_size, qui est la taille globale du tableau, et m_pValues, qui est un pointeur vers un certain nombre d’ints en mémoire égal au champ m_size.

class Simple1DArray
{
private:

    ULONG64 m_size;
    int *m_pValues;
};

Nous pouvons utiliser la commande dx pour examiner le rendu de la structure de données par défaut.

0:000> dx g_array1D
g_array1D                 [Type: Simple1DArray]
    [+0x000] m_size           : 0x5 [Type: unsigned __int64]
    [+0x008] m_pValues        : 0x8be32449e0 : 0 [Type: int *]

Visualiseur JavaScript

Pour visualiser ce type, nous devons créer une classe de prototype (ou de ES6) qui comporte tous les champs et toutes les propriétés que nous voulons que le débogueur affiche. La méthode initializeScript doit également retourner un objet qui indique au fournisseur JavaScript de lier notre prototype en tant que visualiseur pour le type choisi.

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")];
}

Enregistrez le script dans un fichier nommé arrayVisualizer.js.

Utilisez la commande .load (Charger DLL extension) pour charger le fournisseur JavaScript.

0:000> .load C:\ScriptProviders\jsprovider.dll

Utilisez .scriptload pour charger le script du visualiseur de tableau.

0:000> .scriptload c:\WinDbg\Scripts\arrayVisualizer.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\arrayVisualizer.js'

À présent, lorsque la commande dx est utilisée, le visualiseur de script affiche des lignes de contenu tiré du tableau.

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

En outre, cette visualisation JavaScript fournit des fonctionnalités LINQ telles que Select.

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

Ce qui influence la visualisation

Un prototype ou une classe qui est produit par le visualiseur pour un type natif via le retour d’un objet host.typeSignatureRegistration à partir d’initializeScript aura toutes ses propriétés et ses méthodes de JavaScript ajoutées au type natif. En outre, la sémantique suivante s’applique :

  • Tout nom qui ne commence pas par deux traits de soulignement (__) sera disponible dans la visualisation.

  • Les noms qui font partie d’objets JavaScript standard ou qui font partie des protocoles créés par le fournisseur JavaScript ne s’affichent pas dans la visualisation.

  • Un objet peut être rendu itérable via la prise en charge de [Symbol.iterator].

  • Un objet peut être rendu indexable via la prise en charge d’un protocole personnalisé composé de plusieurs fonctions : getDimensionality, getValueAt et éventuellement setValueAt.

Intégration native et JavaScript d’un objet

L’intégration entre JavaScript et le modèle objet du débogueur est bidirectionnelle. Les objets natifs peuvent être transmis dans JavaScript et les objets JavaScript peuvent être passés dans l’évaluateur d’expression du débogueur. Prenons l’exemple de l’ajout de la méthode suivante dans notre script :

function multiplyBySeven(val)
{
    return val * 7;
}

Cette méthode peut désormais être utilisée dans l’exemple de requête LINQ ci-dessus. Tout d’abord, nous chargeons la visualisation JavaScript.

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

Ensuite, nous pouvons utiliser la fonction multiplieBySeven inline, comme indiqué ci-dessous.

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

Points d’arrêt conditionnels avec JavaScript

Vous pouvez utiliser JavaScript pour réaliser un traitement supplémentaire une fois qu’un point d’arrêt est atteint. Par exemple, le script peut être utilisé pour examiner d’autres valeurs d’exécution, puis pour déterminer si vous souhaitez continuer automatiquement l’exécution du code ou l’arrêter et effectuer un débogage manuel supplémentaire.

Pour obtenir des informations générales sur l’utilisation des points d’arrêt, consultez Methods of Controlling Breakpoints (Méthodes de contrôle des points d’arrêt).

Exemple de script de traitement de point d’arrêt DebugHandler.js

Cet exemple détermine si la boîte de dialogue Bloc-notes est une boîte d’ouverture ou d’enregistrement : notepad!ShowOpenSaveDialog. Ce script utilise la variable pszCaption pour déterminer si la boîte de dialogue actuellement ouverte est une boîte de dialogue « Ouvrir » ou s’il s’agit d’une boîte de dialogue « Enregistrer sous ». S’il s’agit d’une boîte de dialogue d’ouverture, l’exécution du code se poursuit. S’il s’agit d’une boîte de dialogue d’enregistrement, l’exécution du code s’arrête et le débogueur s’affiche.

 // 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");
    }
  }

Cette commande définit un point d’arrêt dans notepad!ShowOpenSaveDialog et exécute le script ci-dessus chaque fois que ce point d’arrêt est atteint.

bp notepad!ShowOpenSaveDialog ".scriptrun C:\\WinDbg\\Scripts\\DebugHandler.js"

Ensuite, lorsque l’option Fichier > Enregistrer est sélectionnée dans le Bloc-notes, le script est exécuté, la commande g n’est pas envoyée et l’exécution du code s’arrête.

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

Utiliser des valeurs 64 bits dans les extensions JavaScript

Cette section décrit comment se comportent des valeurs 64 bits transmises dans une extension de débogueur JavaScript. Ce problème est dû au fait que JavaScript ne peut stocker des nombres qu’en 53 bits.

Stockage 64 bits et JavaScript 53 bits

Les valeurs ordinales passées dans JavaScript sont normalement marshalées en tant que nombres JavaScript. Le problème est que les nombres JavaScript sont des valeurs 64 bits à virgule flottante double précision. Toute ordinale de plus de 53 bits perdrait sa précision lors de son entrée dans JavaScript. Cela présente un problème pour les pointeurs et les autres valeurs ordinales de 64 bits qui peuvent avoir des indicateurs dans les octets les plus élevés. Pour résoudre ce problème, toute valeur 64 bits native (à partir du code natif ou du modèle de données) qui entre dans JavaScript, entre en tant que type de bibliothèque, et non en tant que nombre JavaScript. Ce type de bibliothèque retourne vers le code natif sans perdre sa précision numérique.

Conversion automatique

Le type de bibliothèque pour les valeurs 64 bits ordinales prend en charge la conversion JavaScript standard valueOf. Si l’objet est utilisé dans une opération mathématique ou dans une autre construction qui nécessite une conversion de valeur, il est automatiquement converti en nombre JavaScript. Si une perte de précision se produit (la valeur utilise plus de 53 bits de précision ordinale), le fournisseur JavaScript affiche une exception.

Notez que si vous utilisez des opérateurs bit à bit dans JavaScript, vous êtes limité à 32 bits de précision ordinale.

Cet exemple de code additionne deux nombres et est utilisé pour tester la conversion de valeurs 64 bits.

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);
}

Utilisez un éditeur de texte tel que Bloc-notes pour créer un fichier texte nommé PlayWith64BitValues.js

Utilisez la commande .scriptload pour charger le script.

0:000> .scriptload c:\WinDbg\Scripts\PlayWith64BitValues.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\PlayWith64BitValues.js'

Pour faciliter l’utilisation du script, attribuez une variable dans le débogueur pour contenir le contenu du script à l’aide de la commande dx.

0:000> dx @$myScript = Debugger.State.Scripts.PlayWith64BitValues.Contents

Utilisez l’évaluateur d’expression dx pour appeler la fonction addTwoValues.

Tout d’abord, nous allons calculer la valeur de 2^53 =9007199254740992 (hexadécimal 0x20000000000000).

Afin de tester, nous allons d’abord calculer (2^53) - 2 et voir s’il retourne la valeur correcte pour la somme.

0:000> dx @$myScript.playWith64BitValues(9007199254740990, 9007199254740990)
Sum   >> 18014398509481980

Ensuite, nous allons calculer (2^53) -1 =9007199254740991. Cette opération renvoie l’erreur qui indique que le processus de conversion perd en précision. Il s’agit donc de la plus grande valeur qui peut être utilisée avec la méthode de somme dans le code JavaScript.

0:000> dx @$myScript.playWith64BitValues(9007199254740990, 9007199254740991)
Error: 64 bit value loses precision on conversion to number

Appelez une méthode de modèle de données qui transmet des valeurs 64 bits. Il n’y a aucune perte de précision ici.

0:001> dx @$myScript.performOp64BitValues( 0x7FFFFFFFFFFFFFFF,  0x7FFFFFFFFFFFFFFF, (x, y) => x + y)
@$myScript.performOp64BitValues( 0x7FFFFFFFFFFFFFFF,  0x7FFFFFFFFFFFFFFF, (x, y) => x + y) : 0xfffffffffffffffe

Comparaison

Le type de bibliothèque 64 bits est un objet JavaScript et non un type de valeur tel qu’un nombre JavaScript. Cela a des implications pour les opérations de comparaison. Normalement, l’égalité (==) sur un objet indique que les opérandes font référence au même objet et non à la même valeur. Le fournisseur JavaScript atténue ce problème en suivant les références actives des valeurs 64 bits et en retournant le même objet « immuable » pour la valeur 64 bits non collectée. Cela signifie que pendant la comparaison, les éléments suivants se produisent.

// 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");

Utilisez un éditeur de texte tel que Bloc-notes pour créer un fichier texte nommé ComparisonWith64BitValues.js

Utilisez la commande .scriptload pour charger le script.

0:000> .scriptload c:\WinDbg\Scripts\ComparisonWith64BitValues.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\ComparisonWith64BitValues.js'

Pour faciliter l’utilisation du script, attribuez une variable dans le débogueur pour contenir le contenu du script à l’aide de la commande dx.

0:000> dx @$myScript = Debugger.State.Scripts.comparisonWith64BitValues.Contents

Afin d’effectuer des tests, nous allons tout d’abord calculer (2^53) - 2 et s’assurer que les valeurs attendues sont retournées.

0:001> dx @$myScript.comparisonWith64BitValues(9007199254740990, 9007199254740990)
areEqual   >> true
areNotEqual   >> false
isEqualTo42   >> false
isLess   >> false

Nous allons également utiliser le nombre 42 comme première valeur pour vérifier que l’opérateur de comparaison fonctionne correctement.

0:001> dx @$myScript.comparisonWith64BitValues(42, 9007199254740990)
areEqual   >> false
areNotEqual   >> true
isEqualTo42   >> true
isLess   >> true

Ensuite, nous allons calculer (2^53) -1 =9007199254740991. Cette valeur renvoie l’erreur qui indique que le processus de conversion perd en précision. Il s’agit donc de la plus grande valeur qui peut être utilisée avec les opérateurs de comparaison dans le code JavaScript.

0:000> dx @$myScript.playWith64BitValues(9007199254740990, 9007199254740991)
Error: 64 bit value loses precision on conversion to number

Conserver la précision dans les opérations

Pour permettre à une extension de débogueur de maintenir la précision, un ensemble de fonctions mathématiques est projeté sur le type de bibliothèque 64 bits. Si l’extension a besoin (ou pourrait avoir besoin) d’une précision supérieure à 53 bits pour les valeurs 64 bits entrantes, les méthodes suivantes doivent être utilisées au lieu d’utiliser des opérateurs standard :

Nom de la méthode Signature Description
asNumber .asNumber() Convertit la valeur 64 bits en nombre JavaScript. Si une perte de précision se produit, **UNE EXCEPTION EST LEVÉE**
convertToNumber .convertToNumber() Convertit la valeur 64 bits en nombre JavaScript. Si une perte de précision se produit, **AUCUNE EXCEPTION N’EST LEVÉE**
getLowPart .getLowPart() Convertit les 32 derniers bits de la valeur 64 bits en nombre JavaScript
getHighPart .getHighPart() Convertit les 32 premiers bits de la valeur 64 bits en nombre JavaScript
add .add(value) Ajoute une valeur à la valeur 64 bits et retourne le résultat
soustraction .subtract(value) Soustrait une valeur de la valeur 64 bits et retourne le résultat
multiply .multiply(value) Multiplie la valeur 64 bits par la valeur fournie et retourne le résultat
divide .divide(value) Divise la valeur 64 bits par la valeur fournie et retourne le résultat
bitwiseAnd .bitwiseAnd(value) Calcule le bit à bit et la valeur 64 bits avec la valeur fournie et retourne le résultat
bitwiseOr .bitwiseOr(value) Calcule le bit à bit ou la valeur 64 bits avec la valeur fournie et retourne le résultat
bitwiseXor .bitwiseXor(value) Calcule le xor en bit à bit de la valeur 64 bits avec la valeur fournie et retourne le résultat
bitwiseShiftLeft .bitwiseShiftLeft(value) Déplace la valeur 64 bits à gauche par la quantité donnée et retourne le résultat
bitwiseShiftRight .bitwiseShiftRight(value) Déplace la valeur 64 bits à droite par la quantité donnée et retourne le résultat
toString .toString([radix]) Convertit la valeur 64 bits en chaîne d’affichage dans la base par défaut (ou la base éventuellement fournie)

Cette méthode est aussi disponible.

Nom de la méthode Signature Description
compareTo .compareTo(value) Compare la valeur 64 bits à une autre valeur 64 bits.

JavaScript – Débogage

Cette section explique comment utiliser les fonctionnalités du débogueur en matière de débogage du script. Le débogueur prend en charge le débogage de scripts JavaScript à l’aide de la commande .scriptdebug (Débogage JavaScript).

Remarque

Pour utiliser le débogage JavaScript avec WinDb, exécutez le débogueur en tant qu’administrateur.

Utilisez cet exemple de code pour explorer le débogage de JavaScript. Pour cette procédure pas à pas, nous allons le nommer DebuggableSample.js et l’enregistrer dans le répertoire 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
    //
}

Chargez le script d’exemple.

.scriptload C:\MyScripts\DebuggableSample.js

Commencez à déboguer activement le script en utilisant la commande .scriptdebug.

0:000> .scriptdebug C:\MyScripts\DebuggableSample.js
>>> ****** DEBUGGER ENTRY DebuggableSample ******
           No active debug event!

>>> Debug [DebuggableSample <No Position>] >

Une fois que vous voyez l’invite >>> Debug [DebuggableSample <No Position>] > et une demande d’entrée, vous êtes à l’intérieur du débogueur de script.

Utilisez la commande .help pour afficher une liste de commandes dans l’environnement de débogage JavaScript.

>>> 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

Utilisez la commande de débogueur de script sx pour voir la liste des événements que nous pouvons intercepter.

>>> 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     

Utilisez la commande de débogueur du script sxe pour activer la pause à l’entrée afin que le script se fige dans le débogueur de script dès que du code s’exécute à l’intérieur.

>>> Debug [DebuggableSample <No Position>] >sxe en          
sxe en                                                      
Event filter 'en' is now active                             

Quittez le débogueur de script. Nous allons maintenant effectuer un appel de fonction dans le script qui interceptera le débogueur.

>>> Debug [DebuggableSample <No Position>] >q

À ce stade, vous êtes de retour dans le débogueur normal. Exécutez la commande suivante pour appeler le script.

dx @$scriptContents.outermost()

À présent, vous êtes de retour dans le débogueur de script sur la première ligne de la fonction JavaScript la plus externe.

>>> ****** SCRIPT BREAK DebuggableSample [BreakIn] ******   
           Location: line = 73, column = 5                  
           Text: var x = 99                                 

>>> Debug [DebuggableSample 73:5] >                         

En plus de voir l’arrêt dans le débogueur, vous obtenez des informations sur la ligne (73) et la colonne (5) où l’arrêt a eu lieu ainsi que l’extrait de code source qui nous intéresse : var x = 99.

Sautons quelques lignes et accédons à un autre emplacement dans le script.

    p
    t
    p
    t
    p
    p

À ce stade, vous devriez vous arrêter sur la méthode throwAndCatch à la ligne 34.

...
>>> ****** SCRIPT BREAK DebuggableSample [Step Complete] ******                       
           Location: line = 34, column = 5                                            
           Text: var curProc = host.currentProcess                                    

Vous pouvez le vérifier en exécutant une trace de pile.

>>> 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())                

À partir de là, vous pouvez examiner la valeur des variables.

>>> 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                          

Nous allons définir un point d’arrêt sur la ligne de code actuelle et voir quels points d’arrêt sont maintenant définis.

>>> 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                                      

À partir de là, nous allons désactiver l’événement d’entrée (en) à l’aide de la commande de débogueur de script sxd.

>>> Debug [DebuggableSample 34:5] >sxd en                                                                              
sxd en                                                                                                                 
Event filter 'en' is now inactive                                                                                      

Ensuite, il suffit de laisser le script continuer jusqu’à la fin.

>>> Debug [DebuggableSample 34:5] >g                                                                                   
g                                                                                                                      
This is a fun test                                                                                                     
Of the script debugger                                                                                                 
foo.a = 99                                                                                                             
Caught and returned!                                                                                                   
Test                                                                                                                   
...

Exécutez la méthode de script une nouvelle fois et vérifier que notre point d’arrêt est atteint.

0:000> dx @$scriptContents.outermost()                                                
inside outer!                                                                         
>>> ****** SCRIPT BREAK DebuggableSample [Breakpoint 1] ******                        
           Location: line = 34, column = 5                                            
           Text: var curProc = host.currentProcess                                    

Affichez la pile des appels.

>>> 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())                

À ce stade, le but est d’arrêter le débogage de ce script, nous allons donc nous détacher de celui-ci.

>>> Debug [DebuggableSample 34:5] >.detach                  
.detach                                                     
Debugger has been detached from script!                     

Puis tapez « q » pour quitter.

q                                                           
This is a fun test                                          
Of the script debugger                                      
foo.a = 99                                                  
Caught and returned!                                        
Test                                                        

L’exécution de la fonction ne provoque plus d’arrêt dans le débogueur.

0:007> dx @$scriptContents.outermost()
inside outer!
This is a fun test
Of the script debugger
foo.a = 99
Caught and returned!
Test

JavaScript dans VSCode - Ajout d’IntelliSense

Si vous souhaitez utiliser les objets de modèle de données du débogueur dans VSCode, vous pouvez utiliser un fichier de définition disponible dans les kits de développement Windows. Le fichier de définition IntelliSense prend en charge toutes les API host.* d’objet du débogueur. Si vous avez installé le kit dans le répertoire par défaut sur un PC 64 bits, il se trouve ici :

C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\winext\JsProvider.d.ts

Pour utiliser le fichier de définition IntelliSense dans VSCode :

  1. Recherchez le fichier de définition JSProvider.d.ts

  2. Copiez le fichier de définition dans le même dossier que votre script.

  3. Ajoutez /// <reference path="JSProvider.d.ts" /> en haut de votre fichier de script JavaScript.

Avec cette référence dans votre fichier JavaScript, VS Code implémente automatiquement IntelliSense dans les API hôtes fournies par JSProvider en plus des structures de votre script. Par exemple, tapez « host. » et vous verrez IntelliSense pour toutes les API de modèle de débogueur disponibles.

Ressources JavaScript

Voici les ressources JavaScript qui peuvent être utiles lorsque vous développez des extensions de débogage JavaScript.

Voir aussi

Exemples de scripts du débogueur JavaScript

Objets natifs dans les extensions JavaScript