Skip to content

VM (ejecución de JavaScript)

[Estable: 2 - Estable]

Estable: 2 Estabilidad: 2 - Estable

Código fuente: lib/vm.js

El módulo node:vm permite compilar y ejecutar código dentro de los contextos de la máquina virtual V8.

El módulo node:vm no es un mecanismo de seguridad. No lo utilice para ejecutar código no confiable.

El código JavaScript se puede compilar y ejecutar inmediatamente o compilar, guardar y ejecutar más tarde.

Un caso de uso común es ejecutar el código en un Contexto V8 diferente. Esto significa que el código invocado tiene un objeto global diferente al código que lo invoca.

Se puede proporcionar el contexto mediante la contextificación de un objeto. El código invocado trata cualquier propiedad en el contexto como una variable global. Cualquier cambio en las variables globales causado por el código invocado se refleja en el objeto de contexto.

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

const x = 1

const context = { x: 2 }
vm.createContext(context) // Contextificar el objeto.

const code = 'x += 40; var y = 17;'
// `x` e `y` son variables globales en el contexto.
// Inicialmente, x tiene el valor 2 porque ese es el valor de context.x.
vm.runInContext(code, context)

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

console.log(x) // 1; y no está definida.

Clase: vm.Script

Añadido en: v0.3.1

Las instancias de la clase vm.Script contienen scripts precompilados que se pueden ejecutar en contextos específicos.

new vm.Script(code[, options])

[Historial]

VersiónCambios
v21.7.0, v20.12.0Se agregó soporte para vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.
v17.0.0, v16.12.0Se agregó soporte para atributos de importación al parámetro importModuleDynamically.
v10.6.0produceCachedData está obsoleto en favor de script.createCachedData().
v5.7.0Ahora se admiten las opciones cachedData y produceCachedData.
v0.3.1Añadido en: v0.3.1
  • code <string> El código JavaScript a compilar.
  • options <Object> | <string>
    • filename <string> Especifica el nombre de archivo utilizado en los seguimientos de pila producidos por este script. Predeterminado: 'evalmachine.\<anonymous\>'.
    • lineOffset <number> Especifica el desplazamiento del número de línea que se muestra en los seguimientos de pila producidos por este script. Predeterminado: 0.
    • columnOffset <number> Especifica el desplazamiento del número de columna de la primera línea que se muestra en los seguimientos de pila producidos por este script. Predeterminado: 0.
    • cachedData <Buffer> | <TypedArray> | <DataView> Proporciona un Buffer opcional, o TypedArray, o DataView con datos de caché de código de V8 para la fuente suministrada. Cuando se proporciona, el valor cachedDataRejected se establecerá en true o false dependiendo de la aceptación de los datos por parte de V8.
    • produceCachedData <boolean> Cuando es true y no hay cachedData presente, V8 intentará producir datos de caché de código para code. Si tiene éxito, se producirá un Buffer con datos de caché de código de V8 y se almacenará en la propiedad cachedData de la instancia vm.Script devuelta. El valor cachedDataProduced se establecerá en true o false dependiendo de si los datos de caché de código se producen correctamente. Esta opción está obsoleta en favor de script.createCachedData(). Predeterminado: false.
    • importModuleDynamically <Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Se utiliza para especificar cómo se deben cargar los módulos durante la evaluación de este script cuando se llama a import(). Esta opción es parte de la API de módulos experimental. No recomendamos usarla en un entorno de producción. Para obtener información detallada, consulte Soporte de import() dinámico en las API de compilación.

Si options es una cadena, especifica el nombre del archivo.

La creación de un nuevo objeto vm.Script compila code, pero no lo ejecuta. El vm.Script compilado se puede ejecutar varias veces más tarde. El code no está enlazado a ningún objeto global; más bien, se enlaza antes de cada ejecución, solo para esa ejecución.

script.cachedDataRejected

Agregado en: v5.7.0

Cuando se proporciona cachedData para crear el vm.Script, este valor se establecerá en true o false dependiendo de la aceptación de los datos por parte de V8. De lo contrario, el valor es undefined.

script.createCachedData()

Agregado en: v10.6.0

Crea una caché de código que se puede usar con la opción cachedData del constructor Script. Devuelve un Buffer. Este método se puede llamar en cualquier momento y cualquier número de veces.

La caché de código del Script no contiene ningún estado observable de JavaScript. Es seguro guardar la caché de código junto con la fuente del script y usarla para construir nuevas instancias de Script varias veces.

Las funciones en la fuente del Script se pueden marcar como compiladas de forma diferida y no se compilan al construir el Script. Estas funciones se compilarán cuando se invoquen por primera vez. La caché de código serializa los metadatos que V8 conoce actualmente sobre el Script que puede usar para acelerar futuras compilaciones.

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

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

const cacheWithoutAdd = script.createCachedData()
// En `cacheWithoutAdd` la función `add()` está marcada para una compilación completa
// tras la invocación.

script.runInThisContext()

const cacheWithAdd = script.createCachedData()
// `cacheWithAdd` contiene la función `add()` completamente compilada.

script.runInContext(contextifiedObject[, options])

[Historial]

VersiónCambios
v6.3.0La opción breakOnSigint ahora es compatible.
v0.3.1Agregado en: v0.3.1
  • contextifiedObject <Objeto> Un objeto contextificado como el devuelto por el método vm.createContext().

  • options <Objeto>

    • displayErrors <booleano> Cuando es true, si ocurre un Error mientras se compila el code, la línea de código que causa el error se adjunta al seguimiento de la pila. Predeterminado: true.
    • timeout <entero> Especifica el número de milisegundos para ejecutar code antes de terminar la ejecución. Si la ejecución se termina, se lanzará un Error. Este valor debe ser un entero estrictamente positivo.
    • breakOnSigint <booleano> Si es true, recibir SIGINT (+) terminará la ejecución y lanzará un Error. Los controladores existentes para el evento que se han adjuntado a través de process.on('SIGINT') se deshabilitan durante la ejecución del script, pero continúan funcionando después de eso. Predeterminado: false.
  • Devuelve: <cualquier> el resultado de la última instrucción ejecutada en el script.

Ejecuta el código compilado contenido por el objeto vm.Script dentro del contextifiedObject dado y devuelve el resultado. La ejecución del código no tiene acceso al ámbito local.

El siguiente ejemplo compila un código que incrementa una variable global, establece el valor de otra variable global y luego ejecuta el código varias veces. Las variables globales están contenidas en el objeto 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)
// Imprime: { animal: 'cat', count: 12, name: 'kitty' }

El uso de las opciones timeout o breakOnSigint dará como resultado el inicio de nuevos bucles de eventos e hilos correspondientes, lo que tiene una sobrecarga de rendimiento no nula.

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

[Historial]

