Partager via


Script du débogueur JavaScript

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

Vue d’ensemble du script du débogueur JavaScript

Les fournisseurs de scripts établissent un lien entre un langage de script et 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.

Lorsqu'un code JavaScript est chargé via la commande .scriptload, le code principal du script est exécuté, les noms présents dans le script sont importés dans l'espace de noms racine du Debugger dx et le script reste 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’expression du débogueur, modifier le modèle objet du débogueur ou agir comme visualiseur de la même façon 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 #170 - Andy et Bill illustrent les capacités d’extensibilité et de script JavaScript dans le débogueur.

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 — Nouvelles fonctionnalités : Vue d’ensemble et comparaison.

JsProvider.dll

JsProvider.dll est le fournisseur JavaScript 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 prises en charge de Windows.

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

Commandes meta 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 scripts)

La commande .scriptproviders répertorie toutes les langues de script qui sont actuellement comprises par le débogueur et l’extension sous laquelle elles sont inscrites.

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. L’un ou l’autre type de script peut être chargé avec la commande .scriptload.

Pour plus d’informations, consultez .scriptproviders (List Script Providers)

.scriptload (Charger le Script)

La commande .scriptload charge un script et exécute le code racine d’un script et la fonction initializeScript . S’il existe des erreurs dans la charge initiale et l’exécution du script, les erreurs s’affichent dans la console. La commande suivante indique que TestScript.jsa été chargé avec succès.

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

Toutes les manipulations de modèle objet effectuées par le script restent en place jusqu’à ce que le script soit ensuite déchargé ou réexécuter avec un contenu différent.

Pour plus d’informations, consultez .scriptload (Load Script)

.scriptrun

La commande .scriptrun charge un script, exécute le code racine du script, l’initializeScript et la fonction invokeScript . S’il existe des erreurs dans la charge initiale et 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 de modèle objet du débogueur effectuées par le script restent en place jusqu’à ce que le script soit ensuite déchargé ou réexécuter 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 fonction 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 (Unload Script).

.scriptlist (Lister les scripts chargés)

La commande .scriptlist répertorie tous les scripts qui ont été chargés via le fichier .scriptload ou la commande .scriptrun. Si 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 (List Loaded Scripts).

Commencez avec le débogage par script JavaScript

Exemple de script HelloWorld

Cette section explique comment créer et exécuter un script de débogueur JavaScript simple qui s’imprime, Hello World.

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

Utilisez un éditeur de texte tel que le 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 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 (Display NatVis Expression) pour afficher Debugger.State.Scripts pour voir que notre script est maintenant 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.

Ajout de deux valeurs - Exemple de script

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

Ce script simple fournit une fonction unique, addTwoValues.

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

Utilisez un éditeur de texte tel que le 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 (Display NatVis Expression) pour afficher Debugger.State.Scripts pour voir que notre script est maintenant 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 rendre le script un peu plus pratique à utiliser, affectez 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 fichiers . Contenu 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 décharger le script.

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 (Unassemble). L’exemple montre également comment collecter et afficher la sortie de commande dans une boucle.

Ce script fournit une fonction unique, 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 le 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 (Display NatVis Expression) pour afficher Debugger.State.Scripts.RunCommands pour voir que notre script est maintenant 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 de débogueur JavaScript spéciales

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

initialiserScript

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

  • Le script est chargé en mémoire et analysé.
  • Le code racine du script est exécuté.
  • Si le script a une méthode appelée initializeScript, cette méthode est appelée.
  • La valeur de retour d’initializeScript est utilisée pour déterminer comment modifier automatiquement le modèle objet du débogueur.
  • Les noms du script sont reliés à 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 à retourner un tableau JavaScript d’objets d’inscription au fournisseur indiquant 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 et 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 est le contraire comportemental d’initializeScript. Elle est appelée lorsqu'un script est détaché et qu'il est prêt à décharger. Son travail consiste à annuler les modifications apportées au modèle objet que le script a effectué impérativement pendant l’exécution et/ou pour détruire les objets mis en cache par le script.

