Skip to content

VM (exécution de JavaScript)

[Stable : 2 - Stable]

Stable : 2 Stability : 2 - Stable

Code source : lib/vm.js

Le module node:vm permet de compiler et d'exécuter du code dans des contextes de machine virtuelle V8.

Le module node:vm n'est pas un mécanisme de sécurité. Ne l'utilisez pas pour exécuter du code non fiable.

Le code JavaScript peut être compilé et exécuté immédiatement ou compilé, enregistré et exécuté ultérieurement.

Un cas d'utilisation courant consiste à exécuter le code dans un contexte V8 différent. Cela signifie que le code invoqué possède un objet global différent de celui du code appelant.

On peut fournir le contexte en contextualisant un objet. Le code invoqué traite toute propriété du contexte comme une variable globale. Toute modification des variables globales causée par le code invoqué se reflète dans l'objet contexte.

js
const vm = require('node:vm')

const x = 1

const context = { x: 2 }
vm.createContext(context) // Contextualiser l'objet.

const code = 'x += 40; var y = 17;'
// `x` et `y` sont des variables globales dans le contexte.
// Initialement, x a la valeur 2 car c'est la valeur de context.x.
vm.runInContext(code, context)

console.log(context.x) // 42
console.log(context.y) // 17

console.log(x) // 1; y n'est pas défini.

Classe : vm.Script

Ajouté dans : v0.3.1

Les instances de la classe vm.Script contiennent des scripts précompilés qui peuvent être exécutés dans des contextes spécifiques.

new vm.Script(code[, options])

[Historique]

VersionModifications
v21.7.0, v20.12.0Ajout de la prise en charge de vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.
v17.0.0, v16.12.0Ajout de la prise en charge des attributs d'importation au paramètre importModuleDynamically.
v10.6.0L'option produceCachedData est dépréciée au profit de script.createCachedData().
v5.7.0Les options cachedData et produceCachedData sont désormais prises en charge.
v0.3.1Ajouté dans : v0.3.1
  • code <string> Le code JavaScript à compiler.
  • options <Object> | <string>
    • filename <string> Spécifie le nom de fichier utilisé dans les traces de pile produites par ce script. Valeur par défaut : 'evalmachine.<anonymous>'.
    • lineOffset <number> Spécifie le décalage du numéro de ligne qui est affiché dans les traces de pile produites par ce script. Valeur par défaut : 0.
    • columnOffset <number> Spécifie le décalage du numéro de colonne de la première ligne qui est affiché dans les traces de pile produites par ce script. Valeur par défaut : 0.
    • cachedData <Buffer> | <TypedArray> | <DataView> Fournit un Buffer ou un TypedArray, ou un DataView optionnel avec les données du cache de code de V8 pour la source fournie. Lorsqu'il est fourni, la valeur cachedDataRejected sera définie sur true ou false en fonction de l'acceptation des données par V8.
    • produceCachedData <boolean> Lorsque la valeur est true et qu'aucune donnée cachedData n'est présente, V8 tentera de produire des données de cache de code pour code. En cas de succès, un Buffer contenant les données du cache de code de V8 sera produit et stocké dans la propriété cachedData de l'instance vm.Script renvoyée. La valeur cachedDataProduced sera définie sur true ou false selon que les données du cache de code sont produites avec succès ou non. Cette option est dépréciée au profit de script.createCachedData(). Valeur par défaut : false.
    • importModuleDynamically <Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Utilisé pour spécifier comment les modules doivent être chargés lors de l'évaluation de ce script lorsque import() est appelé. Cette option fait partie de l'API expérimentale des modules. Nous ne recommandons pas de l'utiliser en environnement de production. Pour plus d'informations, consultez Prise en charge de import() dynamique dans les API de compilation.

Si options est une chaîne de caractères, elle spécifie le nom du fichier.

La création d'un nouvel objet vm.Script compile code mais ne l'exécute pas. Le vm.Script compilé peut être exécuté ultérieurement plusieurs fois. Le code n'est lié à aucun objet global ; il est plutôt lié avant chaque exécution, uniquement pour cette exécution.

script.cachedDataRejected

Ajouté dans : v5.7.0

Lorsque cachedData est fourni pour créer le vm.Script, cette valeur sera définie sur true ou false selon l'acceptation des données par V8. Sinon, la valeur est undefined.

script.createCachedData()

Ajouté dans : v10.6.0

Crée un cache de code qui peut être utilisé avec l'option cachedData du constructeur Script. Retourne un Buffer. Cette méthode peut être appelée à tout moment et un nombre quelconque de fois.

Le cache de code du Script ne contient aucun état observable JavaScript. Le cache de code peut être sauvegardé en toute sécurité avec la source du script et utilisé pour construire plusieurs fois de nouvelles instances Script.

Les fonctions dans la source Script peuvent être marquées comme compilées paresseusement et elles ne sont pas compilées lors de la construction du Script. Ces fonctions seront compilées lors de leur premier appel. Le cache de code sérialise les métadonnées que V8 connaît actuellement sur le Script qu'il peut utiliser pour accélérer les compilations futures.

js
const script = new vm.Script(`
function add(a, b) {
  return a + b;
}

const x = add(1, 2);
`)

const cacheWithoutAdd = script.createCachedData()
// Dans `cacheWithoutAdd`, la fonction `add()` est marquée pour une compilation complète
// lors de son invocation.

script.runInThisContext()

const cacheWithAdd = script.createCachedData()
// `cacheWithAdd` contient la fonction `add()` entièrement compilée.

script.runInContext(contextifiedObject[, options])

[Historique]

VersionModifications
v6.3.0L'option breakOnSigint est désormais prise en charge.
v0.3.1Ajouté dans : v0.3.1
  • contextifiedObject <Object> Un objet contextified tel que retourné par la méthode vm.createContext().

  • options <Object>

    • displayErrors <boolean> Lorsque true, si une erreur Error se produit lors de la compilation du code, la ligne de code provoquant l'erreur est attachée à la trace de la pile. Par défaut : true.
    • timeout <integer> Spécifie le nombre de millisecondes pour exécuter code avant de terminer l'exécution. Si l'exécution est terminée, une erreur Error sera levée. Cette valeur doit être un entier strictement positif.
    • breakOnSigint <boolean> Si true, la réception de SIGINT (+) mettra fin à l'exécution et lèvera une erreur Error. Les gestionnaires existants de l'événement qui ont été attachés via process.on('SIGINT') sont désactivés pendant l'exécution du script, mais continuent de fonctionner après cela. Par défaut : false.
  • Retourne : <any> le résultat de la toute dernière instruction exécutée dans le script.

Exécute le code compilé contenu par l'objet vm.Script dans le contextifiedObject donné et retourne le résultat. L'exécution du code n'a pas accès à la portée locale.

L'exemple suivant compile du code qui incrémente une variable globale, définit la valeur d'une autre variable globale, puis exécute le code plusieurs fois. Les variables globales sont contenues dans l'objet context.

js
const vm = require('node:vm')

const context = {
  animal: 'cat',
  count: 2,
}

const script = new vm.Script('count += 1; name = "kitty";')

vm.createContext(context)
for (let i = 0; i < 10; ++i) {
  script.runInContext(context)
}

console.log(context)
// Affiche : { animal: 'cat', count: 12, name: 'kitty' }

L'utilisation des options timeout ou breakOnSigint entraînera le démarrage de nouvelles boucles d'événements et de threads correspondants, qui ont des frais généraux de performance non nuls.

script.runInNewContext([contextObject[, options]])

[Historique]