VersiónCambios
v22.8.0, v20.18.0El argumento contextObject ahora acepta vm.constants.DONT_CONTEXTIFY.
v14.6.0La opción microtaskMode ahora es compatible.
v10.0.0La opción contextCodeGeneration ahora es compatible.
v6.3.0La opción breakOnSigint ahora es compatible.
v0.3.1Añadido en: v0.3.1
  • contextObject <Object> | <vm.constants.DONT_CONTEXTIFY> | <undefined> Ya sea vm.constants.DONT_CONTEXTIFY o un objeto que será contextificado. Si es undefined, se creará un objeto contextificado vacío para compatibilidad con versiones anteriores.

  • options <Object>

    • displayErrors <boolean> Cuando es true, si ocurre un Error mientras se compila el code, la línea de código que causa el error se adjunta al seguimiento de la pila. Predeterminado: true.

    • timeout <integer> Especifica el número de milisegundos para ejecutar code antes de terminar la ejecución. Si la ejecución termina, se lanzará un Error. Este valor debe ser un entero estrictamente positivo.

    • breakOnSigint <boolean> Si es true, recibir SIGINT (+) terminará la ejecución y lanzará un Error. Los controladores existentes para el evento que se han adjuntado a través de process.on('SIGINT') se deshabilitan durante la ejecución del script, pero continúan funcionando después de eso. Predeterminado: false.

    • contextName <string> Nombre legible por humanos del contexto recién creado. Predeterminado: 'VM Context i', donde i es un índice numérico ascendente del contexto creado.

    • contextOrigin <string> Origen correspondiente al contexto recién creado para fines de visualización. El origen debe tener el formato de una URL, pero solo con el esquema, el host y el puerto (si es necesario), como el valor de la propiedad url.origin de un objeto URL. En particular, esta cadena debe omitir la barra diagonal final, ya que eso denota una ruta. Predeterminado: ''.

    • contextCodeGeneration <Object>

    • strings <boolean> Si se establece en falso, cualquier llamada a eval o constructores de funciones (Function, GeneratorFunction, etc.) lanzará un EvalError. Predeterminado: true.

    • wasm <boolean> Si se establece en falso, cualquier intento de compilar un módulo WebAssembly lanzará un WebAssembly.CompileError. Predeterminado: true.

    • microtaskMode <string> Si se establece en afterEvaluate, las microtareas (tareas programadas a través de Promises y async functions) se ejecutarán inmediatamente después de que se haya ejecutado el script. Se incluyen en los ámbitos de timeout y breakOnSigint en ese caso.

  • Devuelve: <any> el resultado de la última declaración ejecutada en el script.

Este método es un atajo a script.runInContext(vm.createContext(options), options). Hace varias cosas a la vez:

El siguiente ejemplo compila código que establece una variable global, luego ejecuta el código varias veces en diferentes contextos. Las variables globales se establecen y se encuentran dentro de cada contexto individual.

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

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

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

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

// Esto lanzaría un error si el contexto se crea a partir de un objeto contextificado.
// vm.constants.DONT_CONTEXTIFY permite crear contextos con objetos globales ordinarios
// que se pueden congelar.
const freezeScript = new vm.Script('Object.freeze(globalThis); globalThis;')
const frozenContext = freezeScript.runInNewContext(vm.constants.DONT_CONTEXTIFY)

script.runInThisContext([options])

[Historial]

VersiónCambios
v6.3.0Ahora se admite la opción breakOnSigint.
v0.3.1Agregado en: v0.3.1
  • options <Objeto>

    • displayErrors <booleano> Cuando es true, si ocurre un Error al compilar el código, la línea de código que causa el error se adjunta al rastreo de la pila. Predeterminado: true.
    • timeout <entero> Especifica el número de milisegundos para ejecutar código antes de finalizar la ejecución. Si la ejecución finaliza, se lanzará un Error. Este valor debe ser un entero estrictamente positivo.
    • breakOnSigint <booleano> Si es true, recibir SIGINT (+) finalizará la ejecución y lanzará un Error. Los controladores existentes para el evento que se han adjuntado a través de process.on('SIGINT') se desactivan durante la ejecución del script, pero continúan funcionando después de eso. Predeterminado: false.
  • Retorna: <cualquier> el resultado de la última declaración ejecutada en el script.

Ejecuta el código compilado contenido en el vm.Script dentro del contexto del objeto global actual. La ejecución de código no tiene acceso al ámbito local, pero tiene acceso al objeto global actual.

El siguiente ejemplo compila un código que incrementa una variable global y luego ejecuta ese código varias veces:

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

Agregado en: v19.1.0, v18.13.0

Cuando el script se compila a partir de una fuente que contiene un comentario mágico de mapa de origen, esta propiedad se establecerá en la URL del mapa de origen.

js
import vm from 'node:vm'

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

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

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

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

Class: vm.Module

Agregado en: v13.0.0, v12.16.0

[Estable: 1 - Experimental]

Estable: 1 Estabilidad: 1 - Experimental

Esta característica solo está disponible con el indicador de comando --experimental-vm-modules habilitado.

La clase vm.Module proporciona una interfaz de bajo nivel para usar módulos ECMAScript en contextos de VM. Es la contraparte de la clase vm.Script que refleja fielmente los Registros de Módulos tal como se definen en la especificación ECMAScript.

Sin embargo, a diferencia de vm.Script, cada objeto vm.Module está vinculado a un contexto desde su creación. Las operaciones en objetos vm.Module son intrínsecamente asíncronas, en contraste con la naturaleza síncrona de los objetos vm.Script. El uso de funciones 'async' puede ayudar a manipular objetos vm.Module.

Usar un objeto vm.Module requiere tres pasos distintos: creación/análisis, vinculación y evaluación. Estos tres pasos se ilustran en el siguiente ejemplo.

Esta implementación se encuentra en un nivel inferior al cargador de módulos ECMAScript. Tampoco hay forma de interactuar con el Cargador todavía, aunque se planea soporte.

js
import vm from 'node:vm'

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

// Paso 1
//
// Crea un módulo construyendo un nuevo objeto `vm.SourceTextModule`. Esto
// analiza el texto fuente proporcionado, lanzando un `SyntaxError` si algo sale
// mal. Por defecto, un módulo se crea en el contexto superior. Pero aquí,
// especificamos `contextifiedObject` como el contexto al que pertenece este módulo.
//
// Aquí, intentamos obtener la exportación por defecto del módulo "foo" y
// ponerlo en el enlace local "secret".

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

// Paso 2
//
// "Vincula" las dependencias importadas de este módulo a él.
//
// El callback de vinculación proporcionado (el "vinculador") acepta dos argumentos:
// el módulo padre (`bar` en este caso) y la cadena que es el especificador de
// el módulo importado. Se espera que el callback devuelva un Módulo que
// corresponda al especificador proporcionado, con ciertos requisitos documentados
// en `module.link()`.
//
// Si la vinculación no ha comenzado para el módulo devuelto, el mismo callback del vinculador
// se llamará en el módulo devuelto.
//
// Incluso los módulos de nivel superior sin dependencias deben vincularse explícitamente. El
// callback proporcionado nunca se llamaría, sin embargo.
//
// El método link() devuelve una Promesa que se resolverá cuando todas las
// Promesas devueltas por el vinculador se resuelvan.
//
// Nota: Este es un ejemplo artificial en el que la función del vinculador crea un nuevo
// módulo "foo" cada vez que se llama. En un sistema de módulos completo, un
// caché probablemente se utilizaría para evitar módulos duplicados.