Si un script n’effectue pas de manipulations impératives sur le modèle objet et ne met pas en cache les résultats, il n’est pas nécessaire de disposer d’une méthode uninitializeScript. Toutes les modifications apportées au modèle objet effectuées comme indiqué par la valeur de retour d’initializeScript sont annulées automatiquement par le fournisseur. Ces modifications ne nécessitent pas de méthode explicite 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");
}

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 (Unload Script)
racine oui oui
initializeScript oui oui
invokeScript oui
uninitializeScript oui

Utilisez cet exemple de code pour voir quand chaque fonction est appelée lors du chargement, de l'exécution et du déchargement du script.

// 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éation d’un visualiseur de débogueur en JavaScript

Les fichiers de visualisation personnalisés vous permettent de regrouper et d’organiser les données dans une structure de visualisation qui reflète mieux les relations et le contenu des données. Vous pouvez utiliser les extensions de débogueur JavaScript pour écrire des visualiseurs de débogueur qui agissent d’une manière très similaire à 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 donné. Pour plus d’informations sur NatVis et le débogueur, consultez dx (Display NatVis Expression).

Exemple de classe - Simple1DArray

Prenons un exemple de classe C++ qui représente un tableau unidimensionnel. Cette classe a 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 égale 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 ES6) qui a tous les champs et propriétés que nous voulons que le débogueur affiche. Nous devons également faire en sorte que la méthode initializeScript renvoie un objet qui indique au fournisseur JavaScript de lier notre prototype en tant que visualiseur pour le type donné.

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 (Load Extension DLL) 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 de 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 affecte la visualisation

Un prototype ou une classe qui devient le visualiseur pour un type natif par le biais d’un retour d’un objet host.typeSignatureRegistration à partir d’initializeScript aura toutes les propriétés et méthodes en 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 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.

Pont d'objet Native et JavaScript

Le pont entre JavaScript et le modèle objet du débogueur est bidirectionnel. Les objets natifs peuvent être transmis dans JavaScript et les objets JavaScript peuvent être passés à l’évaluateur d’expression du débogueur. Par exemple, considérez 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 multiplyBySeven directement, 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 effectuer 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 déterminer si vous souhaitez continuer automatiquement l’exécution du code ou 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 Méthodes de contrôle des points d’arrêt.

exemple de script de traitement de point d’arrêtDebugHandler.js

Cet exemple évaluera la boîte de dialogue d'ouverture et d'enregistrement du notepad!ShowOpenSaveDialog. Ce script évalue la variable pszCaption pour déterminer si la boîte de dialogue actuelle 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 ouverte, l’exécution du code se poursuit. S'il s'agit d'une boîte de dialogue de sauvegarde, l'exécution du code se bloque et le débogueur s'enclenche.

 // 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 sur le Bloc-notes ! 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"

Lorsque l'option Enregistrer du fichier > est sélectionnée dans le Bloc-notes, le script est exécuté, mais la commande g n'est pas envoyée, ce qui entraîne une interruption de l'exécution du code.

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 les valeurs 64 bits passées dans une extension de débogueur JavaScript se comportent. Ce problème se produit, car JavaScript a uniquement la possibilité de stocker des numéros à l’aide de 53 bits.

Stockage 64 bits et JavaScript 53 bits

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

Conversion automatique

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

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

Cet exemple de code additionne deux nombres et sera 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 le 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 rendre le script un peu plus pratique à utiliser, affectez 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 2^53 =9007199254740992 (hexadécimal 0x20000000000000).

Tout d’abord pour tester, nous allons utiliser (2^53) - 2 et voir qu’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 indiquant que le processus de conversion perd la précision. Il s’agit donc de la plus grande valeur qui peut être utilisée avec la méthode sum 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 en passant 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 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 aux valeurs 64 bits et en retournant le même objet « immuable » pour les valeurs 64 bits non collectées. Cela signifie que, pour 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 le 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 rendre le script un peu plus pratique à utiliser, affectez 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