VersionModifications
v22.8.0, v20.18.0L'argument contextObject accepte désormais vm.constants.DONT_CONTEXTIFY.
v14.6.0L'option microtaskMode est désormais prise en charge.
v10.0.0L'option contextCodeGeneration est désormais prise en charge.
v6.3.0L'option breakOnSigint est désormais prise en charge.
v0.3.1Ajouté dans : v0.3.1
  • contextObject <Objet> | <vm.constants.DONT_CONTEXTIFY> | <indéfini> Soit vm.constants.DONT_CONTEXTIFY ou un objet qui sera contextualisé. Si indéfini, un objet contextualisé vide sera créé pour la compatibilité descendante.

  • options <Objet>

    • displayErrors <booléen> Lorsqu'il est défini sur true, si une erreur Error se produit lors de la compilation du code, la ligne de code à l'origine de l'erreur est jointe à la trace de la pile. Valeur par défaut : true.

    • timeout <entier> Spécifie le nombre de millisecondes pendant lesquelles exécuter code avant d'arrêter l'exécution. Si l'exécution est interrompue, une erreur Error sera levée. Cette valeur doit être un entier strictement positif.

    • breakOnSigint <booléen> Si true, la réception de SIGINT (+) interrompra l'exécution et lèvera une erreur Error. Les gestionnaires existants de l'événement qui ont été attachés via process.on('SIGINT') sont désactivés pendant l'exécution du script, mais continuent de fonctionner après cela. Valeur par défaut : false.

    • contextName <chaîne de caractères> Nom lisible par l'homme du contexte nouvellement créé. Valeur par défaut : 'VM Context i', où i est un index numérique croissant du contexte créé.

    • contextOrigin <chaîne de caractères> Origine correspondant au contexte nouvellement créé à des fins d'affichage. L'origine doit être formatée comme une URL, mais avec uniquement le schéma, l'hôte et le port (si nécessaire), comme la valeur de la propriété url.origin d'un objet URL. Plus précisément, cette chaîne doit omettre la barre oblique finale, car elle désigne un chemin. Valeur par défaut : ''.

    • contextCodeGeneration <Objet>

    • strings <booléen> S'il est défini sur false, tous les appels à eval ou aux constructeurs de fonctions (Function, GeneratorFunction, etc.) lèveront une erreur EvalError. Valeur par défaut : true.

    • wasm <booléen> S'il est défini sur false, toute tentative de compilation d'un module WebAssembly lèvera une erreur WebAssembly.CompileError. Valeur par défaut : true.

    • microtaskMode <chaîne de caractères> S'il est défini sur afterEvaluate, les microtâches (tâches planifiées via les Promise et les async function) seront exécutées immédiatement après l'exécution du script. Elles sont incluses dans les portées timeout et breakOnSigint dans ce cas.

  • Retourne : <quelconque> le résultat de la toute dernière instruction exécutée dans le script.

Cette méthode est un raccourci vers script.runInContext(vm.createContext(options), options). Elle fait plusieurs choses à la fois :

L'exemple suivant compile du code qui définit une variable globale, puis exécute le code plusieurs fois dans différents contextes. Les globales sont définies sur et contenues dans chaque context individuel.

js
const vm = require('node:vm')

const script = new vm.Script('globalVar = "set"')

const contexts = [{}, {}, {}]
contexts.forEach(context => {
  script.runInNewContext(context)
})

console.log(contexts)
// Affiche : [{ globalVar: 'set' }, { globalVar: 'set' }, { globalVar: 'set' }]

// Cela lèverait une erreur si le contexte est créé à partir d'un objet contextualisé.
// vm.constants.DONT_CONTEXTIFY permet de créer des contextes avec des objets globaux ordinaires
// qui peuvent être gelés.
const freezeScript = new vm.Script('Object.freeze(globalThis); globalThis;')
const frozenContext = freezeScript.runInNewContext(vm.constants.DONT_CONTEXTIFY)

script.runInThisContext([options])

[Historique]

VersionModifications
v6.3.0L'option breakOnSigint est maintenant prise en charge.
v0.3.1Ajouté dans : v0.3.1
  • options <Objet>

    • displayErrors <booléen> Lorsque true, si une erreur Error se produit lors de la compilation du code, la ligne de code provoquant l'erreur est attachée à la trace de la pile. Valeur par défaut : true.
    • timeout <entier> Spécifie le nombre de millisecondes pour exécuter code avant d'arrêter l'exécution. Si l'exécution est interrompue, une erreur Error sera levée. Cette valeur doit être un entier strictement positif.
    • breakOnSigint <booléen> Si true, la réception de SIGINT (+) mettra fin à l'exécution et lèvera une erreur Error. Les gestionnaires existants de l'événement qui ont été attachés via process.on('SIGINT') sont désactivés pendant l'exécution du script, mais continuent de fonctionner après cela. Valeur par défaut : false.
  • Retourne : <quelconque> le résultat de la toute dernière instruction exécutée dans le script.

Exécute le code compilé contenu par vm.Script dans le contexte de l'objet global actuel. Le code en cours d'exécution n'a pas accès à la portée locale, mais a accès à l'objet global actuel.

L'exemple suivant compile du code qui incrémente une variable global, puis exécute ce code plusieurs fois :

js
const vm = require('node:vm')

global.globalVar = 0

const script = new vm.Script('globalVar += 1', { filename: 'myfile.vm' })

for (let i = 0; i < 1000; ++i) {
  script.runInThisContext()
}

console.log(globalVar)

// 1000

script.sourceMapURL

Ajouté dans : v19.1.0, v18.13.0

Lorsque le script est compilé à partir d’une source contenant un commentaire magique de mappage source, cette propriété sera définie sur l’URL de la mappage source.

js
import vm from 'node:vm'

const script = new vm.Script(`
function myFunc() {}
//# sourceMappingURL=sourcemap.json
`)

console.log(script.sourceMapURL)
// Affiche : sourcemap.json
js
const vm = require('node:vm')

const script = new vm.Script(`
function myFunc() {}
//# sourceMappingURL=sourcemap.json
`)

console.log(script.sourceMapURL)
// Affiche : sourcemap.json

Classe : vm.Module

Ajouté dans : v13.0.0, v12.16.0

[Stable : 1 - Expérimental]

Stable : 1 Stabilité : 1 - Expérimental

Cette fonctionnalité est uniquement disponible avec l’indicateur de commande --experimental-vm-modules activé.

La classe vm.Module fournit une interface de bas niveau pour l’utilisation de modules ECMAScript dans des contextes VM. C’est la contrepartie de la classe vm.Script qui reflète étroitement les enregistrements de modules tels que définis dans la spécification ECMAScript.

Contrairement à vm.Script cependant, chaque objet vm.Module est lié à un contexte dès sa création. Les opérations sur les objets vm.Module sont intrinsèquement asynchrones, contrairement à la nature synchrone des objets vm.Script. L’utilisation de fonctions « async » peut aider à manipuler les objets vm.Module.

L’utilisation d’un objet vm.Module nécessite trois étapes distinctes : création/analyse, liaison et évaluation. Ces trois étapes sont illustrées dans l’exemple suivant.

Cette implémentation se situe à un niveau inférieur au chargeur de module ECMAScript. Il n’y a pas non plus de moyen d’interagir avec le chargeur pour le moment, bien que la prise en charge soit prévue.

js
import vm from 'node:vm'

const contextifiedObject = vm.createContext({
  secret: 42,
  print: console.log,
})

// Étape 1
//
// Créer un module en construisant un nouvel objet `vm.SourceTextModule`. Cela
// analyse le texte source fourni, en lançant une `SyntaxError` si quelque chose ne va pas.
// Par défaut, un module est créé dans le contexte supérieur. Mais ici, nous
// spécifions `contextifiedObject` comme contexte auquel appartient ce module.
//
// Ici, nous essayons d’obtenir l’exportation par défaut du module « foo », et
// de le mettre dans la liaison locale « secret ».

const bar = new vm.SourceTextModule(
  `
  import s from 'foo';
  s;
  print(s);
`,
  { context: contextifiedObject }
)

// Étape 2
//
// « Lier » les dépendances importées de ce module à celui-ci.
//
// Le rappel de liaison fourni (le « liaiseur ») accepte deux arguments : le
// module parent (`bar` dans ce cas) et la chaîne qui est le spécificateur du
// module importé. Le rappel est censé renvoyer un module qui
// correspond au spécificateur fourni, avec certaines exigences documentées
// dans `module.link()`.
//
// Si la liaison n’a pas commencé pour le module renvoyé, le même rappel de liaison
// sera appelé sur le module renvoyé.
//
// Même les modules de niveau supérieur sans dépendances doivent être explicitement liés. Le
// rappel fourni ne serait jamais appelé, cependant.
//
// La méthode link() renvoie une Promise qui sera résolue lorsque toutes les
// Promises renvoyées par le lieur seront résolues.
//
// Remarque : Il s’agit d’un exemple artificiel dans la mesure où la fonction lieur crée un nouveau
// module « foo » à chaque fois qu’elle est appelée. Dans un système de modules complet, un
// cache serait probablement utilisé pour éviter les modules dupliqués.

async function linker(specifier, referencingModule) {
  if (specifier === 'foo') {
    return new vm.SourceTextModule(
      `
      // La variable « secret » fait référence à la variable globale que nous avons ajoutée à
      // « contextifiedObject » lors de la création du contexte.
      export default secret;
    `,
      { context: referencingModule.context }
    )

    // L’utilisation de `contextifiedObject` au lieu de `referencingModule.context`
    // ici fonctionnerait également.
  }
  throw new Error(`Impossible de résoudre la dépendance : ${specifier}`)
}
await bar.link(linker)

// Étape 3
//
// Évaluer le module. La méthode evaluate() renvoie une promesse qui sera
// résolue une fois que le module aura terminé son évaluation.