async function linker(specifier, referencingModule) {
  if (specifier === 'foo') {
    return new vm.SourceTextModule(
      `
      // La variable "secret" se refiere a la variable global que agregamos a
      // "contextifiedObject" al crear el contexto.
      export default secret;
    `,
      { context: referencingModule.context }
    )

    // Usar `contextifiedObject` en lugar de `referencingModule.context`
    // aquí también funcionaría.
  }
  throw new Error(`No se puede resolver la dependencia: ${specifier}`)
}
await bar.link(linker)

// Paso 3
//
// Evalúa el módulo. El método evaluate() devuelve una promesa que se
// resolverá después de que el módulo haya terminado de evaluarse.

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

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

;(async () => {
  // Paso 1
  //
  // Crea un módulo construyendo un nuevo objeto `vm.SourceTextModule`. Esto
  // analiza el texto fuente proporcionado, lanzando un `SyntaxError` si algo sale
  // mal. Por defecto, un módulo se crea en el contexto superior. Pero aquí,
  // especificamos `contextifiedObject` como el contexto al que pertenece este módulo.
  //
  // Aquí, intentamos obtener la exportación por defecto del módulo "foo" y
  // ponerlo en el enlace local "secret".

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

  // Paso 2
  //
  // "Vincula" las dependencias importadas de este módulo a él.
  //
  // El callback de vinculación proporcionado (el "vinculador") acepta dos argumentos:
  // el módulo padre (`bar` en este caso) y la cadena que es el especificador de
  // el módulo importado. Se espera que el callback devuelva un Módulo que
  // corresponda al especificador proporcionado, con ciertos requisitos documentados
  // en `module.link()`.
  //
  // Si la vinculación no ha comenzado para el módulo devuelto, el mismo callback del vinculador
  // se llamará en el módulo devuelto.
  //
  // Incluso los módulos de nivel superior sin dependencias deben vincularse explícitamente. El
  // callback proporcionado nunca se llamaría, sin embargo.
  //
  // El método link() devuelve una Promesa que se resolverá cuando todas las
  // Promesas devueltas por el vinculador se resuelvan.
  //
  // Nota: Este es un ejemplo artificial en el que la función del vinculador crea un nuevo
  // módulo "foo" cada vez que se llama. En un sistema de módulos completo, un
  // caché probablemente se utilizaría para evitar módulos duplicados.

  async function linker(specifier, referencingModule) {
    if (specifier === 'foo') {
      return new vm.SourceTextModule(
        `
        // La variable "secret" se refiere a la variable global que agregamos a
        // "contextifiedObject" al crear el contexto.
        export default secret;
      `,
        { context: referencingModule.context }
      )

      // Usar `contextifiedObject` en lugar de `referencingModule.context`
      // aquí también funcionaría.
    }
    throw new Error(`No se puede resolver la dependencia: ${specifier}`)
  }
  await bar.link(linker)

  // Paso 3
  //
  // Evalúa el módulo. El método evaluate() devuelve una promesa que se
  // resolverá después de que el módulo haya terminado de evaluarse.

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

module.dependencySpecifiers

Los especificadores de todas las dependencias de este módulo. El array devuelto está congelado para impedir cualquier cambio en él.

Corresponde al campo [[RequestedModules]] de los Registros de Módulo Cíclico en la especificación ECMAScript.

module.error

Si el module.status es 'errored', esta propiedad contiene la excepción lanzada por el módulo durante la evaluación. Si el estado es cualquier otro, acceder a esta propiedad resultará en una excepción lanzada.

El valor undefined no puede usarse para casos en los que no haya una excepción lanzada debido a una posible ambigüedad con throw undefined;.

Corresponde al campo [[EvaluationError]] de los Registros de Módulo Cíclico en la especificación ECMAScript.

module.evaluate([options])

  • options <Object>

    • timeout <integer> Especifica el número de milisegundos que se deben evaluar antes de terminar la ejecución. Si se interrumpe la ejecución, se lanzará un Error. Este valor debe ser un entero estrictamente positivo.
    • breakOnSigint <boolean> Si es true, recibir SIGINT (+) terminará la ejecución y lanzará un Error. Los controladores existentes para el evento que se hayan adjuntado a través de process.on('SIGINT') se desactivan durante la ejecución del script, pero continúan funcionando después de eso. Predeterminado: false.
  • Devuelve: <Promise> Se cumple con undefined tras el éxito.

Evalúa el módulo.

Esto debe ser llamado después de que el módulo haya sido vinculado; de lo contrario, se rechazará. También se puede llamar cuando el módulo ya ha sido evaluado, en cuyo caso, o bien no hará nada si la evaluación inicial terminó con éxito (module.status es 'evaluated') o volverá a lanzar la excepción que resultó de la evaluación inicial (module.status es 'errored').

Este método no se puede llamar mientras se está evaluando el módulo (module.status es 'evaluating').

Corresponde al campo del método concreto Evaluate() de los Registros de Módulos Cíclicos en la especificación de ECMAScript.

module.identifier

El identificador del módulo actual, tal como se establece en el constructor.

module.link(linker)

[Historial]

VersiónCambios
v21.1.0, v20.10.0, v18.19.0La opción extra.assert se renombra a extra.attributes. El nombre anterior todavía se proporciona por compatibilidad con versiones anteriores.

Vincula las dependencias del módulo. Este método debe llamarse antes de la evaluación, y solo se puede llamar una vez por módulo.

Se espera que la función devuelva un objeto Module o una Promise que eventualmente se resuelva a un objeto Module. El Module devuelto debe satisfacer los siguientes dos invariantes:

  • Debe pertenecer al mismo contexto que el Module padre.
  • Su status no debe ser 'errored'.

Si el status del Module devuelto es 'unlinked', este método se llamará recursivamente en el Module devuelto con la misma función linker proporcionada.

link() devuelve una Promise que se resolverá cuando todas las instancias de enlace se resuelvan en un Module válido, o se rechazará si la función de enlace lanza una excepción o devuelve un Module no válido.

La función de enlace corresponde aproximadamente a la operación abstracta HostResolveImportedModule definida por la implementación en la especificación ECMAScript, con algunas diferencias clave:

La implementación real de HostResolveImportedModule utilizada durante el enlace de módulos es aquella que devuelve los módulos enlazados durante el enlace. Dado que en ese momento todos los módulos ya se habrían enlazado completamente, la implementación de HostResolveImportedModule es totalmente síncrona según la especificación.

Corresponde al campo Link() concrete method de los Cyclic Module Record en la especificación ECMAScript.

module.namespace

El objeto de espacio de nombres del módulo. Esto solo está disponible después de que se haya completado la vinculación (module.link()).

Corresponde a la operación abstracta GetModuleNamespace en la especificación ECMAScript.

module.status

El estado actual del módulo. Será uno de los siguientes:

  • 'unlinked': Aún no se ha llamado a module.link().
  • 'linking': Se ha llamado a module.link(), pero aún no se han resuelto todas las promesas devueltas por la función de enlazado.
  • 'linked': El módulo se ha enlazado correctamente y todas sus dependencias están enlazadas, pero aún no se ha llamado a module.evaluate().
  • 'evaluating': El módulo se está evaluando a través de un module.evaluate() en sí mismo o en un módulo padre.
  • 'evaluated': El módulo se ha evaluado correctamente.
  • 'errored': El módulo se ha evaluado, pero se ha lanzado una excepción.

Aparte de 'errored', esta cadena de estado corresponde al campo [[Status]] del Registro de Módulo Cíclico de la especificación. 'errored' corresponde a 'evaluated' en la especificación, pero con [[EvaluationError]] establecido en un valor que no es undefined.

Clase: vm.SourceTextModule

Añadido en: v9.6.0

[Estable: 1 - Experimental]

Estable: 1 Estabilidad: 1 - Experimental

Esta función solo está disponible con la flag de comando --experimental-vm-modules habilitada.

La clase vm.SourceTextModule proporciona el Registro de Módulo de Texto Fuente tal como se define en la especificación ECMAScript.

new vm.SourceTextModule(code[, options])

[Historial]

VersiónCambios
v17.0.0, v16.12.0Se agregó soporte para atributos de importación al parámetro importModuleDynamically.
  • code <string> Código de Módulo de JavaScript para analizar

  • options

    • identifier <string> Cadena utilizada en los seguimientos de pila. Valor predeterminado: 'vm:module(i)' donde i es un índice ascendente específico del contexto.

    • cachedData <Buffer> | <TypedArray> | <DataView> Proporciona un Buffer o TypedArray opcional, o DataView con los datos de la caché de código de V8 para la fuente proporcionada. El code debe ser el mismo que el módulo del que se creó este cachedData.

    • context <Object> El objeto contextualizado tal como lo devuelve el método vm.createContext(), para compilar y evaluar este Module en él. Si no se especifica ningún contexto, el módulo se evalúa en el contexto de ejecución actual.

    • lineOffset <integer> Especifica el desplazamiento del número de línea que se muestra en los seguimientos de pila producidos por este Module. Valor predeterminado: 0.

    • columnOffset <integer> Especifica el desplazamiento del número de columna de la primera línea que se muestra en los seguimientos de pila producidos por este Module. Valor predeterminado: 0.

    • initializeImportMeta <Function> Se llama durante la evaluación de este Module para inicializar el import.meta.

    • meta <import.meta>

    • module <vm.SourceTextModule>

    • importModuleDynamically <Function> Se utiliza para especificar cómo se deben cargar los módulos durante la evaluación de este módulo cuando se llama a import(). Esta opción es parte de la API de módulos experimentales. No recomendamos usarla en un entorno de producción. Para obtener información detallada, consulte Soporte de import() dinámico en las API de compilación.

Crea una nueva instancia de SourceTextModule.

Las propiedades asignadas al objeto import.meta que son objetos pueden permitir que el módulo acceda a información fuera del context especificado. Utilice vm.runInContext() para crear objetos en un contexto específico.

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) {
    // Note: this object is created in the top context. As such,
    // Object.getPrototypeOf(import.meta.prop) points to the
    // Object.prototype in the top context rather than that in
    // the contextified object.
    meta.prop = {}
  },
})
// Since module has no dependencies, the linker function will never be called.
await module.link(() => {})
await module.evaluate()