Tout d’abord pour tester, nous allons utiliser (2^53) - 2 et voir qu’il retourne les valeurs attendues.

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

Nous allons également essayer le nombre 42 comme première valeur pour valider que l’opérateur de comparaison fonctionne comme il le devrait.

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 retourne l’erreur indiquant que le processus de conversion perd la 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

Maintenance de 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 peut-être) d’une précision supérieure à 53 bits pour les valeurs 64 bits entrantes, les méthodes suivantes doivent être utilisées au lieu de s’appuyer sur des opérateurs standard :

Nom de la méthode signature Description
asNumber .asNumber() Convertit la valeur 64 bits en nombre JavaScript. Si la perte de précision se produit, **UNE EXCEPTION EST LEVÉE**
convertToNumber .convertToNumber() Convertit la valeur 64 bits en nombre JavaScript. Si la perte de précision se produit, **AUCUNE EXCEPTION N’EST LEVÉE**
obtenirPartieBasse .getLowPart() Convertit les 32 bits inférieurs de la valeur 64 bits en nombre JavaScript
getHighPart .getHighPart() Convertit les 32 bits élevés de la valeur 64 bits en un nombre en JavaScript
ajouter .add(value) Ajoute une valeur à la valeur 64 bits et retourne le résultat
soustraction .soustraire(valeur) Soustrait une valeur d'une valeur de 64 bits et renvoie le résultat
multiply .multiplie(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 niveau de bits et la valeur 64 bits avec la valeur fournie et retourne le résultat
bitwiseOr .bitwiseOr(value) Calcule le OU bit à bit de la valeur 64 bits avec la valeur fournie et retourne le résultat.
bitwiseXor .bitwiseXor(value) Calcule le xor au niveau du bit de la valeur 64 bits avec la valeur fournie et retourne le résultat
bitwiseShiftLeft .bitwiseShiftLeft(value) Effectue un déplacement de bits de la valeur 64 bits vers la gauche selon la quantité spécifiée et retourne le résultat.
bitwiseShiftRight .bitwiseShiftRight(value) Décale la valeur de 64 bits vers la droite du montant spécifié et renvoie le résultat.
toString .toString([radix]) Convertit la valeur 64 bits en chaîne d’affichage dans le radix par défaut (ou le radix fourni éventuellement)

Cette méthode est également disponible.

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

Débogage JavaScript

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

Remarque

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

Utilisez cet exemple de code pour explorer le débogage d’un code 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 l’exemple de script.

.scriptload C:\MyScripts\DebuggableSample.js

Démarrez activement le débogage du script à l’aide de 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 le message de 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 afficher 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 de script sxe pour activer l’arrêt sur l’entrée afin que le script intercepte dans le débogueur de script dès qu’un code dans celui-ci s’exécute.

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

Quittez le débogueur de script et nous allons 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 et arrêté sur la première ligne de la fonction la plus externe en JavaScript.

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

>>> Debug [DebuggableSample 73:5] >                         

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

Faisons quelques pas et rendons-nous à un autre endroit dans le script.

    p
    t
    p
    t
    p
    p

À ce stade, vous devez être divisé en la méthode throwAndCatch sur 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                                                                                      

Et puis, 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                                                                                                                   
...

Réexécutez la méthode de script et observez notre point d'arrêt être 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, nous voulons arrêter le débogage de ce script, donc nous nous détachons 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 s'arrête plus 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 VS Code - Ajout d’IntelliSense

Si vous souhaitez utiliser les objets de modèle de données du débogueur dans VS Code, 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 d’objet du débogueur Host.* . 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 VS Code :

  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-y /// <reference path="JSProvider.d.ts" /> en haut de votre fichier de script JavaScript.

Avec cette référence dans votre fichier JavaScript, VS Code vous donnera automatiquement IntelliSense sur 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