// Affiche 42.
await bar.evaluate()
js
const vm = require('node:vm')

const contextifiedObject = vm.createContext({
  secret: 42,
  print: console.log,
})

;(async () => {
  // Étape 1
  //
  // Créer un module en construisant un nouvel objet `vm.SourceTextModule`. Cela
  // analyse le texte source fourni, en lançant une `SyntaxError` si quelque chose ne va pas.
  // Par défaut, un module est créé dans le contexte supérieur. Mais ici, nous
  // spécifions `contextifiedObject` comme contexte auquel appartient ce module.
  //
  // Ici, nous essayons d’obtenir l’exportation par défaut du module « foo », et
  // de le mettre dans la liaison locale « secret ».

  const bar = new vm.SourceTextModule(
    `
    import s from 'foo';
    s;
    print(s);
  `,
    { context: contextifiedObject }
  )

  // Étape 2
  //
  // « Lier » les dépendances importées de ce module à celui-ci.
  //
  // Le rappel de liaison fourni (le « liaiseur ») accepte deux arguments : le
  // module parent (`bar` dans ce cas) et la chaîne qui est le spécificateur du
  // module importé. Le rappel est censé renvoyer un module qui
  // correspond au spécificateur fourni, avec certaines exigences documentées
  // dans `module.link()`.
  //
  // Si la liaison n’a pas commencé pour le module renvoyé, le même rappel de liaison
  // sera appelé sur le module renvoyé.
  //
  // Même les modules de niveau supérieur sans dépendances doivent être explicitement liés. Le
  // rappel fourni ne serait jamais appelé, cependant.
  //
  // La méthode link() renvoie une Promise qui sera résolue lorsque toutes les
  // Promises renvoyées par le lieur seront résolues.
  //
  // Remarque : Il s’agit d’un exemple artificiel dans la mesure où la fonction lieur crée un nouveau
  // module « foo » à chaque fois qu’elle est appelée. Dans un système de modules complet, un
  // cache serait probablement utilisé pour éviter les modules dupliqués.

  async function linker(specifier, referencingModule) {
    if (specifier === 'foo') {
      return new vm.SourceTextModule(
        `
        // La variable « secret » fait référence à la variable globale que nous avons ajoutée à
        // « contextifiedObject » lors de la création du contexte.
        export default secret;
      `,
        { context: referencingModule.context }
      )

      // L’utilisation de `contextifiedObject` au lieu de `referencingModule.context`
      // ici fonctionnerait également.
    }
    throw new Error(`Impossible de résoudre la dépendance : ${specifier}`)
  }
  await bar.link(linker)

  // Étape 3
  //
  // Évaluer le module. La méthode evaluate() renvoie une promesse qui sera
  // résolue une fois que le module aura terminé son évaluation.

  // Affiche 42.
  await bar.evaluate()
})()

module.dependencySpecifiers

Les spécificateurs de toutes les dépendances de ce module. Le tableau renvoyé est figé pour interdire toute modification.

Correspond au champ [[RequestedModules]] des enregistrements de modules cycliques dans la spécification ECMAScript.

module.error

Si le module.status est 'errored', cette propriété contient l'exception levée par le module pendant l'évaluation. Si le statut est autre chose, l'accès à cette propriété entraînera une exception levée.

La valeur undefined ne peut pas être utilisée pour les cas où il n'y a pas d'exception levée en raison d'une ambiguïté possible avec throw undefined;.

Correspond au champ [[EvaluationError]] des enregistrements de modules cycliques dans la spécification ECMAScript.

module.evaluate([options])

  • options <Object>

    • timeout <integer> Spécifie le nombre de millisecondes à évaluer avant de terminer l'exécution. Si l'exécution est interrompue, une Error sera levée. Cette valeur doit être un entier strictement positif.
    • breakOnSigint <boolean> Si true, la réception de SIGINT (+) mettra fin à l'exécution et lèvera une Error. Les gestionnaires existants de l'événement qui ont été attachés via process.on('SIGINT') sont désactivés pendant l'exécution du script, mais continuent à fonctionner après cela. Défaut : false.
  • Retourne : <Promise> Restitue undefined en cas de succès.

Évaluer le module.

Cela doit être appelé après que le module a été lié ; sinon, il sera rejeté. Il pourrait également être appelé lorsque le module a déjà été évalué, auquel cas il ne fera rien si l'évaluation initiale s'est terminée avec succès (module.status est 'evaluated') ou il relancera l'exception résultant de l'évaluation initiale (module.status est 'errored').

Cette méthode ne peut pas être appelée pendant l'évaluation du module (module.status est 'evaluating').

Correspond à la méthode concrète Evaluate() des enregistrements de modules cycliques dans la spécification ECMAScript.

module.identifier

L'identifiant du module courant, tel que défini dans le constructeur.

module.link(linker)

[Historique]

VersionModifications
v21.1.0, v20.10.0, v18.19.0L'option extra.assert est renommée extra.attributes. L'ancien nom est toujours fourni pour la compatibilité descendante.
  • linker <Function>

    • specifier <string> Le spécificateur du module demandé :

    • referencingModule <vm.Module> L'objet Module sur lequel link() est appelé.

    • extra <Object>

    • attributes <Object> Les données de l'attribut : Conformément à ECMA-262, les hôtes doivent déclencher une erreur si un attribut non pris en charge est présent.

    • assert <Object> Alias pour extra.attributes.

    • Retourne : <vm.Module> | <Promise>

  • Retourne : <Promise>

Lier les dépendances des modules. Cette méthode doit être appelée avant l'évaluation et ne peut être appelée qu'une seule fois par module.

La fonction est censée retourner un objet Module ou une Promise qui finit par résoudre un objet Module. Le Module retourné doit satisfaire les deux invariants suivants :

  • Il doit appartenir au même contexte que le Module parent.
  • Son status ne doit pas être 'errored'.

Si le status du Module retourné est 'unlinked', cette méthode sera appelée récursivement sur le Module retourné avec la même fonction linker fournie.

link() retourne une Promise qui sera soit résolue lorsque toutes les instances de liaison résolvent un Module valide, soit rejetée si la fonction linker déclenche une exception ou retourne un Module invalide.

La fonction linker correspond approximativement à l'opération abstraite HostResolveImportedModule définie par l'implémentation dans la spécification ECMAScript, avec quelques différences clés :

L'implémentation réelle de HostResolveImportedModule utilisée lors de la liaison des modules est celle qui retourne les modules liés pendant la liaison. À ce stade, tous les modules ayant déjà été entièrement liés, l'implémentation de HostResolveImportedModule est entièrement synchrone selon la spécification.

Correspond au champ de méthode concrète Link() des enregistrements de module cycliques dans la spécification ECMAScript.

module.namespace

L'objet espace de noms du module. Ceci n'est disponible qu'après la fin de la liaison (module.link()).

Correspond à l'opération abstraite GetModuleNamespace dans la spécification ECMAScript.

module.status

L'état actuel du module. Sera l'un des suivants :

  • 'unlinked' : module.link() n'a pas encore été appelé.
  • 'linking' : module.link() a été appelé, mais toutes les Promises renvoyées par la fonction de liaison n'ont pas encore été résolues.
  • 'linked' : Le module a été lié avec succès, et toutes ses dépendances sont liées, mais module.evaluate() n'a pas encore été appelé.
  • 'evaluating' : Le module est en cours d'évaluation via un module.evaluate() sur lui-même ou un module parent.
  • 'evaluated' : Le module a été évalué avec succès.
  • 'errored' : Le module a été évalué, mais une exception a été levée.

Outre 'errored', cette chaîne de caractères d'état correspond au champ [[Status]] de l'enregistrement de module cyclique de la spécification. 'errored' correspond à 'evaluated' dans la spécification, mais avec [[EvaluationError]] défini sur une valeur différente de undefined.

Classe : vm.SourceTextModule

Ajouté dans : v9.6.0

[Stable : 1 - Expérimental]

Stable : 1 Stabilité : 1 - Expérimental

Cette fonctionnalité est uniquement disponible avec l'indicateur de commande --experimental-vm-modules activé.

La classe vm.SourceTextModule fournit l'enregistrement de module de texte source tel que défini dans la spécification ECMAScript.

new vm.SourceTextModule(code[, options])

[Historique]