// Now, Object.prototype.secret will be equal to 42.
//
// To fix this problem, replace
//     meta.prop = {};
// above with
//     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) {
      // Note: this object is created in the top context. As such,
      // Object.getPrototypeOf(import.meta.prop) points to the
      // Object.prototype in the top context rather than that in
      // the contextified object.
      meta.prop = {}
    },
  })
  // Since module has no dependencies, the linker function will never be called.
  await module.link(() => {})
  await module.evaluate()
  // Now, Object.prototype.secret will be equal to 42.
  //
  // To fix this problem, replace
  //     meta.prop = {};
  // above with
  //     meta.prop = vm.runInContext('{}', contextifiedObject);
})()

sourceTextModule.createCachedData()

Añadido en: v13.7.0, v12.17.0

Crea una caché de código que puede usarse con la opción cachedData del constructor SourceTextModule. Devuelve un Buffer. Este método puede ser llamado cualquier número de veces antes de que el módulo haya sido evaluado.

La caché de código del SourceTextModule no contiene ningún estado observable de JavaScript. La caché de código es segura para ser guardada junto con el código fuente del script y utilizada para construir nuevas instancias de SourceTextModule varias veces.

Las funciones en el código fuente del SourceTextModule pueden marcarse como compiladas de forma diferida y no se compilan en la construcción del SourceTextModule. Estas funciones se compilarán cuando se invoquen por primera vez. La caché de código serializa los metadatos que V8 conoce actualmente sobre el SourceTextModule que puede usar para acelerar futuras compilaciones.

js
// Crea un módulo inicial
const module = new vm.SourceTextModule('const a = 1;')

// Crea datos almacenados en caché a partir de este módulo
const cachedData = module.createCachedData()

// Crea un nuevo módulo usando los datos almacenados en caché. El código debe ser el mismo.
const module2 = new vm.SourceTextModule('const a = 1;', { cachedData })

Clase: vm.SyntheticModule

Agregado en: v13.0.0, v12.16.0

[Estable: 1 - Experimental]

Estable: 1 Estabilidad: 1 - Experimental

Esta función solo está disponible con el indicador de comando --experimental-vm-modules habilitado.

La clase vm.SyntheticModule proporciona el Registro de módulo sintético como se define en la especificación WebIDL. El propósito de los módulos sintéticos es proporcionar una interfaz genérica para exponer fuentes que no son de JavaScript a los gráficos de módulos 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)
})

// Usar `module` en la vinculación...

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

Agregado en: v13.0.0, v12.16.0

  • exportNames <string[]> Arreglo de nombres que se exportarán desde el módulo.
  • evaluateCallback <Function> Se llama cuando se evalúa el módulo.
  • options
    • identifier <string> Cadena utilizada en los rastreos de pila. Predeterminado: 'vm:module(i)' donde i es un índice ascendente específico del contexto.
    • context <Object> El objeto contextificado como lo devuelve el método vm.createContext(), para compilar y evaluar este Module en él.

Crea una nueva instancia SyntheticModule.

Los objetos asignados a las exportaciones de esta instancia pueden permitir que los importadores del módulo accedan a información fuera del context especificado. Utilice vm.runInContext() para crear objetos en un contexto específico.

syntheticModule.setExport(name, value)

Agregado en: v13.0.0, v12.16.0

  • name <string> Nombre de la exportación que se va a establecer.
  • value <any> El valor al que se va a establecer la exportación.

Este método se utiliza después de que el módulo está vinculado para establecer los valores de las exportaciones. Si se llama antes de que el módulo esté vinculado, se lanzará un error ERR_VM_MODULE_STATUS.

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

[Historial]

