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.
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ón | Cambios |
---|---|
v21.7.0, v20.12.0 | Se agregó soporte para vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER . |
v17.0.0, v16.12.0 | Se agregó soporte para atributos de importación al parámetro importModuleDynamically . |
v10.6.0 | produceCachedData está obsoleto en favor de script.createCachedData() . |
v5.7.0 | Ahora se admiten las opciones cachedData y produceCachedData . |
v0.3.1 | Añ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 unBuffer
opcional, oTypedArray
, oDataView
con datos de caché de código de V8 para la fuente suministrada. Cuando se proporciona, el valorcachedDataRejected
se establecerá entrue
ofalse
dependiendo de la aceptación de los datos por parte de V8.produceCachedData
<boolean> Cuando estrue
y no haycachedData
presente, V8 intentará producir datos de caché de código paracode
. Si tiene éxito, se producirá unBuffer
con datos de caché de código de V8 y se almacenará en la propiedadcachedData
de la instanciavm.Script
devuelta. El valorcachedDataProduced
se establecerá entrue
ofalse
dependiendo de si los datos de caché de código se producen correctamente. Esta opción está obsoleta en favor descript.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 aimport()
. 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 deimport()
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
- Devuelve: <Buffer>
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.
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ón | Cambios |
---|---|
v6.3.0 | La opción breakOnSigint ahora es compatible. |
v0.3.1 | Agregado en: v0.3.1 |
contextifiedObject
<Objeto> Un objeto contextificado como el devuelto por el métodovm.createContext()
.options
<Objeto>displayErrors
<booleano> Cuando estrue
, si ocurre unError
mientras se compila elcode
, 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 ejecutarcode
antes de terminar la ejecución. Si la ejecución se termina, se lanzará unError
. Este valor debe ser un entero estrictamente positivo.breakOnSigint
<booleano> Si estrue
, recibirSIGINT
(+) terminará la ejecución y lanzará unError
. Los controladores existentes para el evento que se han adjuntado a través deprocess.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
.
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ón | Cambios |
---|---|
v22.8.0, v20.18.0 | El argumento contextObject ahora acepta vm.constants.DONT_CONTEXTIFY . |
v14.6.0 | La opción microtaskMode ahora es compatible. |
v10.0.0 | La opción contextCodeGeneration ahora es compatible. |
v6.3.0 | La opción breakOnSigint ahora es compatible. |
v0.3.1 | Añadido en: v0.3.1 |
contextObject
<Object> | <vm.constants.DONT_CONTEXTIFY> | <undefined> Ya seavm.constants.DONT_CONTEXTIFY
o un objeto que será contextificado. Si esundefined
, se creará un objeto contextificado vacío para compatibilidad con versiones anteriores.options
<Object>displayErrors
<boolean> Cuando estrue
, si ocurre unError
mientras se compila elcode
, 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 ejecutarcode
antes de terminar la ejecución. Si la ejecución termina, se lanzará unError
. Este valor debe ser un entero estrictamente positivo.breakOnSigint
<boolean> Si estrue
, recibirSIGINT
(+) terminará la ejecución y lanzará unError
. Los controladores existentes para el evento que se han adjuntado a través deprocess.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'
, dondei
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 propiedadurl.origin
de un objetoURL
. 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 aeval
o constructores de funciones (Function
,GeneratorFunction
, etc.) lanzará unEvalError
. Predeterminado:true
.wasm
<boolean> Si se establece en falso, cualquier intento de compilar un módulo WebAssembly lanzará unWebAssembly.CompileError
. Predeterminado:true
.microtaskMode
<string> Si se establece enafterEvaluate
, las microtareas (tareas programadas a través dePromise
s yasync function
s) se ejecutarán inmediatamente después de que se haya ejecutado el script. Se incluyen en los ámbitos detimeout
ybreakOnSigint
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.
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ón | Cambios |
---|---|
v6.3.0 | Ahora se admite la opción breakOnSigint . |
v0.3.1 | Agregado en: v0.3.1 |
options
<Objeto>displayErrors
<booleano> Cuando estrue
, si ocurre unError
al compilar elcó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 ejecutarcódigo
antes de finalizar la ejecución. Si la ejecución finaliza, se lanzará unError
. Este valor debe ser un entero estrictamente positivo.breakOnSigint
<booleano> Si estrue
, recibirSIGINT
(+) finalizará la ejecución y lanzará unError
. Los controladores existentes para el evento que se han adjuntado a través deprocess.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 sí 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:
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.
import vm from 'node:vm'
const script = new vm.Script(`
function myFunc() {}
//# sourceMappingURL=sourcemap.json
`)
console.log(script.sourceMapURL)
// Imprime: sourcemap.json
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.
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()
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á unError
. Este valor debe ser un entero estrictamente positivo.breakOnSigint
<boolean> Si estrue
, recibirSIGINT
(+) terminará la ejecución y lanzará unError
. Los controladores existentes para el evento que se hayan adjuntado a través deprocess.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ón | Cambios |
---|---|
v21.1.0, v20.10.0, v18.19.0 | La opción extra.assert se renombra a extra.attributes . El nombre anterior todavía se proporciona por compatibilidad con versiones anteriores. |
linker
<Function>specifier
<string> El especificador del módulo solicitado:referencingModule
<vm.Module> El objetoModule
en el que se llama alink()
.extra
<Object>attributes
<Object> Los datos del atributo: Según ECMA-262, se espera que los hosts activen un error si hay un atributo no admitido.assert
<Object> Alias paraextra.attributes
.Devuelve: <vm.Module> | <Promise>
Devuelve: <Promise>
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 función de enlace puede ser asíncrona, mientras que HostResolveImportedModule es síncrona.
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 amodule.link()
.'linking'
: Se ha llamado amodule.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 amodule.evaluate()
.'evaluating'
: El módulo se está evaluando a través de unmodule.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.
- Extiende: <vm.Module>
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ón | Cambios |
---|---|
v17.0.0, v16.12.0 | Se agregó soporte para atributos de importación al parámetro importModuleDynamically . |
code
<string> Código de Módulo de JavaScript para analizaroptions
identifier
<string> Cadena utilizada en los seguimientos de pila. Valor predeterminado:'vm:module(i)'
dondei
es un índice ascendente específico del contexto.cachedData
<Buffer> | <TypedArray> | <DataView> Proporciona unBuffer
oTypedArray
opcional, oDataView
con los datos de la caché de código de V8 para la fuente proporcionada. Elcode
debe ser el mismo que el módulo del que se creó estecachedData
.context
<Object> El objeto contextualizado tal como lo devuelve el métodovm.createContext()
, para compilar y evaluar esteModule
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 esteModule
. 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 esteModule
. Valor predeterminado:0
.initializeImportMeta
<Function> Se llama durante la evaluación de esteModule
para inicializar elimport.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 aimport()
. 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 deimport()
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.
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);
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
- Devuelve: <Buffer>
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.
// 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.
- Extiende: <vm.Module>
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.
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)'
dondei
es un índice ascendente específico del contexto.context
<Object> El objeto contextificado como lo devuelve el métodovm.createContext()
, para compilar y evaluar esteModule
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
.
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)
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ón | Cambios |
---|---|
v21.7.0, v20.12.0 | Se agregó soporte para vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER . |
v19.6.0, v18.15.0 | El 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.0 | Se agregó soporte para atributos de importación al parámetro importModuleDynamically . |
v15.9.0 | Se agregó nuevamente la opción importModuleDynamically . |
v14.3.0 | Eliminación de importModuleDynamically debido a problemas de compatibilidad. |
v14.1.0, v13.14.0 | Ahora se admite la opción importModuleDynamically . |
v10.10.0 | Agregado 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 unBuffer
oTypedArray
opcional, oDataView
con los datos de la caché de código de V8 para la fuente proporcionada. Esto debe ser producido por una llamada anterior avm.compileFunction()
con el mismocode
yparams
.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 aimport()
. 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 deimport()
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ón | Cambios |
---|---|
v22.8.0, v20.18.0 | El argumento contextObject ahora acepta vm.constants.DONT_CONTEXTIFY . |
v21.7.0, v20.12.0 | Se agregó soporte para vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER . |
v21.2.0, v20.11.0 | Ahora se admite la opción importModuleDynamically . |
v14.6.0 | Ahora se admite la opción microtaskMode . |
v10.0.0 | El primer argumento ya no puede ser una función. |
v10.0.0 | Ahora se admite la opción codeGeneration . |
v0.3.1 | Añadido en: v0.3.1 |
contextObject
<Object> | <vm.constants.DONT_CONTEXTIFY> | <undefined> Ya seavm.constants.DONT_CONTEXTIFY
o un objeto que será contextificado. Si esundefined
, 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'
, dondei
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 propiedadurl.origin
de un objetoURL
. 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 aeval
o constructores de funciones (Function
,GeneratorFunction
, etc.) generará unEvalError
. Predeterminado:true
.wasm
<boolean> Si se establece en falso, cualquier intento de compilar un módulo de WebAssembly generará unWebAssembly.CompileError
. Predeterminado:true
.microtaskMode
<string> Si se establece enafterEvaluate
, las microtareas (tareas programadas a través dePromise
s yasync function
s) se ejecutarán inmediatamente después de que un script se haya ejecutado a través descript.runInContext()
. Se incluyen en los ámbitostimeout
ybreakOnSigint
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 aimport()
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 deimport()
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.
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.
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ón | Cambios |
---|---|
v21.7.0, v20.12.0 | Se agregó soporte para vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER . |
v17.0.0, v16.12.0 | Se agregó soporte para atributos de importación al parámetro importModuleDynamically . |
v6.3.0 | La opción breakOnSigint ahora es compatible. |
v0.3.1 | Agregado en: v0.3.1 |
code
<string> El código JavaScript para compilar y ejecutar.contextifiedObject
<Object> El objeto contextificado que se utilizará comoglobal
cuando se compile y ejecute elcode
.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 estrue
, si ocurre unError
al compilar elcode
, 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 ejecutarcode
antes de terminar la ejecución. Si se termina la ejecución, se lanzará unError
. Este valor debe ser un entero estrictamente positivo.breakOnSigint
<boolean> Si estrue
, recibirSIGINT
(+) terminará la ejecución y lanzará unError
. Los controladores existentes para el evento que se han adjuntado a través deprocess.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 unBuffer
opcional oTypedArray
, oDataView
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 aimport()
. 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 deimport()
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:
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ón | Cambios |
---|---|
v22.8.0, v20.18.0 | El argumento contextObject ahora acepta vm.constants.DONT_CONTEXTIFY . |
v21.7.0, v20.12.0 | Se agregó soporte para vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER . |
v17.0.0, v16.12.0 | Se agregó soporte para atributos de importación al parámetro importModuleDynamically . |
v14.6.0 | Ahora se admite la opción microtaskMode . |
v10.0.0 | Ahora se admite la opción contextCodeGeneration . |
v6.3.0 | Ahora se admite la opción breakOnSigint . |
v0.3.1 | Agregado en: v0.3.1 |
code
<string> El código JavaScript para compilar y ejecutar.contextObject
<Object> | <vm.constants.DONT_CONTEXTIFY> | <undefined> Ya seavm.constants.DONT_CONTEXTIFY
o un objeto que será contextificado. Si esundefined
, se creará un objeto contextificado vacío por compatibilidad con versiones anteriores.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 estrue
, si ocurre unError
al compilar elcode
, 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 ejecutarcode
antes de finalizar la ejecución. Si se finaliza la ejecución, se lanzará unError
. Este valor debe ser un entero estrictamente positivo.breakOnSigint
<boolean> Si estrue
, recibirSIGINT
(+) finalizará la ejecución y lanzará unError
. Los controladores existentes para el evento que se han adjuntado a través deprocess.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'
, dondei
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 propiedadurl.origin
de un objetoURL
. 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 aeval
o constructores de funciones (Function
,GeneratorFunction
, etc.) lanzará unEvalError
. Predeterminado:true
.wasm
<boolean> Si se establece en falso, cualquier intento de compilar un módulo WebAssembly lanzará unWebAssembly.CompileError
. Predeterminado:true
.
cachedData
<Buffer> | <TypedArray> | <DataView> Proporciona unBuffer
opcional oTypedArray
, oDataView
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 aimport()
. 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 deimport()
dinámico en las API de compilación.microtaskMode
<string> Si se establece enafterEvaluate
, las microtareas (tareas programadas a través dePromise
s yasync function
s) se ejecutarán inmediatamente después de que se haya ejecutado el script. Se incluyen en los alcances detimeout
ybreakOnSigint
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
.
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ón | Cambios |
---|---|
v21.7.0, v20.12.0 | Se añadió soporte para vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER . |
v17.0.0, v16.12.0 | Se añadió soporte para atributos de importación al parámetro importModuleDynamically . |
v6.3.0 | La opción breakOnSigint ahora es compatible. |
v0.3.1 | Añadido en: v0.3.1 |
code
<string> El código JavaScript para compilar y ejecutar.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 estrue
, si ocurre unError
al compilar elcode
, 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 ejecutarcode
antes de terminar la ejecución. Si se termina la ejecución, se lanzará unError
. Este valor debe ser un entero estrictamente positivo.breakOnSigint
<boolean> Si estrue
, recibirSIGINT
(+) terminará la ejecución y lanzará unError
. Los controladores existentes para el evento que se han adjuntado a través deprocess.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 unBuffer
oTypedArray
opcional, oDataView
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 aimport()
. 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 deimport()
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:
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()
sí 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:
'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.
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.
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.
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 Promise
s y las async function
s 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:
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
:
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.
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)
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:
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)
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 aimport()
referrer
<vm.Script> | <Function> | <vm.SourceTextModule> | <Object> El referente es elvm.Script
compilado paranew vm.Script
,vm.runInThisContext
,vm.runInContext
yvm.runInNewContext
. Es laFunction
compilada paravm.compileFunction
, elvm.SourceTextModule
compilado paranew vm.SourceTextModule
y el contextoObject
paravm.createContext()
.importAttributes
<Object> El valor"with"
pasado al parámetro opcionaloptionsExpression
, 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ónthen
.
// 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' } }
// 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' } }
})()