VersionModifications
v17.0.0, v16.12.0Ajout de la prise en charge des attributs d'importation au paramètre importModuleDynamically.
  • code <chaîne de caractères> Code de module JavaScript à analyser

  • options

    • identifier <chaîne de caractères> Chaîne utilisée dans les traces de pile. Valeur par défaut : 'vm:module(i)'i est un index ascendant spécifique au contexte.

    • cachedData <Buffer> | <TypedArray> | <DataView> Fournit un Buffer ou un TypedArray facultatif, ou un DataView avec les données du cache de code de V8 pour la source fournie. Le code doit être le même que le module à partir duquel ce cachedData a été créé.

    • context <Objet> L'objet contextualisé tel que renvoyé par la méthode vm.createContext(), pour compiler et évaluer ce Module dans. Si aucun contexte n'est spécifié, le module est évalué dans le contexte d'exécution actuel.

    • lineOffset <entier> Spécifie le décalage du numéro de ligne qui est affiché dans les traces de pile produites par ce Module. Valeur par défaut : 0.

    • columnOffset <entier> Spécifie le décalage du numéro de colonne de la première ligne qui est affiché dans les traces de pile produites par ce Module. Valeur par défaut : 0.

    • initializeImportMeta <Fonction> Appelé lors de l'évaluation de ce Module pour initialiser l'import.meta.

    • meta <import.meta>

    • module <vm.SourceTextModule>

    • importModuleDynamically <Fonction> Utilisé pour spécifier comment les modules doivent être chargés pendant l'évaluation de ce module lorsque import() est appelé. Cette option fait partie de l'API des modules expérimentaux. Nous ne recommandons pas de l'utiliser dans un environnement de production. Pour plus d'informations, voir Prise en charge de l'importation dynamique dans les API de compilation.

Crée une nouvelle instance SourceTextModule.

Les propriétés affectées à l'objet import.meta qui sont des objets peuvent permettre au module d'accéder à des informations en dehors du context spécifié. Utilisez vm.runInContext() pour créer des objets dans un contexte spécifique.

js
import vm from 'node:vm'

const contextifiedObject = vm.createContext({ secret: 42 })

const module = new vm.SourceTextModule('Object.getPrototypeOf(import.meta.prop).secret = secret;', {
  initializeImportMeta(meta) {
    // Remarque : cet objet est créé dans le contexte supérieur. Ainsi,
    // Object.getPrototypeOf(import.meta.prop) pointe vers le
    // Object.prototype dans le contexte supérieur plutôt que dans
    // l'objet contextualisé.
    meta.prop = {}
  },
})
// Étant donné que le module n'a aucune dépendance, la fonction de liaison ne sera jamais appelée.
await module.link(() => {})
await module.evaluate()

// Maintenant, Object.prototype.secret sera égal à 42.
//
// Pour résoudre ce problème, remplacez
//     meta.prop = {};
// ci-dessus par
//     meta.prop = vm.runInContext('{}', contextifiedObject);
js
const vm = require('node:vm')
const contextifiedObject = vm.createContext({ secret: 42 })
;(async () => {
  const module = new vm.SourceTextModule('Object.getPrototypeOf(import.meta.prop).secret = secret;', {
    initializeImportMeta(meta) {
      // Remarque : cet objet est créé dans le contexte supérieur. Ainsi,
      // Object.getPrototypeOf(import.meta.prop) pointe vers le
      // Object.prototype dans le contexte supérieur plutôt que dans
      // l'objet contextualisé.
      meta.prop = {}
    },
  })
  // Étant donné que le module n'a aucune dépendance, la fonction de liaison ne sera jamais appelée.
  await module.link(() => {})
  await module.evaluate()
  // Maintenant, Object.prototype.secret sera égal à 42.
  //
  // Pour résoudre ce problème, remplacez
  //     meta.prop = {};
  // ci-dessus par
  //     meta.prop = vm.runInContext('{}', contextifiedObject);
})()

sourceTextModule.createCachedData()

Ajouté dans : v13.7.0, v12.17.0

Crée un cache de code pouvant être utilisé avec l’option cachedData du constructeur SourceTextModule. Retourne un Buffer. Cette méthode peut être appelée plusieurs fois avant que le module n’ait été évalué.

Le cache de code du SourceTextModule ne contient aucun état observable JavaScript. Le cache de code peut être sauvegardé en toute sécurité avec la source du script et utilisé pour construire plusieurs fois de nouvelles instances SourceTextModule.

Les fonctions dans la source SourceTextModule peuvent être marquées comme compilées paresseusement et elles ne sont pas compilées lors de la construction du SourceTextModule. Ces fonctions seront compilées lors de leur première invocation. Le cache de code sérialise les métadonnées que V8 connaît actuellement sur le SourceTextModule qu’il peut utiliser pour accélérer les compilations futures.

js
// Créer un module initial
const module = new vm.SourceTextModule('const a = 1;')

// Créer des données mises en cache à partir de ce module
const cachedData = module.createCachedData()

// Créer un nouveau module en utilisant les données mises en cache. Le code doit être le même.
const module2 = new vm.SourceTextModule('const a = 1;', { cachedData })

Classe : vm.SyntheticModule

Ajouté dans : v13.0.0, v12.16.0

[Stable : 1 - Expérimental]

Stable : 1 Stabilité : 1 - Expérimental

Cette fonctionnalité est uniquement disponible avec l’indicateur de commande --experimental-vm-modules activé.

La classe vm.SyntheticModule fournit l’Enregistrement de module synthétique tel que défini dans la spécification WebIDL. Le but des modules synthétiques est de fournir une interface générique pour exposer des sources non JavaScript aux graphes de modules ECMAScript.

js
const vm = require('node:vm')

const source = '{ "a": 1 }'
const module = new vm.SyntheticModule(['default'], function () {
  const obj = JSON.parse(source)
  this.setExport('default', obj)
})

// Utiliser `module` dans la liaison…

new vm.SyntheticModule(exportNames, evaluateCallback[, options])

Ajouté dans : v13.0.0, v12.16.0

  • exportNames <string[]> Tableau de noms qui seront exportés depuis le module.
  • evaluateCallback <Function> Appelée lorsque le module est évalué.
  • options
    • identifier <string> Chaîne utilisée dans les traces de pile. Valeur par défaut : 'vm:module(i)'i est un index ascendant spécifique au contexte.
    • context <Object> L'objet contextualisé tel que renvoyé par la méthode vm.createContext(), pour compiler et évaluer ce Module.

Crée une nouvelle instance SyntheticModule.

Les objets assignés aux exportations de cette instance peuvent permettre aux importateurs du module d'accéder à des informations en dehors du context spécifié. Utilisez vm.runInContext() pour créer des objets dans un contexte spécifique.

syntheticModule.setExport(name, value)

Ajouté dans : v13.0.0, v12.16.0

  • name <string> Nom de l'exportation à définir.
  • value <any> La valeur à attribuer à l'exportation.

Cette méthode est utilisée après la liaison du module pour définir les valeurs des exportations. Si elle est appelée avant la liaison du module, une erreur ERR_VM_MODULE_STATUS sera levée.

js
import vm from 'node:vm'

const m = new vm.SyntheticModule(['x'], () => {
  m.setExport('x', 1)
})

await m.link(() => {})
await m.evaluate()

assert.strictEqual(m.namespace.x, 1)
js
const vm = require('node:vm')
;(async () => {
  const m = new vm.SyntheticModule(['x'], () => {
    m.setExport('x', 1)
  })
  await m.link(() => {})
  await m.evaluate()
  assert.strictEqual(m.namespace.x, 1)
})()

vm.compileFunction(code[, params[, options]])

[Historique]

VersionModifications
v21.7.0, v20.12.0Ajout du support pour vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.
v19.6.0, v18.15.0La valeur de retour inclut désormais cachedDataRejected avec la même sémantique que la version vm.Script si l'option cachedData a été passée.
v17.0.0, v16.12.0Ajout du support pour les attributs d'importation au paramètre importModuleDynamically.
v15.9.0Ajout de l'option importModuleDynamically.
v14.3.0Suppression de importModuleDynamically en raison de problèmes de compatibilité.
v14.1.0, v13.14.0L'option importModuleDynamically est désormais prise en charge.
v10.10.0Ajouté dans la version : v10.10.0
  • code <string> Le corps de la fonction à compiler.

  • params <string[]> Un tableau de chaînes contenant tous les paramètres de la fonction.

  • options <Object>

    • filename <string> Spécifie le nom de fichier utilisé dans les traces de pile produites par ce script. Défaut : ''.
    • lineOffset <number> Spécifie le décalage du numéro de ligne qui est affiché dans les traces de pile produites par ce script. Défaut : 0.
    • columnOffset <number> Spécifie le décalage du numéro de colonne de la première ligne qui est affiché dans les traces de pile produites par ce script. Défaut : 0.
    • cachedData <Buffer> | <TypedArray> | <DataView> Fournit un Buffer, un TypedArray ou un DataView optionnel avec les données du cache de code de V8 pour la source fournie. Ceci doit être produit par un appel précédent à vm.compileFunction() avec les mêmes code et params.
    • produceCachedData <boolean> Spécifie s'il faut produire de nouvelles données de cache. Défaut : false.
    • parsingContext <Object> L'objet contextualisé dans lequel la fonction doit être compilée.
    • contextExtensions <Object[]> Un tableau contenant une collection d'extensions de contexte (objets encapsulant la portée actuelle) à appliquer lors de la compilation. Défaut : [].
  • importModuleDynamically <Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Utilisé pour spécifier la manière dont les modules doivent être chargés lors de l'évaluation de cette fonction lorsque import() est appelé. Cette option fait partie de l'API expérimentale des modules. Nous ne recommandons pas de l'utiliser en production. Pour plus d'informations, consultez Support de import() dynamique dans les API de compilation.

  • Retourne : <Function>