VersiónCambios
v21.7.0, v20.12.0Se agregó soporte para vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.
v19.6.0, v18.15.0El valor de retorno ahora incluye cachedDataRejected con la misma semántica que la versión vm.Script si se pasó la opción cachedData.
v17.0.0, v16.12.0Se agregó soporte para atributos de importación al parámetro importModuleDynamically.
v15.9.0Se agregó nuevamente la opción importModuleDynamically.
v14.3.0Eliminación de importModuleDynamically debido a problemas de compatibilidad.
v14.1.0, v13.14.0Ahora se admite la opción importModuleDynamically.
v10.10.0Agregado en: v10.10.0
  • code <string> El cuerpo de la función a compilar.

  • params <string[]> Un arreglo de cadenas que contiene todos los parámetros para la función.

  • options <Object>

    • filename <string> Especifica el nombre de archivo utilizado en los rastreos de pila producidos por este script. Predeterminado: ''.
    • lineOffset <number> Especifica el desplazamiento del número de línea que se muestra en los rastreos de pila producidos por este script. Predeterminado: 0.
    • columnOffset <number> Especifica el desplazamiento del número de columna de la primera línea que se muestra en los rastreos de pila producidos por este script. Predeterminado: 0.
    • cachedData <Buffer> | <TypedArray> | <DataView> Proporciona un Buffer o TypedArray opcional, o DataView con los datos de la caché de código de V8 para la fuente proporcionada. Esto debe ser producido por una llamada anterior a vm.compileFunction() con el mismo code y params.
    • produceCachedData <boolean> Especifica si se deben producir nuevos datos de caché. Predeterminado: false.
    • parsingContext <Object> El objeto contextificado en el que se debe compilar dicha función.
    • contextExtensions <Object[]> Un arreglo que contiene una colección de extensiones de contexto (objetos que envuelven el alcance actual) que se aplicarán durante la compilación. Predeterminado: [].
  • importModuleDynamically <Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Se utiliza para especificar cómo se deben cargar los módulos durante la evaluación de esta función cuando se llama a import(). Esta opción es parte de la API de módulos experimentales. No recomendamos usarla en un entorno de producción. Para obtener información detallada, consulta Soporte de import() dinámico en las API de compilación.

  • Devuelve: <Function>

Compila el código dado en el contexto proporcionado (si no se proporciona ningún contexto, se utiliza el contexto actual) y lo devuelve envuelto dentro de una función con los params dados.

vm.constants

Añadido en: v21.7.0, v20.12.0

Devuelve un objeto que contiene constantes de uso común para las operaciones de VM.

vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER

Añadido en: v21.7.0, v20.12.0

[Estable: 1 - Experimental]

Estable: 1 Estabilidad: 1.1 - Desarrollo activo

Una constante que se puede utilizar como opción importModuleDynamically para vm.Script y vm.compileFunction() para que Node.js utilice el cargador ESM predeterminado del contexto principal para cargar el módulo solicitado.

Para obtener información detallada, consulta Soporte de import() dinámico en las API de compilación.

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

[Historial]

VersiónCambios
v22.8.0, v20.18.0El argumento contextObject ahora acepta vm.constants.DONT_CONTEXTIFY.
v21.7.0, v20.12.0Se agregó soporte para vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.
v21.2.0, v20.11.0Ahora se admite la opción importModuleDynamically.
v14.6.0Ahora se admite la opción microtaskMode.
v10.0.0El primer argumento ya no puede ser una función.
v10.0.0Ahora se admite la opción codeGeneration.
v0.3.1Añadido en: v0.3.1
  • contextObject <Object> | <vm.constants.DONT_CONTEXTIFY> | <undefined> Ya sea vm.constants.DONT_CONTEXTIFY o un objeto que será contextificado. Si es undefined, se creará un objeto contextificado vacío para la compatibilidad con versiones anteriores.

  • options <Object>

    • name <string> Nombre legible por humanos del contexto recién creado. Predeterminado: 'VM Context i', donde i es un índice numérico ascendente del contexto creado.

    • origin <string> Origen correspondiente al contexto recién creado para fines de visualización. El origen debe tener el formato de una URL, pero solo con el esquema, el host y el puerto (si es necesario), como el valor de la propiedad url.origin de un objeto URL. En particular, esta cadena debe omitir la barra diagonal final, ya que denota una ruta. Predeterminado: ''.

    • codeGeneration <Object>

    • strings <boolean> Si se establece en falso, cualquier llamada a eval o constructores de funciones (Function, GeneratorFunction, etc.) generará un EvalError. Predeterminado: true.

    • wasm <boolean> Si se establece en falso, cualquier intento de compilar un módulo de WebAssembly generará un WebAssembly.CompileError. Predeterminado: true.

    • microtaskMode <string> Si se establece en afterEvaluate, las microtareas (tareas programadas a través de Promises y async functions) se ejecutarán inmediatamente después de que un script se haya ejecutado a través de script.runInContext(). Se incluyen en los ámbitos timeout y breakOnSigint en ese caso.

    • importModuleDynamically <Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Se utiliza para especificar cómo se deben cargar los módulos cuando se llama a import() en este contexto sin un script o módulo de referencia. Esta opción es parte de la API de módulos experimental. No recomendamos usarla en un entorno de producción. Para obtener información detallada, consulta Soporte de import() dinámico en las API de compilación.

  • Devuelve: <Object> objeto contextificado.

Si el contextObject dado es un objeto, el método vm.createContext() preparará ese objeto y devolverá una referencia al mismo para que pueda utilizarse en llamadas a vm.runInContext() o script.runInContext(). Dentro de dichos scripts, el objeto global se envolverá con el contextObject, conservando todas sus propiedades existentes, pero también teniendo los objetos y funciones integrados que cualquier objeto global estándar tiene. Fuera de los scripts ejecutados por el módulo vm, las variables globales permanecerán sin cambios.

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

global.globalVar = 3

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

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

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

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

Si se omite contextObject (o se pasa explícitamente como undefined), se devolverá un objeto contextificado nuevo y vacío.

Cuando el objeto global en el contexto recién creado está contextificado, tiene algunas peculiaridades en comparación con los objetos globales ordinarios. Por ejemplo, no se puede congelar. Para crear un contexto sin las peculiaridades de contextificación, pasa vm.constants.DONT_CONTEXTIFY como el argumento contextObject. Consulta la documentación de vm.constants.DONT_CONTEXTIFY para obtener más detalles.

El método vm.createContext() es útil principalmente para crear un único contexto que se puede utilizar para ejecutar varios scripts. Por ejemplo, si se emula un navegador web, el método se puede utilizar para crear un único contexto que represente el objeto global de una ventana y, a continuación, ejecutar todas las etiquetas \<script\> juntas dentro de ese contexto.

El name y origin proporcionados del contexto se hacen visibles a través de la API de Inspector.

vm.isContext(objeto)

Agregado en: v0.11.7

Devuelve true si el objeto dado ha sido contextificado utilizando vm.createContext(), o si es el objeto global de un contexto creado utilizando vm.constants.DONT_CONTEXTIFY.

vm.measureMemory([opciones])

Agregado en: v13.10.0

[Estable: 1 - Experimental]

Estable: 1 Estabilidad: 1 - Experimental

Mide la memoria conocida por V8 y utilizada por todos los contextos conocidos por el aislamiento V8 actual, o el contexto principal.

  • opciones <Objeto> Opcional.

    • mode <string> Ya sea 'summary' o 'detailed'. En modo resumen, solo se devolverá la memoria medida para el contexto principal. En modo detallado, se devolverá la memoria medida para todos los contextos conocidos por el aislamiento V8 actual. Predeterminado: 'summary'
    • execution <string> Ya sea 'default' o 'eager'. Con la ejecución predeterminada, la promesa no se resolverá hasta después de que comience la siguiente recolección de basura programada, lo que puede llevar un tiempo (o nunca si el programa sale antes de la siguiente GC). Con la ejecución ansiosa, la GC se iniciará de inmediato para medir la memoria. Predeterminado: 'default'
  • Devuelve: <Promesa> Si la memoria se mide con éxito, la promesa se resolverá con un objeto que contiene información sobre el uso de la memoria. De lo contrario, será rechazada con un error ERR_CONTEXT_NOT_INITIALIZED.

El formato del objeto con el que la Promesa devuelta puede resolverse es específico del motor V8 y puede cambiar de una versión de V8 a la siguiente.

El resultado devuelto es diferente de las estadísticas devueltas por v8.getHeapSpaceStatistics() en que vm.measureMemory() mide la memoria accesible por cada contexto específico de V8 en la instancia actual del motor V8, mientras que el resultado de v8.getHeapSpaceStatistics() mide la memoria ocupada por cada espacio de montón en la instancia V8 actual.

js
const vm = require('node:vm')
// Mide la memoria utilizada por el contexto principal.
vm.measureMemory({ mode: 'summary' })
  // Esto es lo mismo que vm.measureMemory()
  .then(result => {
    // El formato actual es:
    // {
    //   total: {
    //      jsMemoryEstimate: 2418479, jsMemoryRange: [ 2418479, 2745799 ]
    //    }
    // }
    console.log(result)
  })

const context = vm.createContext({ a: 1 })
vm.measureMemory({ mode: 'detailed', execution: 'eager' }).then(result => {
  // Haz referencia al contexto aquí para que no se recoja basura
  // hasta que la medición esté completa.
  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])

[Historial]

VersiónCambios
v21.7.0, v20.12.0Se agregó soporte para vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.
v17.0.0, v16.12.0Se agregó soporte para atributos de importación al parámetro importModuleDynamically.
v6.3.0La opción breakOnSigint ahora es compatible.
v0.3.1Agregado en: v0.3.1
  • code <string> El código JavaScript para compilar y ejecutar.
  • contextifiedObject <Object> El objeto contextificado que se utilizará como global cuando se compile y ejecute el code.
  • options <Object> | <string>
    • filename <string> Especifica el nombre de archivo utilizado en los rastreos de pila producidos por este script. Predeterminado: 'evalmachine.\<anonymous\>'.
    • lineOffset <number> Especifica el desplazamiento del número de línea que se muestra en los rastreos de pila producidos por este script. Predeterminado: 0.
    • columnOffset <number> Especifica el desplazamiento del número de columna de la primera línea que se muestra en los rastreos de pila producidos por este script. Predeterminado: 0.
    • displayErrors <boolean> Cuando es true, si ocurre un Error al compilar el code, la línea de código que causa el error se adjunta al rastreo de pila. Predeterminado: true.
    • timeout <integer> Especifica el número de milisegundos para ejecutar code antes de terminar la ejecución. Si se termina la ejecución, se lanzará un Error. Este valor debe ser un entero estrictamente positivo.
    • breakOnSigint <boolean> Si es true, recibir SIGINT (+) terminará la ejecución y lanzará un Error. Los controladores existentes para el evento que se han adjuntado a través de process.on('SIGINT') se deshabilitan durante la ejecución del script, pero continúan funcionando después de eso. Predeterminado: false.
    • cachedData <Buffer> | <TypedArray> | <DataView> Proporciona un Buffer opcional o TypedArray, o DataView con los datos de caché de código de V8 para la fuente proporcionada.
    • importModuleDynamically <Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Se utiliza para especificar cómo se deben cargar los módulos durante la evaluación de este script cuando se llama a import(). Esta opción es parte de la API de módulos experimentales. No recomendamos usarla en un entorno de producción. Para obtener información detallada, consulte Soporte de import() dinámico en las API de compilación.

El método vm.runInContext() compila code, lo ejecuta dentro del contexto de contextifiedObject y luego devuelve el resultado. El código en ejecución no tiene acceso al ámbito local. El objeto contextifiedObject debe haber sido previamente contextificado utilizando el método vm.createContext().

Si options es una cadena, especifica el nombre de archivo.

El siguiente ejemplo compila y ejecuta diferentes scripts utilizando un único objeto contextificado:

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)
// Imprime: { globalVar: 1024 }

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

[Historial]

VersiónCambios
v22.8.0, v20.18.0El argumento contextObject ahora acepta vm.constants.DONT_CONTEXTIFY.
v21.7.0, v20.12.0Se agregó soporte para vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.
v17.0.0, v16.12.0Se agregó soporte para atributos de importación al parámetro importModuleDynamically.
v14.6.0Ahora se admite la opción microtaskMode.
v10.0.0Ahora se admite la opción contextCodeGeneration.
v6.3.0Ahora se admite la opción breakOnSigint.
v0.3.1Agregado en: v0.3.1
  • code <string> El código JavaScript para compilar y ejecutar.

  • contextObject <Object> | <vm.constants.DONT_CONTEXTIFY> | <undefined> Ya sea vm.constants.DONT_CONTEXTIFY o un objeto que será contextificado. Si es undefined, se creará un objeto contextificado vacío por compatibilidad con versiones anteriores.

  • options <Object> | <string>

    • filename <string> Especifica el nombre de archivo utilizado en los seguimientos de pila producidos por este script. Predeterminado: 'evalmachine.\<anónimo\>'.
    • lineOffset <number> Especifica el desplazamiento del número de línea que se muestra en los seguimientos de pila producidos por este script. Predeterminado: 0.
    • columnOffset <number> Especifica el desplazamiento del número de columna de la primera línea que se muestra en los seguimientos de pila producidos por este script. Predeterminado: 0.
    • displayErrors <boolean> Cuando es true, si ocurre un Error al compilar el code, la línea de código que causa el error se adjunta al seguimiento de pila. Predeterminado: true.
    • timeout <integer> Especifica la cantidad de milisegundos para ejecutar code antes de finalizar la ejecución. Si se finaliza la ejecución, se lanzará un Error. Este valor debe ser un entero estrictamente positivo.
    • breakOnSigint <boolean> Si es true, recibir SIGINT (+) finalizará la ejecución y lanzará un Error. Los controladores existentes para el evento que se han adjuntado a través de process.on('SIGINT') se deshabilitan durante la ejecución del script, pero continúan funcionando después de eso. Predeterminado: false.
    • contextName <string> Nombre legible por humanos del contexto recién creado. Predeterminado: 'VM Context i', donde i es un índice numérico ascendente del contexto creado.
    • contextOrigin <string> Origen correspondiente al contexto recién creado para fines de visualización. El origen debe tener el formato de una URL, pero solo con el esquema, el host y el puerto (si es necesario), como el valor de la propiedad url.origin de un objeto URL. En particular, esta cadena debe omitir la barra diagonal final, ya que denota una ruta. Predeterminado: ''.
    • contextCodeGeneration <Object>
      • strings <boolean> Si se establece en falso, cualquier llamada a eval o constructores de funciones (Function, GeneratorFunction, etc.) lanzará un EvalError. Predeterminado: true.
      • wasm <boolean> Si se establece en falso, cualquier intento de compilar un módulo WebAssembly lanzará un WebAssembly.CompileError. Predeterminado: true.
    • cachedData <Buffer> | <TypedArray> | <DataView> Proporciona un Buffer opcional o TypedArray, o DataView con los datos de caché de código de V8 para la fuente suministrada.
    • importModuleDynamically <Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Se utiliza para especificar cómo se deben cargar los módulos durante la evaluación de este script cuando se llama a import(). Esta opción es parte de la API de módulos experimentales. No recomendamos usarla en un entorno de producción. Para obtener información detallada, consulte Soporte de import() dinámico en las API de compilación.
    • microtaskMode <string> Si se establece en afterEvaluate, las microtareas (tareas programadas a través de Promises y async functions) se ejecutarán inmediatamente después de que se haya ejecutado el script. Se incluyen en los alcances de timeout y breakOnSigint en ese caso.
  • Devuelve: <any> el resultado de la última instrucción ejecutada en el script.