Compile le code donné dans le contexte fourni (si aucun contexte n'est fourni, le contexte actuel est utilisé), et le retourne encapsulé dans une fonction avec les params donnés.

vm.constants

Ajouté dans : v21.7.0, v20.12.0

Retourne un objet contenant les constantes couramment utilisées pour les opérations de la VM.

vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER

Ajouté dans : v21.7.0, v20.12.0

[Stable : 1 - Expérimental]

Stable : 1 Stabilité : 1.1 - Développement actif

Une constante qui peut être utilisée comme option importModuleDynamically pour vm.Script et vm.compileFunction() afin que Node.js utilise le chargeur ESM par défaut du contexte principal pour charger le module demandé.

Pour plus d’informations, consultez Prise en charge de import() dynamique dans les API de compilation.

vm.createContext([contextObject[, options]])

[Historique]

VersionModifications
v22.8.0, v20.18.0L’argument contextObject accepte désormais vm.constants.DONT_CONTEXTIFY.
v21.7.0, v20.12.0Ajout de la prise en charge de vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.
v21.2.0, v20.11.0L’option importModuleDynamically est désormais prise en charge.
v14.6.0L’option microtaskMode est désormais prise en charge.
v10.0.0Le premier argument ne peut plus être une fonction.
v10.0.0L’option codeGeneration est désormais prise en charge.
v0.3.1Ajouté dans : v0.3.1
  • contextObject <Objet> | <vm.constants.DONT_CONTEXTIFY> | <indéfini> Soit vm.constants.DONT_CONTEXTIFY soit un objet qui sera contextualisé. Si indéfini, un objet contextualisé vide sera créé pour assurer la compatibilité descendante.

  • options <Objet>

    • name <chaîne de caractères> Nom lisible par l’homme du contexte nouvellement créé. Par défaut : 'Contexte VM i', où i est un index numérique croissant du contexte créé.

    • origin <chaîne de caractères> Origine correspondant au contexte nouvellement créé à des fins d’affichage. L’origine doit être formatée comme une URL, mais avec uniquement le schéma, l’hôte et le port (si nécessaire), comme la valeur de la propriété url.origin d’un objet URL. Plus précisément, cette chaîne de caractères doit omettre la barre oblique finale, car elle désigne un chemin. Par défaut : ''.

    • codeGeneration <Objet>

    • strings <booléen> Si défini sur faux, tous les appels à eval ou aux constructeurs de fonctions (Function, GeneratorFunction, etc.) lèveront une erreur EvalError. Par défaut : true.

    • wasm <booléen> Si défini sur faux, toute tentative de compilation d’un module WebAssembly lèvera une erreur WebAssembly.CompileError. Par défaut : true.

    • microtaskMode <chaîne de caractères> Si défini sur afterEvaluate, les microtâches (tâches planifiées via les Promise et les async function) seront exécutées immédiatement après l’exécution d’un script via script.runInContext(). Elles sont incluses dans les portées timeout et breakOnSigint dans ce cas.

    • importModuleDynamically <Fonction> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Utilisé pour spécifier comment les modules doivent être chargés lorsque import() est appelé dans ce contexte sans script ou module de référence. Cette option fait partie de l’API expérimentale des modules. Nous ne recommandons pas de l’utiliser en production. Pour plus d’informations, consultez Prise en charge de import() dynamique dans les API de compilation.

  • Retourne : <Objet> objet contextualisé.

Si contextObject est un objet, la méthode vm.createContext() préparera cet objet et renverra une référence à celui-ci afin qu’il puisse être utilisé dans les appels à vm.runInContext() ou script.runInContext(). À l’intérieur de ces scripts, l’objet global sera encapsulé par contextObject, conservant toutes ses propriétés existantes mais disposant également des objets et fonctions intégrés que possède tout objet global standard. En dehors des scripts exécutés par le module vm, les variables globales resteront inchangées.

js
const vm = require('node:vm')

global.globalVar = 3

const context = { globalVar: 1 }
vm.createContext(context)

vm.runInContext('globalVar *= 2;', context)

console.log(context)
// Affiche : { globalVar: 2 }

console.log(global.globalVar)
// Affiche : 3

Si contextObject est omis (ou passé explicitement comme undefined), un nouvel objet contextualisé vide sera renvoyé.

Lorsque l’objet global dans le contexte nouvellement créé est contextualisé, il présente quelques particularités par rapport aux objets globaux ordinaires. Par exemple, il ne peut pas être figé. Pour créer un contexte sans les particularités de la contextualisation, passez vm.constants.DONT_CONTEXTIFY comme argument contextObject. Consultez la documentation de vm.constants.DONT_CONTEXTIFY pour plus de détails.

La méthode vm.createContext() est principalement utile pour créer un contexte unique pouvant être utilisé pour exécuter plusieurs scripts. Par exemple, si vous émulez un navigateur Web, la méthode peut être utilisée pour créer un contexte unique représentant l’objet global d’une fenêtre, puis exécuter toutes les balises \<script\> ensemble dans ce contexte.

Le name et l’origin fournis du contexte sont rendus visibles via l’API de l’inspecteur.

vm.isContext(objet)

Ajouté dans : v0.11.7

Retourne true si l’objet objet donné a été contexualisé à l’aide de vm.createContext(), ou s’il s’agit de l’objet global d’un contexte créé à l’aide de vm.constants.DONT_CONTEXTIFY.

vm.measureMemory([options])

Ajouté dans : v13.10.0

[Stable : 1 - Expérimental]

Stable : 1 Stabilité : 1 - Expérimental

Mesurer la mémoire connue de V8 et utilisée par tous les contextes connus de l’isolant V8 actuel, ou le contexte principal.

  • options <Objet> Facultatif.

    • mode <chaîne de caractères> Soit 'summary' soit 'detailed'. En mode résumé, seule la mémoire mesurée pour le contexte principal sera retournée. En mode détaillé, la mémoire mesurée pour tous les contextes connus de l’isolant V8 actuel sera retournée. Défaut : 'summary'
    • execution <chaîne de caractères> Soit 'default' soit 'eager'. Avec l’exécution par défaut, la promesse ne sera pas résolue avant le début de la prochaine collecte de déchets planifiée, ce qui peut prendre un certain temps (ou jamais si le programme se termine avant la prochaine collecte de déchets). Avec l’exécution immédiate, la collecte de déchets sera lancée immédiatement pour mesurer la mémoire. Défaut : 'default'
  • Retourne : <Promise> Si la mémoire est correctement mesurée, la promesse sera résolue avec un objet contenant des informations sur l’utilisation de la mémoire. Sinon, elle sera rejetée avec une erreur ERR_CONTEXT_NOT_INITIALIZED.

Le format de l’objet que la Promise retournée peut résoudre est spécifique au moteur V8 et peut changer d’une version de V8 à l’autre.

Le résultat retourné est différent des statistiques retournées par v8.getHeapSpaceStatistics() en ce sens que vm.measureMemory() mesure la mémoire accessible par chaque contexte spécifique à V8 dans l’instance actuelle du moteur V8, tandis que le résultat de v8.getHeapSpaceStatistics() mesure la mémoire occupée par chaque espace de tas dans l’instance V8 actuelle.

js
const vm = require('node:vm')
// Mesurer la mémoire utilisée par le contexte principal.
vm.measureMemory({ mode: 'summary' })
  // Ceci est identique à vm.measureMemory()
  .then(result => {
    // Le format actuel est :
    // {
    //   total: {
    //      jsMemoryEstimate: 2418479, jsMemoryRange: [ 2418479, 2745799 ]
    //    }
    // }
    console.log(result)
  })

const context = vm.createContext({ a: 1 })
vm.measureMemory({ mode: 'detailed', execution: 'eager' }).then(result => {
  // Référencez le contexte ici afin qu’il ne soit pas collecté par le ramasse-miettes
  // avant que la mesure ne soit terminée.
  console.log(context.a)
  // {
  //   total: {
  //     jsMemoryEstimate: 2574732,
  //     jsMemoryRange: [ 2574732, 2904372 ]
  //   },
  //   current: {
  //     jsMemoryEstimate: 2438996,
  //     jsMemoryRange: [ 2438996, 2768636 ]
  //   },
  //   other: [
  //     {
  //       jsMemoryEstimate: 135736,
  //       jsMemoryRange: [ 135736, 465376 ]
  //     }
  //   ]
  // }
  console.log(result)
})

vm.runInContext(code, contextifiedObject[, options])

[Historique]

VersionModifications
v21.7.0, v20.12.0Ajout de la prise en charge de vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.
v17.0.0, v16.12.0Ajout de la prise en charge des attributs d'importation au paramètre importModuleDynamically.
v6.3.0L'option breakOnSigint est désormais prise en charge.
v0.3.1Ajouté dans : v0.3.1
  • code <string> Le code JavaScript à compiler et à exécuter.
  • contextifiedObject <Object> L'objet contextualisé qui sera utilisé comme global lors de la compilation et de l'exécution du code.
  • options <Object> | <string>
    • filename <string> Spécifie le nom de fichier utilisé dans les traces de pile produites par ce script. Valeur par défaut : 'evalmachine.\<anonymous\>'.
    • lineOffset <number> Spécifie le décalage du numéro de ligne affiché dans les traces de pile produites par ce script. Valeur par défaut : 0.
    • columnOffset <number> Spécifie le décalage du numéro de colonne de la première ligne affiché dans les traces de pile produites par ce script. Valeur par défaut : 0.
    • displayErrors <boolean> Si true, si une erreur Error se produit lors de la compilation du code, la ligne de code à l'origine de l'erreur est attachée à la trace de pile. Valeur par défaut : true.
    • timeout <integer> Spécifie le nombre de millisecondes pour exécuter le code avant d'arrêter l'exécution. Si l'exécution est interrompue, une erreur Error sera levée. Cette valeur doit être un entier strictement positif.
    • breakOnSigint <boolean> Si true, la réception de SIGINT (+) mettra fin à l'exécution et lèvera une erreur Error. Les gestionnaires existants de l'événement qui ont été attachés via process.on('SIGINT') sont désactivés pendant l'exécution du script, mais continuent de fonctionner après cela. Valeur par défaut : false.
    • cachedData <Buffer> | <TypedArray> | <DataView> Fournit un Buffer ou un TypedArray, ou un DataView facultatif avec les données du cache de code de V8 pour la source fournie.
    • importModuleDynamically <Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Utilisé pour spécifier comment les modules doivent être chargés pendant l'évaluation de ce script lorsque import() est appelé. Cette option fait partie de l'API expérimentale des modules. Nous ne recommandons pas de l'utiliser dans un environnement de production. Pour plus d'informations, consultez Prise en charge de import() dynamique dans les API de compilation.

La méthode vm.runInContext() compile le code, l'exécute dans le contexte de contextifiedObject, puis renvoie le résultat. L'exécution du code n'a pas accès à la portée locale. L'objet contextifiedObject doit avoir été préalablement contextualisé à l'aide de la méthode vm.createContext().

Si options est une chaîne, elle spécifie le nom de fichier.

L'exemple suivant compile et exécute différents scripts en utilisant un seul objet contextualisé :

js
const vm = require('node:vm')

const contextObject = { globalVar: 1 }
vm.createContext(contextObject)

for (let i = 0; i < 10; ++i) {
  vm.runInContext('globalVar *= 2;', contextObject)
}
console.log(contextObject)
// Affiche : { globalVar: 1024 }

vm.runInNewContext(code[, contextObject[, options]])

[Historique]

VersionModifications
v22.8.0, v20.18.0L'argument contextObject accepte désormais vm.constants.DONT_CONTEXTIFY.
v21.7.0, v20.12.0Ajout de la prise en charge de vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.
v17.0.0, v16.12.0Ajout de la prise en charge des attributs d'importation au paramètre importModuleDynamically.
v14.6.0L'option microtaskMode est désormais prise en charge.
v10.0.0L'option contextCodeGeneration est désormais prise en charge.
v6.3.0L'option breakOnSigint est désormais prise en charge.
v0.3.1Ajouté dans : v0.3.1
  • code <string> Le code JavaScript à compiler et à exécuter.

  • contextObject <Object> | <vm.constants.DONT_CONTEXTIFY> | <undefined> Soit vm.constants.DONT_CONTEXTIFY soit un objet qui sera contexualisé. Si undefined, un objet contextualisé vide sera créé pour assurer la compatibilité descendante.

  • options <Object> | <string>

    • filename <string> Spécifie le nom de fichier utilisé dans les traces de pile produites par ce script. Défaut : 'evalmachine.\<anonymous\>'.

    • lineOffset <number> Spécifie le décalage du numéro de ligne qui est affiché dans les traces de pile produites par ce script. Défaut : 0.

    • columnOffset <number> Spécifie le décalage du numéro de colonne de la première ligne qui est affiché dans les traces de pile produites par ce script. Défaut : 0.

    • displayErrors <boolean> Lorsqu'il est défini sur true, si une erreur Error se produit lors de la compilation du code, la ligne de code à l'origine de l'erreur est ajoutée à la trace de pile. Défaut : true.

    • timeout <integer> Spécifie le nombre de millisecondes pour exécuter code avant d'arrêter l'exécution. Si l'exécution est interrompue, une erreur Error sera levée. Cette valeur doit être un entier strictement positif.

    • breakOnSigint <boolean> Si true, la réception de SIGINT (+) interrompra l'exécution et lèvera une erreur Error. Les gestionnaires existants de l'événement qui ont été attachés via process.on('SIGINT') sont désactivés pendant l'exécution du script, mais continuent de fonctionner après cela. Défaut : false.

    • contextName <string> Nom lisible par l'homme du contexte nouvellement créé. Défaut : 'VM Context i', où i est un index numérique ascendant du contexte créé.

    • contextOrigin <string> Origine correspondant au contexte nouvellement créé à des fins d'affichage. L'origine doit être formatée comme une URL, mais avec uniquement le schéma, l'hôte et le port (si nécessaire), comme la valeur de la propriété url.origin d'un objet URL. Plus précisément, cette chaîne doit omettre la barre oblique de fin, car elle désigne un chemin. Défaut : ''.

    • contextCodeGeneration <Object>

    • strings <boolean> Si défini sur false, tout appel à eval ou aux constructeurs de fonctions (Function, GeneratorFunction, etc.) lèvera une erreur EvalError. Défaut : true.

    • wasm <boolean> Si défini sur false, toute tentative de compilation d'un module WebAssembly lèvera une erreur WebAssembly.CompileError. Défaut : true.

    • cachedData <Buffer> | <TypedArray> | <DataView> Fournit un Buffer ou un TypedArray, ou un DataView optionnel avec les données du cache de code de V8 pour la source fournie.

    • importModuleDynamically <Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Utilisé pour spécifier comment les modules doivent être chargés pendant l'évaluation de ce script lorsque import() est appelé. Cette option fait partie de l'API expérimentale des modules. Nous ne recommandons pas de l'utiliser dans un environnement de production. Pour plus d'informations, consultez Prise en charge de import() dynamique dans les API de compilation.

    • microtaskMode <string> Si défini sur afterEvaluate, les microtâches (tâches planifiées via les Promise et les async function) seront exécutées immédiatement après l'exécution du script. Elles sont incluses dans les portées timeout et breakOnSigint dans ce cas.

  • Retourne : <any> le résultat de la toute dernière instruction exécutée dans le script.

Cette méthode est un raccourci pour (new vm.Script(code, options)).runInContext(vm.createContext(options), options). Si options est une chaîne, elle spécifie le nom du fichier.

Elle fait plusieurs choses à la fois :

L'exemple suivant compile et exécute un code qui incrémente une variable globale et en définit une nouvelle. Ces variables globales sont contenues dans contextObject.

js
const vm = require('node:vm')

const contextObject = {
  animal: 'cat',
  count: 2,
}

vm.runInNewContext('count += 1; name = "kitty"', contextObject)
console.log(contextObject)
// Affiche : { animal: 'cat', count: 3, name: 'kitty' }

// Cela lèverait une erreur si le contexte est créé à partir d'un objet contextualisé.
// vm.constants.DONT_CONTEXTIFY permet de créer des contextes avec des objets globaux ordinaires qui
// peuvent être figés.
const frozenContext = vm.runInNewContext('Object.freeze(globalThis); globalThis;', vm.constants.DONT_CONTEXTIFY)

vm.runInThisContext(code[, options])

[Historique]

VersionModifications
v21.7.0, v20.12.0Ajout de la prise en charge de vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.
v17.0.0, v16.12.0Ajout de la prise en charge des attributs d'importation au paramètre importModuleDynamically.
v6.3.0L'option breakOnSigint est désormais prise en charge.
v0.3.1Ajouté dans : v0.3.1
  • code <string> Le code JavaScript à compiler et à exécuter.

  • options <Object> | <string>

    • filename <string> Spécifie le nom de fichier utilisé dans les traces de pile produites par ce script. Valeur par défaut : 'evalmachine.\<anonymous\>'.
    • lineOffset <number> Spécifie le décalage du numéro de ligne qui est affiché dans les traces de pile produites par ce script. Valeur par défaut : 0.
    • columnOffset <number> Spécifie le décalage du numéro de colonne de la première ligne qui est affiché dans les traces de pile produites par ce script. Valeur par défaut : 0.
    • displayErrors <boolean> Lorsque la valeur est true, si une erreur Error se produit lors de la compilation du code, la ligne de code à l'origine de l'erreur est jointe à la trace de pile. Valeur par défaut : true.
    • timeout <integer> Spécifie le nombre de millisecondes pour exécuter code avant d'arrêter l'exécution. Si l'exécution est interrompue, une erreur Error est levée. Cette valeur doit être un entier strictement positif.
    • breakOnSigint <boolean> Si la valeur est true, la réception de SIGINT (+) met fin à l'exécution et lève une erreur Error. Les gestionnaires existants de l'événement qui ont été attachés via process.on('SIGINT') sont désactivés pendant l'exécution du script, mais continuent de fonctionner après cela. Valeur par défaut : false.
    • cachedData <Buffer> | <TypedArray> | <DataView> Fournit un Buffer, un TypedArray ou un DataView facultatif avec les données du cache de code de V8 pour la source fournie.
    • importModuleDynamically <Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Utilisé pour spécifier comment les modules doivent être chargés pendant l'évaluation de ce script lorsque import() est appelé. Cette option fait partie de l'API expérimentale des modules. Nous ne recommandons pas de l'utiliser dans un environnement de production. Pour plus d'informations, consultez Prise en charge de import() dynamique dans les API de compilation.
  • Retourne : <any> le résultat de la toute dernière instruction exécutée dans le script.

vm.runInThisContext() compile code, l'exécute dans le contexte du global actuel et renvoie le résultat. L'exécution du code n'a pas accès à la portée locale, mais a accès à l'objet global actuel.

Si options est une chaîne, alors elle spécifie le nom de fichier.

L'exemple suivant illustre l'utilisation de vm.runInThisContext() et de la fonction JavaScript eval() pour exécuter le même code :

js
const vm = require('node:vm')
let localVar = 'initial value'

const vmResult = vm.runInThisContext('localVar = "vm";')
console.log(`vmResult: '${vmResult}', localVar: '${localVar}'`)
// Affiche : vmResult: 'vm', localVar: 'initial value'

const evalResult = eval('localVar = "eval";')
console.log(`evalResult: '${evalResult}', localVar: '${localVar}'`)
// Affiche : evalResult: 'eval', localVar: 'eval'

Comme vm.runInThisContext() n'a pas accès à la portée locale, localVar est inchangé. En revanche, eval() a accès à la portée locale, donc la valeur localVar est modifiée. De cette façon, vm.runInThisContext() ressemble beaucoup à un appel eval() indirect, par exemple (0,eval)('code').

Exemple : Exécution d'un serveur HTTP dans une VM

Lors de l'utilisation de script.runInThisContext() ou de vm.runInThisContext(), le code est exécuté dans le contexte global V8 courant. Le code passé à ce contexte VM aura sa propre portée isolée.

Pour exécuter un serveur web simple à l'aide du module node:http, le code passé au contexte doit soit appeler require('node:http') lui-même, soit avoir une référence au module node:http qui lui est passée. Par exemple :

js
'use strict'
const vm = require('node:vm')

const code = `
((require) => {
  const http = require('node:http');

  http.createServer((request, response) => {
    response.writeHead(200, { 'Content-Type': 'text/plain' });
    response.end('Hello World\\n');
  }).listen(8124);

  console.log('Serveur en cours d\'exécution sur http://127.0.0.1:8124/');
})`

vm.runInThisContext(code)(require)

Le require() dans le cas ci-dessus partage l'état avec le contexte duquel il est passé. Cela peut introduire des risques lorsque du code non fiable est exécuté, par exemple en modifiant les objets du contexte de manière indésirable.

Que signifie « contextifier » un objet ?

Tout JavaScript exécuté dans Node.js s'exécute dans la portée d'un « contexte ». Selon le Guide de l'intégrateur V8 :

Lorsque la méthode vm.createContext() est appelée avec un objet, l'argument contextObject sera utilisé pour encapsuler l'objet global d'une nouvelle instance d'un contexte V8 (si contextObject est undefined, un nouvel objet sera créé à partir du contexte courant avant sa contextualisation). Ce contexte V8 fournit au code exécuté à l'aide des méthodes du module node:vm un environnement global isolé dans lequel il peut fonctionner. Le processus de création du contexte V8 et de son association avec le contextObject dans le contexte externe est ce que ce document appelle « contextifier » l'objet.

La contextualisation introduirait quelques bizarreries à la valeur globalThis dans le contexte. Par exemple, elle ne peut pas être figée, et elle n'est pas référentiellement égale à contextObject dans le contexte externe.

js
const vm = require('node:vm')

// Une option `contextObject` indéfinie contextualise l'objet global.
const context = vm.createContext()
console.log(vm.runInContext('globalThis', context) === context) // false
// Un objet global contextualisé ne peut pas être figé.
try {
  vm.runInContext('Object.freeze(globalThis);', context)
} catch (e) {
  console.log(e) // TypeError: Impossible de figer
}
console.log(vm.runInContext('globalThis.foo = 1; foo;', context)) // 1

Pour créer un contexte avec un objet global ordinaire et accéder à un proxy global dans le contexte externe avec moins de bizarreries, spécifiez vm.constants.DONT_CONTEXTIFY comme argument contextObject.

vm.constants.DONT_CONTEXTIFY

Cette constante, lorsqu'elle est utilisée comme argument contextObject dans les API vm, indique à Node.js de créer un contexte sans encapsuler son objet global avec un autre objet de manière spécifique à Node.js. En conséquence, la valeur globalThis à l'intérieur du nouveau contexte se comporterait plus près d'une valeur ordinaire.

js
const vm = require('node:vm')

// Utilisez vm.constants.DONT_CONTEXTIFY pour figer l'objet global.
const context = vm.createContext(vm.constants.DONT_CONTEXTIFY)
vm.runInContext('Object.freeze(globalThis);', context)
try {
  vm.runInContext('bar = 1; bar;', context)
} catch (e) {
  console.log(e) // Uncaught ReferenceError: bar is not defined
}

Lorsque vm.constants.DONT_CONTEXTIFY est utilisé comme argument contextObject pour vm.createContext(), l'objet retourné est un objet de type proxy pour l'objet global dans le nouveau contexte avec moins de particularités spécifiques à Node.js. Il est référentiellement égal à la valeur globalThis dans le nouveau contexte, peut être modifié de l'extérieur du contexte et peut être utilisé pour accéder directement aux éléments intégrés dans le nouveau contexte.

js
const vm = require('node:vm')

const context = vm.createContext(vm.constants.DONT_CONTEXTIFY)

// L'objet retourné est référentiellement égal à globalThis dans le nouveau contexte.
console.log(vm.runInContext('globalThis', context) === context) // true

// Peut être utilisé pour accéder aux globales dans le nouveau contexte directement.
console.log(context.Array) // [Function: Array]
vm.runInContext('foo = 1;', context)
console.log(context.foo) // 1
context.bar = 1
console.log(vm.runInContext('bar;', context)) // 1

// Peut être figé et cela affecte le contexte interne.
Object.freeze(context)
try {
  vm.runInContext('baz = 1; baz;', context)
} catch (e) {
  console.log(e) // Uncaught ReferenceError: baz is not defined
}

Interactions de délai d'attente avec les tâches asynchrones et les Promises

Les Promise et les async function peuvent planifier des tâches exécutées par le moteur JavaScript de manière asynchrone. Par défaut, ces tâches sont exécutées une fois que toutes les fonctions JavaScript de la pile actuelle ont fini de s'exécuter. Cela permet d'échapper à la fonctionnalité des options timeout et breakOnSigint.

Par exemple, le code suivant exécuté par vm.runInNewContext() avec un délai d'attente de 5 millisecondes planifie une boucle infinie à exécuter après la résolution d'une promesse. La boucle planifiée n'est jamais interrompue par le délai d'attente :

js
const vm = require('node:vm')

function loop() {
  console.log('entering loop')
  while (1) console.log(Date.now())
}

vm.runInNewContext('Promise.resolve().then(() => loop());', { loop, console }, { timeout: 5 })
// Ceci est imprimé *avant* 'entering loop' (!)
console.log('done executing')

Ceci peut être résolu en passant microtaskMode: 'afterEvaluate' au code qui crée le Context :

js
const vm = require('node:vm')

function loop() {
  while (1) console.log(Date.now())
}

vm.runInNewContext(
  'Promise.resolve().then(() => loop());',
  { loop, console },
  { timeout: 5, microtaskMode: 'afterEvaluate' }
)

Dans ce cas, la microtâche planifiée via promise.then() sera exécutée avant le retour de vm.runInNewContext(), et sera interrompue par la fonctionnalité timeout. Ceci s'applique uniquement au code exécuté dans un vm.Context, donc par exemple vm.runInThisContext() ne prend pas cette option.

Les rappels Promise sont entrés dans la file d'attente des microtâches du contexte dans lequel ils ont été créés. Par exemple, si () =\> loop() est remplacé par simplement loop dans l'exemple ci-dessus, alors loop sera poussé dans la file d'attente globale des microtâches, car il s'agit d'une fonction du contexte externe (principal), et pourra donc également échapper au délai d'attente.

Si les fonctions de planification asynchrone telles que process.nextTick(), queueMicrotask(), setTimeout(), setImmediate(), etc. sont rendues disponibles dans un vm.Context, les fonctions qui leur sont passées seront ajoutées aux files d'attente globales, qui sont partagées par tous les contextes. Par conséquent, les rappels passés à ces fonctions ne sont pas contrôlables non plus par le délai d'attente.

Prise en charge de import() dynamique dans les API de compilation

Les API suivantes prennent en charge une option importModuleDynamically pour activer import() dynamique dans le code compilé par le module vm.

  • new vm.Script
  • vm.compileFunction()
  • new vm.SourceTextModule
  • vm.runInThisContext()
  • vm.runInContext()
  • vm.runInNewContext()
  • vm.createContext()

Cette option fait toujours partie de l'API expérimentale des modules. Nous ne recommandons pas de l'utiliser en environnement de production.

Lorsque l'option importModuleDynamically n'est pas spécifiée ou est indéfinie

Si cette option n'est pas spécifiée, ou si elle est undefined, le code contenant import() peut toujours être compilé par les API vm, mais lorsque le code compilé est exécuté et qu'il appelle effectivement import(), le résultat sera rejeté avec ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING.

Lorsque importModuleDynamically est vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER

Cette option n'est actuellement pas prise en charge pour vm.SourceTextModule.

Avec cette option, lorsqu'un import() est initié dans le code compilé, Node.js utilisera le chargeur ESM par défaut du contexte principal pour charger le module demandé et le renvoyer au code en cours d'exécution.

Cela donne accès aux modules intégrés de Node.js tels que fs ou http au code en cours de compilation. Si le code est exécuté dans un contexte différent, sachez que les objets créés par les modules chargés à partir du contexte principal proviennent toujours du contexte principal et ne sont pas instanceof les classes intégrées du nouveau contexte.

js
const { Script, constants } = require('node:vm')
const script = new Script('import("node:fs").then(({readFile}) => readFile instanceof Function)', {
  importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER,
})

// false : l’URL chargée à partir du contexte principal n’est pas une instance de la classe Function
// dans le nouveau contexte.
script.runInNewContext().then(console.log)
js
import { Script, constants } from 'node:vm'

const script = new Script('import("node:fs").then(({readFile}) => readFile instanceof Function)', {
  importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER,
})

// false : l’URL chargée à partir du contexte principal n’est pas une instance de la classe Function
// dans le nouveau contexte.
script.runInNewContext().then(console.log)

Cette option permet également au script ou à la fonction de charger des modules utilisateur :

js
import { Script, constants } from 'node:vm'
import { resolve } from 'node:path'
import { writeFileSync } from 'node:fs'

// Écrire test.js et test.txt dans le répertoire où le script actuel
// en cours d’exécution est situé.
writeFileSync(resolve(import.meta.dirname, 'test.mjs'), 'export const filename = "./test.json";')
writeFileSync(resolve(import.meta.dirname, 'test.json'), '{"hello": "world"}')

// Compiler un script qui charge test.mjs puis test.json
// comme si le script était placé dans le même répertoire.
const script = new Script(
  `(async function() {
    const { filename } = await import('./test.mjs');
    return import(filename, { with: { type: 'json' } })
  })();`,
  {
    filename: resolve(import.meta.dirname, 'test-with-default.js'),
    importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER,
  }
)

// { default: { hello: 'world' } }
script.runInThisContext().then(console.log)
js
const { Script, constants } = require('node:vm')
const { resolve } = require('node:path')
const { writeFileSync } = require('node:fs')

// Écrire test.js et test.txt dans le répertoire où le script actuel
// en cours d’exécution est situé.
writeFileSync(resolve(__dirname, 'test.mjs'), 'export const filename = "./test.json";')
writeFileSync(resolve(__dirname, 'test.json'), '{"hello": "world"}')

// Compiler un script qui charge test.mjs puis test.json
// comme si le script était placé dans le même répertoire.
const script = new Script(
  `(async function() {
    const { filename } = await import('./test.mjs');
    return import(filename, { with: { type: 'json' } })
  })();`,
  {
    filename: resolve(__dirname, 'test-with-default.js'),
    importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER,
  }
)

// { default: { hello: 'world' } }
script.runInThisContext().then(console.log)

Il existe quelques mises en garde concernant le chargement des modules utilisateur à l'aide du chargeur par défaut du contexte principal :

Lorsque importModuleDynamically est une fonction

Lorsque importModuleDynamically est une fonction, elle sera invoquée lorsque import() est appelé dans le code compilé afin de permettre aux utilisateurs de personnaliser la façon dont le module demandé doit être compilé et évalué. Actuellement, l'instance Node.js doit être lancée avec l'indicateur --experimental-vm-modules pour que cette option fonctionne. Si l'indicateur n'est pas défini, ce rappel sera ignoré. Si le code évalué appelle effectivement import(), le résultat sera rejeté avec ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG.

Le rappel importModuleDynamically(specifier, referrer, importAttributes) a la signature suivante :

  • specifier <string> spécificateur passé à import()
  • referrer <vm.Script> | <Function> | <vm.SourceTextModule> | <Object> Le référent est le vm.Script compilé pour new vm.Script, vm.runInThisContext, vm.runInContext et vm.runInNewContext. C'est la Function compilée pour vm.compileFunction, le vm.SourceTextModule compilé pour new vm.SourceTextModule, et le contexte Object pour vm.createContext().
  • importAttributes <Object> La valeur "with" passée au paramètre optionnel optionsExpression, ou un objet vide si aucune valeur n'a été fournie.
  • Retourne : <Module Namespace Object> | <vm.Module> Il est recommandé de retourner un vm.Module afin de profiter du suivi des erreurs et d'éviter les problèmes avec les espaces de noms qui contiennent des exportations de fonctions then.
js
// Ce script doit être exécuté avec --experimental-vm-modules.
import { Script, SyntheticModule } from 'node:vm'

const script = new Script('import("foo.json", { with: { type: "json" } })', {
  async importModuleDynamically(specifier, referrer, importAttributes) {
    console.log(specifier) // 'foo.json'
    console.log(referrer) // Le script compilé
    console.log(importAttributes) // { type: 'json' }
    const m = new SyntheticModule(['bar'], () => {})
    await m.link(() => {})
    m.setExport('bar', { hello: 'world' })
    return m
  },
})
const result = await script.runInThisContext()
console.log(result) //  { bar: { hello: 'world' } }
js
// Ce script doit être exécuté avec --experimental-vm-modules.
const { Script, SyntheticModule } = require('node:vm')

;(async function main() {
  const script = new Script('import("foo.json", { with: { type: "json" } })', {
    async importModuleDynamically(specifier, referrer, importAttributes) {
      console.log(specifier) // 'foo.json'
      console.log(referrer) // Le script compilé
      console.log(importAttributes) // { type: 'json' }
      const m = new SyntheticModule(['bar'], () => {})
      await m.link(() => {})
      m.setExport('bar', { hello: 'world' })
      return m
    },
  })
  const result = await script.runInThisContext()
  console.log(result) //  { bar: { hello: 'world' } }
})()