Este método es un atajo para (new vm.Script(code, options)).runInContext(vm.createContext(options), options). Si options es una cadena, entonces especifica el nombre de archivo.

Hace varias cosas a la vez:

El siguiente ejemplo compila y ejecuta código que incrementa una variable global y establece una nueva. Estas variables globales están contenidas en el contextObject.

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

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

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

// Esto se lanzaría si el contexto se crea a partir de un objeto contextificado.
// vm.constants.DONT_CONTEXTIFY permite crear contextos con objetos globales ordinarios que
// pueden ser congelados.
const frozenContext = vm.runInNewContext('Object.freeze(globalThis); globalThis;', vm.constants.DONT_CONTEXTIFY)

vm.runInThisContext(code[, options])

[Historial]

VersiónCambios
v21.7.0, v20.12.0Se añadió soporte para vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.
v17.0.0, v16.12.0Se añadió soporte para atributos de importación al parámetro importModuleDynamically.
v6.3.0La opción breakOnSigint ahora es compatible.
v0.3.1Añadido en: v0.3.1
  • code <string> El código JavaScript para compilar y ejecutar.

  • options <Object> | <string>

    • filename <string> Especifica el nombre de archivo utilizado en los seguimientos de pila producidos por este script. Predeterminado: 'evalmachine.\<anónimo\>'.
    • lineOffset <number> Especifica el desplazamiento del número de línea que se muestra en los seguimientos de pila producidos por este script. Predeterminado: 0.
    • columnOffset <number> Especifica el desplazamiento del número de columna de la primera línea que se muestra en los seguimientos de pila producidos por este script. Predeterminado: 0.
    • displayErrors <boolean> Cuando es true, si ocurre un Error al compilar el code, la línea de código que causa el error se adjunta al seguimiento de la pila. Predeterminado: true.
    • timeout <integer> Especifica el número de milisegundos para ejecutar code antes de terminar la ejecución. Si se termina la ejecución, se lanzará un Error. Este valor debe ser un entero estrictamente positivo.
    • breakOnSigint <boolean> Si es true, recibir SIGINT (+) terminará la ejecución y lanzará un Error. Los controladores existentes para el evento que se han adjuntado a través de process.on('SIGINT') se deshabilitan durante la ejecución del script, pero continúan funcionando después de eso. Predeterminado: false.
    • cachedData <Buffer> | <TypedArray> | <DataView> Proporciona un Buffer o TypedArray opcional, o DataView con los datos de la caché de código de V8 para la fuente suministrada.
    • importModuleDynamically <Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Se utiliza para especificar cómo se deben cargar los módulos durante la evaluación de este script cuando se llama a import(). Esta opción es parte de la API de módulos experimental. No recomendamos usarla en un entorno de producción. Para obtener información detallada, consulte Soporte de import() dinámico en API de compilación.
  • Devuelve: <any> el resultado de la última instrucción ejecutada en el script.

vm.runInThisContext() compila code, lo ejecuta dentro del contexto del global actual y devuelve el resultado. El código de ejecución no tiene acceso al ámbito local, pero sí tiene acceso al objeto global actual.

Si options es una cadena, entonces especifica el nombre del archivo.

El siguiente ejemplo ilustra el uso tanto de vm.runInThisContext() como de la función JavaScript eval() para ejecutar el mismo código:

js
const vm = require('node:vm')
let localVar = 'valor inicial'

const vmResult = vm.runInThisContext('localVar = "vm";')
console.log(`vmResult: '${vmResult}', localVar: '${localVar}'`)
// Imprime: vmResult: 'vm', localVar: 'valor inicial'

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

Debido a que vm.runInThisContext() no tiene acceso al ámbito local, localVar no cambia. En contraste, eval() tiene acceso al ámbito local, por lo que el valor localVar se cambia. De esta manera, vm.runInThisContext() se parece mucho a una llamada eval() indirecta, p. ej. (0,eval)('code').

Ejemplo: Ejecutar un servidor HTTP dentro de una VM

Cuando se utiliza script.runInThisContext() o vm.runInThisContext(), el código se ejecuta dentro del contexto global actual de V8. El código pasado a este contexto de VM tendrá su propio ámbito aislado.

Para ejecutar un servidor web simple utilizando el módulo node:http, el código pasado al contexto debe llamar a require('node:http') por sí solo, o tener una referencia al módulo node:http que se le haya pasado. Por ejemplo:

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('Hola Mundo\\n');
  }).listen(8124);

  console.log('Servidor ejecutándose en http://127.0.0.1:8124/');
})`

vm.runInThisContext(code)(require)

El require() en el caso anterior comparte el estado con el contexto del que se pasa. Esto puede introducir riesgos cuando se ejecuta código no confiable, por ejemplo, alterando objetos en el contexto de formas no deseadas.

¿Qué significa "contextualizar" un objeto?

Todo el JavaScript ejecutado dentro de Node.js se ejecuta dentro del alcance de un "contexto". Según la Guía del Integrador de V8:

Cuando se llama al método vm.createContext() con un objeto, el argumento contextObject se utilizará para envolver el objeto global de una nueva instancia de un Contexto V8 (si contextObject es undefined, se creará un nuevo objeto a partir del contexto actual antes de ser contextualizado). Este Contexto V8 proporciona al code ejecutado utilizando los métodos del módulo node:vm un entorno global aislado dentro del cual puede operar. El proceso de crear el Contexto V8 y asociarlo con el contextObject en el contexto exterior es a lo que este documento se refiere como "contextualizar" el objeto.

La contextualización introduciría algunas peculiaridades en el valor globalThis en el contexto. Por ejemplo, no se puede congelar y no es una referencia igual al contextObject en el contexto exterior.

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

// Una opción `contextObject` indefinida hace que el objeto global se contextualice.
const context = vm.createContext()
console.log(vm.runInContext('globalThis', context) === context) // false
// Un objeto global contextualizado no se puede congelar.
try {
  vm.runInContext('Object.freeze(globalThis);', context)
} catch (e) {
  console.log(e) // TypeError: No se puede congelar
}
console.log(vm.runInContext('globalThis.foo = 1; foo;', context)) // 1

Para crear un contexto con un objeto global ordinario y obtener acceso a un proxy global en el contexto exterior con menos peculiaridades, especifique vm.constants.DONT_CONTEXTIFY como argumento contextObject.

vm.constants.DONT_CONTEXTIFY

Esta constante, cuando se usa como el argumento contextObject en las API de vm, indica a Node.js que cree un contexto sin envolver su objeto global con otro objeto de una manera específica de Node.js. Como resultado, el valor globalThis dentro del nuevo contexto se comportaría de manera más similar a uno ordinario.

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

// Usa vm.constants.DONT_CONTEXTIFY para congelar el objeto 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
}

Cuando vm.constants.DONT_CONTEXTIFY se usa como el argumento contextObject para vm.createContext(), el objeto devuelto es un objeto similar a un proxy para el objeto global en el contexto recién creado con menos peculiaridades específicas de Node.js. Es una referencia igual al valor globalThis en el nuevo contexto, se puede modificar desde fuera del contexto y se puede utilizar para acceder directamente a los elementos integrados en el nuevo contexto.

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

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

// El objeto devuelto es una referencia igual a globalThis en el nuevo contexto.
console.log(vm.runInContext('globalThis', context) === context) // true

// Se puede usar para acceder directamente a los globales en el nuevo contexto.
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

// Se puede congelar y afecta al contexto interno.
Object.freeze(context)
try {
  vm.runInContext('baz = 1; baz;', context)
} catch (e) {
  console.log(e) // Uncaught ReferenceError: baz is not defined
}

Interacciones de tiempo de espera con tareas asíncronas y Promesas

Las Promises y las async functions pueden programar tareas que el motor de JavaScript ejecuta de forma asíncrona. Por defecto, estas tareas se ejecutan después de que todas las funciones de JavaScript de la pila actual hayan terminado de ejecutarse. Esto permite escapar de la funcionalidad de las opciones timeout y breakOnSigint.

Por ejemplo, el siguiente código ejecutado por vm.runInNewContext() con un tiempo de espera de 5 milisegundos programa un bucle infinito para que se ejecute después de que una promesa se resuelva. El bucle programado nunca es interrumpido por el tiempo de espera:

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

function loop() {
  console.log('entrando al bucle')
  while (1) console.log(Date.now())
}

vm.runInNewContext('Promise.resolve().then(() => loop());', { loop, console }, { timeout: 5 })
// Esto se imprime *antes* de 'entrando al bucle' (!)
console.log('ejecución terminada')

Esto se puede solucionar pasando microtaskMode: 'afterEvaluate' al código que crea el 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' }
)

En este caso, la microtarea programada a través de promise.then() se ejecutará antes de regresar de vm.runInNewContext(), y será interrumpida por la funcionalidad timeout. Esto se aplica solo al código que se ejecuta en un vm.Context, por lo que, por ejemplo, vm.runInThisContext() no tiene esta opción.

Las devoluciones de llamada de Promise se introducen en la cola de microtareas del contexto en el que se crearon. Por ejemplo, si () =\> loop() se sustituye por simplemente loop en el ejemplo anterior, entonces loop se insertará en la cola de microtareas global, porque es una función del contexto exterior (principal) y, por lo tanto, también podrá escapar del tiempo de espera.

Si las funciones de programación asíncrona como process.nextTick(), queueMicrotask(), setTimeout(), setImmediate(), etc., están disponibles dentro de un vm.Context, las funciones que se les pasan se añadirán a las colas globales, que son compartidas por todos los contextos. Por lo tanto, las devoluciones de llamada que se pasan a esas funciones tampoco son controlables a través del tiempo de espera.

Soporte de import() dinámico en APIs de compilación

Las siguientes APIs admiten una opción importModuleDynamically para habilitar import() dinámico en el código compilado por el módulo vm.

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

Esta opción aún forma parte de la API de módulos experimental. No se recomienda usarla en un entorno de producción.

Cuando la opción importModuleDynamically no se especifica o es indefinida

Si esta opción no se especifica o si es undefined, el código que contiene import() aún puede ser compilado por las APIs de vm, pero cuando el código compilado se ejecuta y realmente llama a import(), el resultado rechazará con ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING.

Cuando importModuleDynamically es vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER

Esta opción no se admite actualmente para vm.SourceTextModule.

Con esta opción, cuando se inicia un import() en el código compilado, Node.js utilizaría el cargador ESM predeterminado del contexto principal para cargar el módulo solicitado y devolverlo al código que se está ejecutando.

Esto da acceso a los módulos integrados de Node.js como fs o http al código que se está compilando. Si el código se ejecuta en un contexto diferente, tenga en cuenta que los objetos creados por los módulos cargados desde el contexto principal siguen siendo del contexto principal y no son instanceof de clases integradas en el nuevo contexto.

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: La URL cargada desde el contexto principal no es una instancia de la clase Function
// en el nuevo contexto.
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: La URL cargada desde el contexto principal no es una instancia de la clase Function
// en el nuevo contexto.
script.runInNewContext().then(console.log)

Esta opción también permite que el script o la función carguen módulos de usuario:

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

// Escribe test.js y test.txt en el directorio donde se encuentra el script actual
// que se está ejecutando.
writeFileSync(resolve(import.meta.dirname, 'test.mjs'), 'export const filename = "./test.json";')
writeFileSync(resolve(import.meta.dirname, 'test.json'), '{"hello": "world"}')

// Compila un script que carga test.mjs y luego test.json
// como si el script se colocara en el mismo directorio.
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')

// Escribe test.js y test.txt en el directorio donde se encuentra el script actual
// que se está ejecutando.
writeFileSync(resolve(__dirname, 'test.mjs'), 'export const filename = "./test.json";')
writeFileSync(resolve(__dirname, 'test.json'), '{"hello": "world"}')

// Compila un script que carga test.mjs y luego test.json
// como si el script se colocara en el mismo directorio.
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)

Existen algunas salvedades al cargar módulos de usuario utilizando el cargador predeterminado del contexto principal:

Cuando importModuleDynamically es una función

Cuando importModuleDynamically es una función, se invocará cuando se llame a import() en el código compilado para que los usuarios personalicen cómo se debe compilar y evaluar el módulo solicitado. Actualmente, la instancia de Node.js debe iniciarse con el indicador --experimental-vm-modules para que esta opción funcione. Si el indicador no está establecido, esta devolución de llamada se ignorará. Si el código evaluado realmente llama a import(), el resultado se rechazará con ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG.

La devolución de llamada importModuleDynamically(specifier, referrer, importAttributes) tiene la siguiente firma:

  • specifier <string> especificador pasado a import()
  • referrer <vm.Script> | <Function> | <vm.SourceTextModule> | <Object> El referente es el vm.Script compilado para new vm.Script, vm.runInThisContext, vm.runInContext y vm.runInNewContext. Es la Function compilada para vm.compileFunction, el vm.SourceTextModule compilado para new vm.SourceTextModule y el contexto Object para vm.createContext().
  • importAttributes <Object> El valor "with" pasado al parámetro opcional optionsExpression, o un objeto vacío si no se proporcionó ningún valor.
  • Devuelve: <Objeto de espacio de nombres del módulo> | <vm.Module> Se recomienda devolver un vm.Module para aprovechar el seguimiento de errores y evitar problemas con los espacios de nombres que contienen exportaciones de la función then.
js
// Este script debe ejecutarse con --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) // El script compilado
    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
// Este script debe ejecutarse con --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) // El script compilado
      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' } }
})()