Skip to content

VM (executando JavaScript)

[Estável: 2 - Estável]

Estável: 2 Estabilidade: 2 - Estável

Código-fonte: lib/vm.js

O módulo node:vm permite compilar e executar código dentro de contextos da Máquina Virtual V8.

O módulo node:vm não é um mecanismo de segurança. Não o utilize para executar código não confiável.

O código JavaScript pode ser compilado e executado imediatamente ou compilado, salvo e executado posteriormente.

Um caso de uso comum é executar o código em um contexto V8 diferente. Isso significa que o código invocado tem um objeto global diferente do código que o invoca.

Pode-se fornecer o contexto contextizando um objeto. O código invocado trata qualquer propriedade no contexto como uma variável global. Quaisquer alterações em variáveis globais causadas pelo código invocado são refletidas no objeto de contexto.

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

const x = 1

const context = { x: 2 }
vm.createContext(context) // Contextiza o objeto.

const code = 'x += 40; var y = 17;'
// `x` e `y` são variáveis globais no contexto.
// Inicialmente, x tem o valor 2 porque esse é o valor de context.x.
vm.runInContext(code, context)

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

console.log(x) // 1; y não está definido.

Classe: vm.Script

Adicionado em: v0.3.1

Instâncias da classe vm.Script contêm scripts pré-compilados que podem ser executados em contextos específicos.

new vm.Script(code[, options])

[Histórico]

VersãoAlterações
v21.7.0, v20.12.0Adicionou suporte para vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.
v17.0.0, v16.12.0Adicionou suporte para atributos de importação ao parâmetro importModuleDynamically.
v10.6.0O produceCachedData está depreciado em favor de script.createCachedData().
v5.7.0As opções cachedData e produceCachedData agora são suportadas.
v0.3.1Adicionada em: v0.3.1
  • code <string> O código JavaScript a ser compilado.
  • options <Object> | <string>
    • filename <string> Especifica o nome do arquivo usado nos rastros de pilha produzidos por este script. Padrão: 'evalmachine.\<anonymous\>'.
    • lineOffset <number> Especifica o offset do número da linha que é exibido nos rastros de pilha produzidos por este script. Padrão: 0.
    • columnOffset <number> Especifica o offset do número da coluna da primeira linha que é exibido nos rastros de pilha produzidos por este script. Padrão: 0.
    • cachedData <Buffer> | <TypedArray> | <DataView> Fornece um Buffer ou TypedArray opcional, ou DataView com os dados do cache de código do V8 para a fonte fornecida. Quando fornecido, o valor cachedDataRejected será definido como true ou false, dependendo da aceitação dos dados pelo V8.
    • produceCachedData <boolean> Quando true e nenhum cachedData estiver presente, o V8 tentará produzir dados do cache de código para code. Após o sucesso, um Buffer com os dados do cache de código do V8 será produzido e armazenado na propriedade cachedData da instância vm.Script retornada. O valor cachedDataProduced será definido como true ou false, dependendo se os dados do cache de código são produzidos com sucesso. Esta opção está depreciada em favor de script.createCachedData(). Padrão: false.
    • importModuleDynamically <Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Usado para especificar como os módulos devem ser carregados durante a avaliação deste script quando import() é chamado. Esta opção faz parte da API experimental de módulos. Não recomendamos usá-la em um ambiente de produção. Para informações detalhadas, consulte Suporte de import() dinâmico em APIs de compilação.

Se options for uma string, então ela especifica o nome do arquivo.

Criar um novo objeto vm.Script compila code, mas não o executa. O vm.Script compilado pode ser executado posteriormente várias vezes. O code não está vinculado a nenhum objeto global; em vez disso, ele é vinculado antes de cada execução, apenas para aquela execução.

script.cachedDataRejected

Adicionado em: v5.7.0

Quando cachedData é fornecido para criar o vm.Script, este valor será definido como true ou false dependendo da aceitação dos dados pelo V8. Caso contrário, o valor é undefined.

script.createCachedData()

Adicionado em: v10.6.0

Cria um cache de código que pode ser usado com a opção cachedData do construtor Script. Retorna um Buffer. Este método pode ser chamado a qualquer momento e qualquer número de vezes.

O cache de código do Script não contém nenhum estado observável JavaScript. O cache de código pode ser salvo juntamente com a fonte do script e usado para construir novas instâncias de Script várias vezes.

As funções na fonte do Script podem ser marcadas como compiladas preguiçosamente e não são compiladas na construção do Script. Essas funções serão compiladas quando forem invocadas pela primeira vez. O cache de código serializa os metadados que o V8 conhece atualmente sobre o Script que ele pode usar para acelerar compilações futuras.

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

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

const cacheWithoutAdd = script.createCachedData()
// Em `cacheWithoutAdd` a função `add()` está marcada para compilação completa
// após a invocação.

script.runInThisContext()

const cacheWithAdd = script.createCachedData()
// `cacheWithAdd` contém a função `add()` totalmente compilada.

script.runInContext(contextifiedObject[, options])

[Histórico]

VersãoAlterações
v6.3.0A opção breakOnSigint é agora suportada.
v0.3.1Adicionada em: v0.3.1
  • contextifiedObject <Object> Um objeto contextificado como retornado pelo método vm.createContext().

  • options <Object>

    • displayErrors <boolean> Quando true, se um Error ocorrer durante a compilação do code, a linha de código que causou o erro é anexada ao rastreamento de pilha. Padrão: true.
    • timeout <integer> Especifica o número de milissegundos para executar code antes de terminar a execução. Se a execução for terminada, um Error será lançado. Este valor deve ser um inteiro estritamente positivo.
    • breakOnSigint <boolean> Se true, receber SIGINT (+) terminará a execução e lançará um Error. Os manipuladores existentes para o evento que foram anexados via process.on('SIGINT') são desabilitados durante a execução do script, mas continuam a funcionar depois disso. Padrão: false.
  • Retorna: <any> o resultado da última instrução executada no script.

Executa o código compilado contido pelo objeto vm.Script dentro do contextifiedObject fornecido e retorna o resultado. A execução do código não tem acesso ao escopo local.

O exemplo a seguir compila código que incrementa uma variável global, define o valor de outra variável global e, em seguida, executa o código várias vezes. As variáveis globais estão contidas no 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' }

Usar as opções timeout ou breakOnSigint resultará em novos loops de eventos e threads correspondentes sendo iniciados, que têm uma sobrecarga de desempenho diferente de zero.

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

[Histórico]

VersãoAlterações
v22.8.0, v20.18.0O argumento contextObject agora aceita vm.constants.DONT_CONTEXTIFY.
v14.6.0A opção microtaskMode agora é suportada.
v10.0.0A opção contextCodeGeneration agora é suportada.
v6.3.0A opção breakOnSigint agora é suportada.
v0.3.1Adicionado em: v0.3.1
  • contextObject <Objeto> | <vm.constants.DONT_CONTEXTIFY> | <indefinido> Ou vm.constants.DONT_CONTEXTIFY ou um objeto que será contextificado. Se indefinido, um objeto contextificado vazio será criado para compatibilidade com versões anteriores.

  • options <Objeto>

    • displayErrors <booleano> Quando true, se um Error ocorrer durante a compilação do code, a linha de código que causou o erro é anexada ao rastro da pilha. Padrão: true.

    • timeout <inteiro> Especifica o número de milissegundos para executar code antes de terminar a execução. Se a execução for terminada, um Error será lançado. Este valor deve ser um inteiro estritamente positivo.

    • breakOnSigint <booleano> Se true, receber SIGINT (+) terminará a execução e lançará um Error. Os manipuladores existentes para o evento que foram anexados via process.on('SIGINT') são desabilitados durante a execução do script, mas continuam a funcionar depois disso. Padrão: false.

    • contextName <string> Nome legível por humanos do contexto recém-criado. Padrão: 'VM Context i', onde i é um índice numérico ascendente do contexto criado.

    • contextOrigin <string> Origem correspondente ao contexto recém-criado para fins de exibição. A origem deve ser formatada como uma URL, mas apenas com o esquema, host e porta (se necessário), como o valor da propriedade url.origin de um objeto URL. Mais notavelmente, esta string deve omitir a barra final, pois isso denota um caminho. Padrão: ''.

    • contextCodeGeneration <Objeto>

    • strings <booleano> Se definido como false, qualquer chamada para eval ou construtores de função (Function, GeneratorFunction, etc.) lançará um EvalError. Padrão: true.

    • wasm <booleano> Se definido como false, qualquer tentativa de compilar um módulo WebAssembly lançará um WebAssembly.CompileError. Padrão: true.

    • microtaskMode <string> Se definido como afterEvaluate, microtarefas (tarefas agendadas através de Promises e async functions) serão executadas imediatamente após a execução do script. Elas são incluídas nos escopos timeout e breakOnSigint nesse caso.

  • Retorna: <qualquer> o resultado da última instrução executada no script.

Este método é um atalho para script.runInContext(vm.createContext(options), options). Ele faz várias coisas ao mesmo tempo:

O exemplo a seguir compila código que define uma variável global, depois executa o código várias vezes em contextos diferentes. As variáveis globais são definidas e contidas em cada context 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' }]

// Isto lançaria uma exceção se o contexto fosse criado a partir de um objeto contextificado.
// vm.constants.DONT_CONTEXTIFY permite criar contextos com objetos globais comuns
// que podem ser congelados.
const freezeScript = new vm.Script('Object.freeze(globalThis); globalThis;')
const frozenContext = freezeScript.runInNewContext(vm.constants.DONT_CONTEXTIFY)

script.runInThisContext([options])

[Histórico]

VersãoAlterações
v6.3.0A opção breakOnSigint agora é suportada.
v0.3.1Adicionada em: v0.3.1
  • options <Objeto>

    • displayErrors <booleano> Quando true, se um Error ocorrer durante a compilação do code, a linha de código que causou o erro é anexada ao rastreamento da pilha. Padrão: true.
    • timeout <inteiro> Especifica o número de milissegundos para executar code antes de terminar a execução. Se a execução for terminada, um Error será lançado. Este valor deve ser um inteiro estritamente positivo.
    • breakOnSigint <booleano> Se true, receber SIGINT (+) terminará a execução e lançará um Error. Os manipuladores existentes para o evento que foram anexados via process.on('SIGINT') são desabilitados durante a execução do script, mas continuam a funcionar após isso. Padrão: false.
  • Retorna: <qualquer> o resultado da última instrução executada no script.

Executa o código compilado contido pelo vm.Script dentro do contexto do objeto global atual. A execução do código não tem acesso ao escopo local, mas tem acesso ao objeto global atual.

O exemplo a seguir compila o código que incrementa uma variável global e, em seguida, executa esse código várias vezes:

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

Adicionado em: v19.1.0, v18.13.0

Quando o script é compilado de uma fonte que contém um comentário mágico de mapeamento de origem, esta propriedade será definida como a URL do mapeamento de origem.

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

Classe: vm.Module

Adicionado em: v13.0.0, v12.16.0

[Estável: 1 - Experimental]

Estável: 1 Estabilidade: 1 - Experimental

Este recurso está disponível apenas com a sinalização de comando --experimental-vm-modules ativada.

A classe vm.Module fornece uma interface de baixo nível para usar módulos ECMAScript em contextos de VM. É a contraparte da classe vm.Script que espelha de perto os Registros de Módulo conforme definidos na especificação ECMAScript.

Ao contrário de vm.Script, no entanto, cada objeto vm.Module está vinculado a um contexto desde sua criação. As operações em objetos vm.Module são intrinsecamente assíncronas, em contraste com a natureza síncrona dos objetos vm.Script. O uso de funções 'async' pode ajudar a manipular objetos vm.Module.

Usar um objeto vm.Module requer três etapas distintas: criação/análise, ligação e avaliação. Essas três etapas são ilustradas no exemplo a seguir.

Esta implementação está em um nível inferior ao Carregador de Módulo ECMAScript. Também não há como interagir com o Carregador ainda, embora o suporte esteja planejado.

js
import vm from 'node:vm'

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

// Etapa 1
//
// Crie um Módulo construindo um novo objeto `vm.SourceTextModule`. Isso
// analisa o texto de origem fornecido, lançando um `SyntaxError` se algo der
// errado. Por padrão, um Módulo é criado no contexto superior. Mas aqui,
// especificamos `contextifiedObject` como o contexto ao qual este Módulo pertence.
//
// Aqui, tentamos obter a exportação padrão do módulo "foo" e
// colocá-la no vínculo local "secret".

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

// Etapa 2
//
// "Vincule" as dependências importadas deste Módulo a ele.
//
// O callback de vinculação fornecido (o "vinculador") aceita dois argumentos: o
// módulo pai (`bar` neste caso) e a string que é o especificador do
// módulo importado. O callback deve retornar um Módulo que
// corresponde ao especificador fornecido, com certos requisitos documentados
// em `module.link()`.
//
// Se a vinculação não tiver começado para o Módulo retornado, o mesmo callback
// de vinculação será chamado no Módulo retornado.
//
// Mesmo os Módulos de nível superior sem dependências devem ser explicitamente
// vinculados. O callback fornecido, no entanto, nunca seria chamado.
//
// O método link() retorna uma Promise que será resolvida quando todas as
// Promises retornadas pelo vinculador forem resolvidas.
//
// Nota: Este é um exemplo artificial, pois a função vinculador cria um novo
// módulo "foo" a cada vez que é chamada. Em um sistema de módulo completo, um
// cache provavelmente seria usado para evitar módulos duplicados.

async function linker(specifier, referencingModule) {
  if (specifier === 'foo') {
    return new vm.SourceTextModule(
      `
      // A variável "secret" se refere à variável global que adicionamos a
      // "contextifiedObject" ao criar o contexto.
      export default secret;
    `,
      { context: referencingModule.context }
    )

    // Usar `contextifiedObject` em vez de `referencingModule.context`
    // aqui também funcionaria.
  }
  throw new Error(`Não foi possível resolver a dependência: ${specifier}`)
}
await bar.link(linker)

// Etapa 3
//
// Avalie o Módulo. O método evaluate() retorna uma promise que será
// resolvida após o módulo terminar de ser avaliado.

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

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

;(async () => {
  // Etapa 1
  //
  // Crie um Módulo construindo um novo objeto `vm.SourceTextModule`. Isso
  // analisa o texto de origem fornecido, lançando um `SyntaxError` se algo der
  // errado. Por padrão, um Módulo é criado no contexto superior. Mas aqui,
  // especificamos `contextifiedObject` como o contexto ao qual este Módulo pertence.
  //
  // Aqui, tentamos obter a exportação padrão do módulo "foo" e
  // colocá-la no vínculo local "secret".

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

  // Etapa 2
  //
  // "Vincule" as dependências importadas deste Módulo a ele.
  //
  // O callback de vinculação fornecido (o "vinculador") aceita dois argumentos: o
  // módulo pai (`bar` neste caso) e a string que é o especificador do
  // módulo importado. O callback deve retornar um Módulo que
  // corresponde ao especificador fornecido, com certos requisitos documentados
  // em `module.link()`.
  //
  // Se a vinculação não tiver começado para o Módulo retornado, o mesmo callback
  // de vinculação será chamado no Módulo retornado.
  //
  // Mesmo os Módulos de nível superior sem dependências devem ser explicitamente
  // vinculados. O callback fornecido, no entanto, nunca seria chamado.
  //
  // O método link() retorna uma Promise que será resolvida quando todas as
  // Promises retornadas pelo vinculador forem resolvidas.
  //
  // Nota: Este é um exemplo artificial, pois a função vinculador cria um novo
  // módulo "foo" a cada vez que é chamada. Em um sistema de módulo completo, um
  // cache provavelmente seria usado para evitar módulos duplicados.

  async function linker(specifier, referencingModule) {
    if (specifier === 'foo') {
      return new vm.SourceTextModule(
        `
        // A variável "secret" se refere à variável global que adicionamos a
        // "contextifiedObject" ao criar o contexto.
        export default secret;
      `,
        { context: referencingModule.context }
      )

      // Usar `contextifiedObject` em vez de `referencingModule.context`
      // aqui também funcionaria.
    }
    throw new Error(`Não foi possível resolver a dependência: ${specifier}`)
  }
  await bar.link(linker)

  // Etapa 3
  //
  // Avalie o Módulo. O método evaluate() retorna uma promise que será
  // resolvida após o módulo terminar de ser avaliado.

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

module.dependencySpecifiers

Os especificadores de todas as dependências deste módulo. O array retornado é congelado para impedir quaisquer alterações nele.

Corresponde ao campo [[RequestedModules]] dos registros de módulos cíclicos na especificação ECMAScript.

module.error

Se o module.status for 'errored', esta propriedade contém a exceção lançada pelo módulo durante a avaliação. Se o status for diferente, acessar esta propriedade resultará em uma exceção lançada.

O valor undefined não pode ser usado para casos em que não há uma exceção lançada devido à possível ambiguidade com throw undefined;.

Corresponde ao campo [[EvaluationError]] dos registros de módulos cíclicos na especificação ECMAScript.

module.evaluate([options])

  • options <Object>

    • timeout <integer> Especifica o número de milissegundos para avaliar antes de terminar a execução. Se a execução for interrompida, um Error será lançado. Este valor deve ser um inteiro estritamente positivo.
    • breakOnSigint <boolean> Se true, receber SIGINT (+) terminará a execução e lançará um Error. Os manipuladores existentes para o evento que foram anexados via process.on('SIGINT') são desabilitados durante a execução do script, mas continuam a funcionar depois disso. Padrão: false.
  • Retorna: <Promise> Cumpre com undefined em caso de sucesso.

Avaliar o módulo.

Isso deve ser chamado depois que o módulo foi vinculado; caso contrário, ele será rejeitado. Pode ser chamado também quando o módulo já foi avaliado, caso em que ele não fará nada se a avaliação inicial terminar com sucesso (module.status é 'evaluated') ou lançará novamente a exceção que a avaliação inicial resultou (module.status é 'errored').

Este método não pode ser chamado enquanto o módulo está sendo avaliado (module.status é 'evaluating').

Corresponde ao campo método concreto Evaluate() dos registros de módulos cíclicos na especificação ECMAScript.

module.identifier

O identificador do módulo atual, como definido no construtor.

module.link(linker)

[Histórico]

VersãoAlterações
v21.1.0, v20.10.0, v18.19.0A opção extra.assert foi renomeada para extra.attributes. O nome anterior ainda é fornecido para compatibilidade com versões anteriores.

Vincular dependências de módulos. Este método deve ser chamado antes da avaliação e só pode ser chamado uma vez por módulo.

A função deve retornar um objeto Module ou uma Promise que eventualmente se resolve para um objeto Module. O Module retornado deve satisfazer as duas invariantes a seguir:

  • Deve pertencer ao mesmo contexto que o Module pai.
  • Seu status não deve ser 'errored'.

Se o status do Module retornado for 'unlinked', este método será chamado recursivamente no Module retornado com a mesma função linker fornecida.

link() retorna uma Promise que será resolvida quando todas as instâncias de vinculação se resolverem para um Module válido, ou rejeitada se a função linker lançar uma exceção ou retornar um Module inválido.

A função linker corresponde aproximadamente à operação abstrata HostResolveImportedModule definida pela implementação na especificação ECMAScript, com algumas diferenças importantes:

A implementação real de HostResolveImportedModule usada durante a vinculação do módulo é aquela que retorna os módulos vinculados durante a vinculação. Como nesse ponto todos os módulos já teriam sido totalmente vinculados, a implementação de HostResolveImportedModule é totalmente síncrona por especificação.

Corresponde ao campo método concreto Link() de Registros de Módulo Cíclicos na especificação ECMAScript.

module.namespace

O objeto namespace do módulo. Isso só está disponível após a conclusão do link (module.link()).

Corresponde à operação abstrata GetModuleNamespace na especificação ECMAScript.

module.status

O status atual do módulo. Será um dos seguintes:

  • 'unlinked': module.link() ainda não foi chamado.
  • 'linking': module.link() foi chamado, mas nem todas as Promises retornadas pela função de linkagem foram resolvidas ainda.
  • 'linked': O módulo foi vinculado com sucesso e todas as suas dependências estão vinculadas, mas module.evaluate() ainda não foi chamado.
  • 'evaluating': O módulo está sendo avaliado através de module.evaluate() em si mesmo ou em um módulo pai.
  • 'evaluated': O módulo foi avaliado com sucesso.
  • 'errored': O módulo foi avaliado, mas uma exceção foi lançada.

Além de 'errored', esta string de status corresponde ao campo [[Status]] do Registro de Módulo Cíclico da especificação. 'errored' corresponde a 'evaluated' na especificação, mas com [[EvaluationError]] definido como um valor que não é undefined.

Classe: vm.SourceTextModule

Adicionado em: v9.6.0

[Estável: 1 - Experimental]

Estável: 1 Estabilidade: 1 - Experimental

Este recurso está disponível apenas com a flag de comando --experimental-vm-modules ativada.

A classe vm.SourceTextModule fornece o Registro de Módulo de Texto de Origem conforme definido na especificação ECMAScript.

new vm.SourceTextModule(code[, options])

[Histórico]

VersãoAlterações
v17.0.0, v16.12.0Adicionou suporte para atributos de importação ao parâmetro importModuleDynamically.
  • code <string> Código do módulo JavaScript para análise

  • options

    • identifier <string> String usada em rastros de pilha. Padrão: 'vm:module(i)' onde i é um índice ascendente específico do contexto.

    • cachedData <Buffer> | <TypedArray> | <DataView> Fornece um Buffer ou TypedArray opcional, ou DataView com os dados do cache de código do V8 para a fonte fornecida. O code deve ser o mesmo que o módulo do qual este cachedData foi criado.

    • context <Object> O objeto contextizado conforme retornado pelo método vm.createContext(), para compilar e avaliar este Module. Se nenhum contexto for especificado, o módulo será avaliado no contexto de execução atual.

    • lineOffset <integer> Especifica o deslocamento do número da linha que é exibido nos rastros de pilha produzidos por este Module. Padrão: 0.

    • columnOffset <integer> Especifica o deslocamento do número da coluna da primeira linha que é exibido nos rastros de pilha produzidos por este Module. Padrão: 0.

    • initializeImportMeta <Function> Chamado durante a avaliação deste Module para inicializar o import.meta.

    • meta <import.meta>

    • module <vm.SourceTextModule>

    • importModuleDynamically <Function> Usado para especificar como os módulos devem ser carregados durante a avaliação deste módulo quando import() é chamado. Esta opção faz parte da API experimental de módulos. Não recomendamos usá-la em um ambiente de produção. Para informações detalhadas, consulte Suporte de import() dinâmico em APIs de compilação.

Cria uma nova instância SourceTextModule.

Propriedades atribuídas ao objeto import.meta que são objetos podem permitir que o módulo acesse informações fora do context especificado. Use vm.runInContext() para criar objetos em um 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) {
    // Nota: este objeto é criado no contexto superior. Como tal,
    // Object.getPrototypeOf(import.meta.prop) aponta para o
    // Object.prototype no contexto superior em vez daquele no
    // objeto contextizado.
    meta.prop = {}
  },
})
// Como o módulo não tem dependências, a função de linkagem nunca será chamada.
await module.link(() => {})
await module.evaluate()

// Agora, Object.prototype.secret será igual a 42.
//
// Para corrigir este problema, substitua
//     meta.prop = {};
// acima por
//     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) {
      // Nota: este objeto é criado no contexto superior. Como tal,
      // Object.getPrototypeOf(import.meta.prop) aponta para o
      // Object.prototype no contexto superior em vez daquele no
      // objeto contextizado.
      meta.prop = {}
    },
  })
  // Como o módulo não tem dependências, a função de linkagem nunca será chamada.
  await module.link(() => {})
  await module.evaluate()
  // Agora, Object.prototype.secret será igual a 42.
  //
  // Para corrigir este problema, substitua
  //     meta.prop = {};
  // acima por
  //     meta.prop = vm.runInContext('{}', contextifiedObject);
})()

sourceTextModule.createCachedData()

Adicionado em: v13.7.0, v12.17.0

Cria um cache de código que pode ser usado com a opção cachedData do construtor SourceTextModule. Retorna um Buffer. Este método pode ser chamado qualquer número de vezes antes do módulo ter sido avaliado.

O cache de código do SourceTextModule não contém nenhum estado observável JavaScript. O cache de código pode ser salvo junto com a fonte do script e usado para construir novas instâncias SourceTextModule múltiplas vezes.

Funções na fonte SourceTextModule podem ser marcadas como compiladas preguiçosamente e não são compiladas na construção do SourceTextModule. Essas funções serão compiladas quando forem invocadas pela primeira vez. O cache de código serializa os metadados que o V8 atualmente conhece sobre o SourceTextModule que ele pode usar para acelerar compilações futuras.

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

// Cria dados em cache a partir deste módulo
const cachedData = module.createCachedData()

// Cria um novo módulo usando os dados em cache. O código deve ser o mesmo.
const module2 = new vm.SourceTextModule('const a = 1;', { cachedData })

Classe: vm.SyntheticModule

Adicionado em: v13.0.0, v12.16.0

[Estável: 1 - Experimental]

Estável: 1 Estabilidade: 1 - Experimental

Este recurso está disponível apenas com o sinalizador de comando --experimental-vm-modules ativado.

A classe vm.SyntheticModule fornece o Registro de Módulo Sintético conforme definido na especificação WebIDL. O objetivo dos módulos sintéticos é fornecer uma interface genérica para expor fontes não JavaScript a 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)
})

// Use `module` na ligação...

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

Adicionado em: v13.0.0, v12.16.0

  • exportNames <string[]> Array de nomes que serão exportados do módulo.
  • evaluateCallback <Function> Chamado quando o módulo é avaliado.
  • options
    • identifier <string> String usada em rastros de pilha. Padrão: 'vm:module(i)' onde i é um índice ascendente específico do contexto.
    • context <Object> O objeto contextizado conforme retornado pelo método vm.createContext(), para compilar e avaliar este Module.

Cria uma nova instância SyntheticModule.

Objetos atribuídos às exportações desta instância podem permitir que importadores do módulo acessem informações fora do context especificado. Use vm.runInContext() para criar objetos em um contexto específico.

syntheticModule.setExport(name, value)

Adicionado em: v13.0.0, v12.16.0

  • name <string> Nome da exportação a ser definida.
  • value <any> O valor para definir a exportação.

Este método é usado após o módulo ser vinculado para definir os valores das exportações. Se for chamado antes do módulo ser vinculado, um erro ERR_VM_MODULE_STATUS será lançado.

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

[Histórico]

VersãoAlterações
v21.7.0, v20.12.0Adicionada a compatibilidade com vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.
v19.6.0, v18.15.0O valor retornado agora inclui cachedDataRejected com a mesma semântica que a versão vm.Script se a opção cachedData foi passada.
v17.0.0, v16.12.0Adicionada a compatibilidade com atributos de importação ao parâmetro importModuleDynamically.
v15.9.0A opção importModuleDynamically foi adicionada novamente.
v14.3.0Remoção de importModuleDynamically devido a problemas de compatibilidade.
v14.1.0, v13.14.0A opção importModuleDynamically agora é suportada.
v10.10.0Adicionada em: v10.10.0
  • code <string> O corpo da função a ser compilada.

  • params <string[]> Um array de strings contendo todos os parâmetros para a função.

  • options <Object>

    • filename <string> Especifica o nome do arquivo usado nos rastros de pilha produzidos por este script. Padrão: ''.
    • lineOffset <number> Especifica o offset do número da linha que é exibido nos rastros de pilha produzidos por este script. Padrão: 0.
    • columnOffset <number> Especifica o offset do número da coluna da primeira linha que é exibido nos rastros de pilha produzidos por este script. Padrão: 0.
    • cachedData <Buffer> | <TypedArray> | <DataView> Fornece um Buffer ou TypedArray opcional, ou DataView com os dados do cache de código do V8 para a fonte fornecida. Isso deve ser produzido por uma chamada anterior a vm.compileFunction() com o mesmo code e params.
    • produceCachedData <boolean> Especifica se deve produzir novos dados de cache. Padrão: false.
    • parsingContext <Object> O objeto contextizado em que a função mencionada deve ser compilada.
    • contextExtensions <Object[]> Um array contendo uma coleção de extensões de contexto (objetos que encapsulam o escopo atual) a serem aplicadas durante a compilação. Padrão: [].
  • importModuleDynamically <Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Usado para especificar como os módulos devem ser carregados durante a avaliação desta função quando import() é chamado. Esta opção faz parte da API experimental de módulos. Não recomendamos usá-la em um ambiente de produção. Para informações detalhadas, consulte Suporte de import() dinâmico em APIs de compilação.

  • Retorna: <Function>

Compila o código fornecido no contexto fornecido (se nenhum contexto for fornecido, o contexto atual é usado) e o retorna encapsulado em uma função com os params fornecidos.

vm.constants

Adicionado em: v21.7.0, v20.12.0

Retorna um objeto contendo constantes comumente usadas para operações da VM.

vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER

Adicionado em: v21.7.0, v20.12.0

[Estável: 1 - Experimental]

Estável: 1 Estabilidade: 1.1 - Desenvolvimento ativo

Uma constante que pode ser usada como a opção importModuleDynamically para vm.Script e vm.compileFunction() para que o Node.js use o carregador ESM padrão do contexto principal para carregar o módulo solicitado.

Para informações detalhadas, consulte Suporte de import() dinâmico em APIs de compilação.

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

[Histórico]

VersãoAlterações
v22.8.0, v20.18.0O argumento contextObject agora aceita vm.constants.DONT_CONTEXTIFY.
v21.7.0, v20.12.0Adicionou suporte para vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.
v21.2.0, v20.11.0A opção importModuleDynamically agora é suportada.
v14.6.0A opção microtaskMode agora é suportada.
v10.0.0O primeiro argumento não pode mais ser uma função.
v10.0.0A opção codeGeneration agora é suportada.
v0.3.1Adicionada em: v0.3.1
  • contextObject <Object> | <vm.constants.DONT_CONTEXTIFY> | <undefined> Ou vm.constants.DONT_CONTEXTIFY ou um objeto que será contextizado. Se undefined, um objeto contextizado vazio será criado para compatibilidade com versões anteriores.

  • options <Object>

    • name <string> Nome legível por humanos do contexto recém-criado. Padrão: 'VM Context i', onde i é um índice numérico crescente do contexto criado.

    • origin <string> Origem correspondente ao contexto recém-criado para fins de exibição. A origem deve ser formatada como uma URL, mas apenas com o esquema, host e porta (se necessário), como o valor da propriedade url.origin de um objeto URL. Mais notavelmente, esta string deve omitir a barra final, pois isso denota um caminho. Padrão: ''.

    • codeGeneration <Object>

    • strings <boolean> Se definido como falso, qualquer chamada para eval ou construtores de função (Function, GeneratorFunction, etc.) lançará um EvalError. Padrão: true.

    • wasm <boolean> Se definido como falso, qualquer tentativa de compilar um módulo WebAssembly lançará um WebAssembly.CompileError. Padrão: true.

    • microtaskMode <string> Se definido como afterEvaluate, microtarefas (tarefas programadas através de Promises e async functions) serão executadas imediatamente após um script ter sido executado através de script.runInContext(). Elas são incluídas nos escopos timeout e breakOnSigint nesse caso.

    • importModuleDynamically <Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Usado para especificar como os módulos devem ser carregados quando import() é chamado neste contexto sem um script ou módulo de referência. Esta opção faz parte da API experimental de módulos. Não recomendamos usá-la em um ambiente de produção. Para informações detalhadas, consulte Suporte de import() dinâmico em APIs de compilação.

  • Retorna: <Object> objeto contextizado.

Se o contextObject fornecido for um objeto, o método vm.createContext() preparará esse objeto e retornará uma referência a ele para que possa ser usado em chamadas para vm.runInContext() ou script.runInContext(). Dentro desses scripts, o objeto global será encapsulado pelo contextObject, retendo todas as suas propriedades existentes, mas também tendo os objetos e funções integrados que qualquer objeto global padrão possui. Fora dos scripts executados pelo módulo vm, as variáveis globais permanecerão inalteradas.

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

Se contextObject for omitido (ou passado explicitamente como undefined), um novo objeto contextizado vazio será retornado.

Quando o objeto global no contexto recém-criado é contextizado, ele apresenta algumas peculiaridades em comparação com objetos globais comuns. Por exemplo, ele não pode ser congelado. Para criar um contexto sem as peculiaridades de contextificação, passe vm.constants.DONT_CONTEXTIFY como o argumento contextObject. Consulte a documentação de vm.constants.DONT_CONTEXTIFY para obter detalhes.

O método vm.createContext() é principalmente útil para criar um único contexto que pode ser usado para executar vários scripts. Por exemplo, se estiver emulando um navegador da web, o método pode ser usado para criar um único contexto representando o objeto global de uma janela, e então executar todas as tags \<script\> juntas dentro desse contexto.

O name e a origin fornecidos do contexto são tornados visíveis através da API do Inspector.

vm.isContext(object)

Adicionado em: v0.11.7

Retorna true se o objeto object fornecido foi contextizado usando vm.createContext(), ou se é o objeto global de um contexto criado usando vm.constants.DONT_CONTEXTIFY.

vm.measureMemory([options])

Adicionado em: v13.10.0

[Estável: 1 - Experimental]

Estável: 1 Estabilidade: 1 - Experimental

Mede a memória conhecida pelo V8 e usada por todos os contextos conhecidos pelo isolamento V8 atual, ou o contexto principal.

  • options <Object> Opcional.

    • mode <string> 'summary' ou 'detailed'. No modo resumo, somente a memória medida para o contexto principal será retornada. No modo detalhado, a memória medida para todos os contextos conhecidos pelo isolamento V8 atual será retornada. Padrão: 'summary'
    • execution <string> 'default' ou 'eager'. Com a execução padrão, a promise não será resolvida até após o início da próxima coleta de lixo agendada, o que pode levar algum tempo (ou nunca, se o programa sair antes da próxima GC). Com a execução imediata, o GC será iniciado imediatamente para medir a memória. Padrão: 'default'
  • Retorna: <Promise> Se a memória for medida com sucesso, a promise será resolvida com um objeto contendo informações sobre o uso da memória. Caso contrário, será rejeitada com um erro ERR_CONTEXT_NOT_INITIALIZED.

O formato do objeto com o qual a Promise retornada pode ser resolvida é específico do mecanismo V8 e pode mudar de uma versão do V8 para a próxima.

O resultado retornado difere das estatísticas retornadas por v8.getHeapSpaceStatistics() no sentido de que vm.measureMemory() mede a memória acessível por cada contexto específico do V8 na instância atual do mecanismo V8, enquanto o resultado de v8.getHeapSpaceStatistics() mede a memória ocupada por cada espaço de heap na instância V8 atual.

js
const vm = require('node:vm')
// Mede a memória usada pelo contexto principal.
vm.measureMemory({ mode: 'summary' })
  // Isso é o mesmo que vm.measureMemory()
  .then(result => {
    // O formato atual é:
    // {
    //   total: {
    //      jsMemoryEstimate: 2418479, jsMemoryRange: [ 2418479, 2745799 ]
    //    }
    // }
    console.log(result)
  })

const context = vm.createContext({ a: 1 })
vm.measureMemory({ mode: 'detailed', execution: 'eager' }).then(result => {
  // Referencie o contexto aqui para que ele não seja coletado pelo GC
  // até que a medição esteja 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])

[Histórico]

VersãoAlterações
v21.7.0, v20.12.0Suporte adicionado para vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.
v17.0.0, v16.12.0Suporte adicionado para atributos de importação ao parâmetro importModuleDynamically.
v6.3.0A opção breakOnSigint agora é suportada.
v0.3.1Adicionada em: v0.3.1
  • code <string> O código JavaScript para compilar e executar.
  • contextifiedObject <Object> O objeto contextizado que será usado como global quando o code for compilado e executado.
  • options <Object> | <string>
    • filename <string> Especifica o nome do arquivo usado nos rastros de pilha produzidos por este script. Padrão: 'evalmachine.\<anonymous\>'.
    • lineOffset <number> Especifica o deslocamento do número da linha que é exibido nos rastros de pilha produzidos por este script. Padrão: 0.
    • columnOffset <number> Especifica o deslocamento do número da coluna da primeira linha que é exibido nos rastros de pilha produzidos por este script. Padrão: 0.
    • displayErrors <boolean> Quando true, se um Error ocorrer durante a compilação do code, a linha de código que causou o erro é anexada ao rastro de pilha. Padrão: true.
    • timeout <integer> Especifica o número de milissegundos para executar code antes de terminar a execução. Se a execução for terminada, um Error será lançado. Este valor deve ser um inteiro estritamente positivo.
    • breakOnSigint <boolean> Se true, receber SIGINT (+) terminará a execução e lançará um Error. Os manipuladores existentes para o evento que foram anexados via process.on('SIGINT') são desabilitados durante a execução do script, mas continuam a funcionar depois disso. Padrão: false.
    • cachedData <Buffer> | <TypedArray> | <DataView> Fornece um Buffer ou TypedArray, ou DataView opcional com os dados do cache de código do V8 para a fonte fornecida.
    • importModuleDynamically <Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Usado para especificar como os módulos devem ser carregados durante a avaliação deste script quando import() é chamado. Esta opção faz parte da API experimental de módulos. Não recomendamos usá-la em um ambiente de produção. Para informações detalhadas, consulte Suporte de import() dinâmico em APIs de compilação.

O método vm.runInContext() compila code, executa-o no contexto do contextifiedObject e retorna o resultado. A execução do código não tem acesso ao escopo local. O objeto contextifiedObject deve ter sido previamente contextizado usando o método vm.createContext().

Se options for uma string, então ele especifica o nome do arquivo.

O exemplo a seguir compila e executa scripts diferentes usando um único objeto contextizado:

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

[Histórico]

VersãoAlterações
v22.8.0, v20.18.0O argumento contextObject agora aceita vm.constants.DONT_CONTEXTIFY.
v21.7.0, v20.12.0Adicionou suporte para vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.
v17.0.0, v16.12.0Adicionou suporte para atributos de importação ao parâmetro importModuleDynamically.
v14.6.0A opção microtaskMode agora é suportada.
v10.0.0A opção contextCodeGeneration agora é suportada.
v6.3.0A opção breakOnSigint agora é suportada.
v0.3.1Adicionado em: v0.3.1
  • code <string> O código JavaScript a ser compilado e executado.

  • contextObject <Object> | <vm.constants.DONT_CONTEXTIFY> | <undefined> Ou vm.constants.DONT_CONTEXTIFY ou um objeto que será contextizado. Se undefined, um objeto contextizado vazio será criado para compatibilidade com versões anteriores.

  • options <Object> | <string>

    • filename <string> Especifica o nome do arquivo usado nos rastros de pilha produzidos por este script. Padrão: 'evalmachine.\<anonymous\>'.

    • lineOffset <number> Especifica o deslocamento do número da linha que é exibido nos rastros de pilha produzidos por este script. Padrão: 0.

    • columnOffset <number> Especifica o deslocamento do número da coluna da primeira linha que é exibido nos rastros de pilha produzidos por este script. Padrão: 0.

    • displayErrors <boolean> Quando true, se um Error ocorrer durante a compilação do code, a linha de código que causou o erro é anexada ao rastro de pilha. Padrão: true.

    • timeout <integer> Especifica o número de milissegundos para executar code antes de terminar a execução. Se a execução for terminada, um Error será lançado. Este valor deve ser um inteiro estritamente positivo.

    • breakOnSigint <boolean> Se true, receber SIGINT (+) terminará a execução e lançará um Error. Os manipuladores existentes para o evento que foram anexados via process.on('SIGINT') são desabilitados durante a execução do script, mas continuam a funcionar depois disso. Padrão: false.

    • contextName <string> Nome legível por humanos do contexto recém-criado. Padrão: 'VM Context i', onde i é um índice numérico ascendente do contexto criado.

    • contextOrigin <string> Origem correspondente ao contexto recém-criado para fins de exibição. A origem deve ser formatada como uma URL, mas apenas com o esquema, host e porta (se necessário), como o valor da propriedade url.origin de um objeto URL. Mais notavelmente, esta string deve omitir a barra final, pois isso denota um caminho. Padrão: ''.

    • contextCodeGeneration <Object>

    • strings <boolean> Se definido como false, qualquer chamada para eval ou construtores de função (Function, GeneratorFunction, etc.) lançará um EvalError. Padrão: true.

    • wasm <boolean> Se definido como false, qualquer tentativa de compilar um módulo WebAssembly lançará um WebAssembly.CompileError. Padrão: true.

    • cachedData <Buffer> | <TypedArray> | <DataView> Fornece um Buffer ou TypedArray, ou DataView opcional com os dados do cache de código do V8 para a fonte fornecida.

    • importModuleDynamically <Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Usado para especificar como os módulos devem ser carregados durante a avaliação deste script quando import() é chamado. Esta opção faz parte da API experimental de módulos. Não recomendamos usá-la em um ambiente de produção. Para informações detalhadas, consulte Suporte de import() dinâmico em APIs de compilação.

    • microtaskMode <string> Se definido como afterEvaluate, microtarefas (tarefas programadas por meio de Promises e async functions) serão executadas imediatamente após a execução do script. Elas são incluídas nos escopos timeout e breakOnSigint nesse caso.

  • Retorna: <any> o resultado da última instrução executada no script.

Este método é um atalho para (new vm.Script(code, options)).runInContext(vm.createContext(options), options). Se options for uma string, ele especifica o nome do arquivo.

Ele faz várias coisas ao mesmo tempo:

O exemplo a seguir compila e executa código que incrementa uma variável global e define uma nova. Essas variáveis globais estão contidas no 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' }

// Isso lançaria uma exceção se o contexto fosse criado a partir de um objeto contextizado.
// vm.constants.DONT_CONTEXTIFY permite criar contextos com objetos globais comuns que
// podem ser congelados.
const frozenContext = vm.runInNewContext('Object.freeze(globalThis); globalThis;', vm.constants.DONT_CONTEXTIFY)

vm.runInThisContext(code[, options])

[Histórico]

VersãoAlterações
v21.7.0, v20.12.0Adicionou suporte para vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.
v17.0.0, v16.12.0Adicionou suporte para atributos de importação ao parâmetro importModuleDynamically.
v6.3.0A opção breakOnSigint agora é suportada.
v0.3.1Adicionado em: v0.3.1
  • code <string> O código JavaScript para compilar e executar.

  • options <Object> | <string>

    • filename <string> Especifica o nome do arquivo usado nos rastros de pilha produzidos por este script. Padrão: 'evalmachine.\<anonymous\>'.
    • lineOffset <number> Especifica o offset do número da linha que é exibido nos rastros de pilha produzidos por este script. Padrão: 0.
    • columnOffset <number> Especifica o offset do número da coluna da primeira linha que é exibido nos rastros de pilha produzidos por este script. Padrão: 0.
    • displayErrors <boolean> Quando true, se um Error ocorrer durante a compilação do code, a linha de código que causou o erro é anexada ao rastro de pilha. Padrão: true.
    • timeout <integer> Especifica o número de milissegundos para executar code antes de terminar a execução. Se a execução for terminada, um Error será lançado. Este valor deve ser um inteiro estritamente positivo.
    • breakOnSigint <boolean> Se true, receber SIGINT (+) terminará a execução e lançará um Error. Os manipuladores existentes para o evento que foram anexados via process.on('SIGINT') são desabilitados durante a execução do script, mas continuam a funcionar depois disso. Padrão: false.
    • cachedData <Buffer> | <TypedArray> | <DataView> Fornece um Buffer ou TypedArray opcional, ou DataView com os dados do cache de código do V8 para a fonte fornecida.
    • importModuleDynamically <Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Usado para especificar como os módulos devem ser carregados durante a avaliação deste script quando import() é chamado. Esta opção faz parte da API experimental de módulos. Não recomendamos usá-la em um ambiente de produção. Para informações detalhadas, consulte Suporte de import() dinâmico em APIs de compilação.
  • Retorna: <any> o resultado da última declaração executada no script.

vm.runInThisContext() compila code, executa-o no contexto do global atual e retorna o resultado. A execução do código não tem acesso ao escopo local, mas tem acesso ao objeto global atual.

Se options for uma string, então ele especifica o nome do arquivo.

O exemplo a seguir ilustra o uso de vm.runInThisContext() e da função JavaScript eval() para executar o mesmo 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'

Como vm.runInThisContext() não tem acesso ao escopo local, localVar permanece inalterado. Em contraste, eval() tem acesso ao escopo local, portanto o valor localVar é alterado. Desta forma, vm.runInThisContext() é muito parecido com uma chamada indireta de eval(), por exemplo, (0,eval)('code').

Exemplo: Executando um servidor HTTP dentro de uma VM

Quando se usa script.runInThisContext() ou vm.runInThisContext(), o código é executado dentro do contexto global V8 atual. O código passado para este contexto da VM terá seu próprio escopo isolado.

Para executar um servidor web simples usando o módulo node:http, o código passado para o contexto deve chamar require('node:http') por si só, ou ter uma referência ao módulo node:http passada a ele. Por exemplo:

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

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

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

  console.log('Servidor rodando em http://127.0.0.1:8124/');
})`

vm.runInThisContext(code)(require)

O require() no caso acima compartilha o estado com o contexto de onde é passado. Isso pode introduzir riscos quando código não confiável é executado, por exemplo, alterando objetos no contexto de maneiras indesejadas.

O que significa "contextificar" um objeto?

Todo JavaScript executado dentro do Node.js roda dentro do escopo de um "contexto". De acordo com o Guia do Incorporador V8:

Quando o método vm.createContext() é chamado com um objeto, o argumento contextObject será usado para encapsular o objeto global de uma nova instância de um Contexto V8 (se contextObject for undefined, um novo objeto será criado a partir do contexto atual antes de sua contextificação). Este Contexto V8 fornece ao código executado usando os métodos do módulo node:vm um ambiente global isolado dentro do qual ele pode operar. O processo de criação do Contexto V8 e sua associação com o contextObject no contexto externo é o que este documento se refere como "contextificar" o objeto.

A contextificação introduziria algumas peculiaridades ao valor globalThis no contexto. Por exemplo, ele não pode ser congelado e não é referencialmente igual ao contextObject no contexto externo.

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

// Uma opção `contextObject` indefinida torna o objeto global contextificado.
const context = vm.createContext()
console.log(vm.runInContext('globalThis', context) === context) // false
// Um objeto global contextificado não pode ser congelado.
try {
  vm.runInContext('Object.freeze(globalThis);', context)
} catch (e) {
  console.log(e) // TypeError: Cannot freeze
}
console.log(vm.runInContext('globalThis.foo = 1; foo;', context)) // 1

Para criar um contexto com um objeto global comum e obter acesso a um proxy global no contexto externo com menos peculiaridades, especifique vm.constants.DONT_CONTEXTIFY como o argumento contextObject.

vm.constants.DONT_CONTEXTIFY

Esta constante, quando usada como o argumento contextObject nas APIs vm, instrui o Node.js a criar um contexto sem encapsular seu objeto global com outro objeto de maneira específica do Node.js. Como resultado, o valor globalThis dentro do novo contexto se comportaria mais próximo de um comum.

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

// Use vm.constants.DONT_CONTEXTIFY para congelar o 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
}

Quando vm.constants.DONT_CONTEXTIFY é usado como o argumento contextObject para vm.createContext(), o objeto retornado é um objeto semelhante a um proxy para o objeto global no contexto recém-criado com menos peculiaridades específicas do Node.js. É igual por referência ao valor globalThis no novo contexto, pode ser modificado de fora do contexto e pode ser usado para acessar elementos integrados no novo contexto diretamente.

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

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

// O objeto retornado é igual por referência ao globalThis no novo contexto.
console.log(vm.runInContext('globalThis', context) === context) // true

// Pode ser usado para acessar variáveis globais no novo contexto diretamente.
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

// Pode ser congelado e isso afeta o contexto interno.
Object.freeze(context)
try {
  vm.runInContext('baz = 1; baz;', context)
} catch (e) {
  console.log(e) // Uncaught ReferenceError: baz is not defined
}

Interações de tempo limite com tarefas assíncronas e Promises

Promises e async functions podem programar tarefas executadas pelo mecanismo JavaScript de forma assíncrona. Por padrão, essas tarefas são executadas depois que todas as funções JavaScript na pilha atual terminam de executar. Isso permite escapar da funcionalidade das opções timeout e breakOnSigint.

Por exemplo, o seguinte código executado por vm.runInNewContext() com um tempo limite de 5 milissegundos agenda um loop infinito para ser executado após uma promise ser resolvida. O loop agendado nunca é interrompido pelo tempo limite:

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

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

vm.runInNewContext('Promise.resolve().then(() => loop());', { loop, console }, { timeout: 5 })
// Isso é impresso *antes* de 'entering loop' (!)
console.log('done executing')

Isso pode ser resolvido passando microtaskMode: 'afterEvaluate' para o código que cria o 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' }
)

Neste caso, a microtarefa programada por meio de promise.then() será executada antes de retornar de vm.runInNewContext(), e será interrompida pela funcionalidade de timeout. Isso se aplica apenas ao código que está sendo executado em um vm.Context, portanto, por exemplo, vm.runInThisContext() não recebe esta opção.

Os callbacks de Promise são inseridos na fila de microtarefas do contexto em que foram criados. Por exemplo, se () =\> loop() for substituído por apenas loop no exemplo acima, então loop será inserido na fila de microtarefas global, porque é uma função do contexto externo (principal), e, portanto, também poderá escapar do tempo limite.

Se as funções de programação assíncrona, como process.nextTick(), queueMicrotask(), setTimeout(), setImmediate(), etc., estiverem disponíveis dentro de um vm.Context, as funções passadas a elas serão adicionadas a filas globais, que são compartilhadas por todos os contextos. Portanto, os callbacks passados para essas funções também não são controláveis pelo tempo limite.

Suporte de import() dinâmico em APIs de compilação

As APIs a seguir suportam uma opção importModuleDynamically para habilitar import() dinâmico em código compilado pelo módulo vm.

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

Esta opção ainda faz parte da API experimental de módulos. Não recomendamos seu uso em um ambiente de produção.

Quando a opção importModuleDynamically não é especificada ou está indefinida

Se esta opção não for especificada, ou se estiver undefined, o código contendo import() ainda pode ser compilado pelas APIs vm, mas quando o código compilado for executado e realmente chamar import(), o resultado será rejeitado com ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING.

Quando importModuleDynamically é vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER

Esta opção não é atualmente suportada para vm.SourceTextModule.

Com esta opção, quando um import() é iniciado no código compilado, o Node.js usaria o carregador ESM padrão do contexto principal para carregar o módulo solicitado e retorná-lo ao código que está sendo executado.

Isso dá acesso aos módulos integrados do Node.js, como fs ou http, ao código que está sendo compilado. Se o código for executado em um contexto diferente, esteja ciente de que os objetos criados por módulos carregados do contexto principal ainda são do contexto principal e não são instanceof classes integradas no novo 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: URL carregado do contexto principal não é uma instância da classe Function
// no novo 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: URL carregado do contexto principal não é uma instância da classe Function
// no novo contexto.
script.runInNewContext().then(console.log)

Esta opção também permite que o script ou função carregue módulos de usuário:

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

// Escreve test.js e test.txt no diretório onde o script atual
// está sendo executado.
writeFileSync(resolve(import.meta.dirname, 'test.mjs'), 'export const filename = "./test.json";')
writeFileSync(resolve(import.meta.dirname, 'test.json'), '{"hello": "world"}')

// Compila um script que carrega test.mjs e depois test.json
// como se o script estivesse localizado no mesmo diretório.
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')

// Escreve test.js e test.txt no diretório onde o script atual
// está sendo executado.
writeFileSync(resolve(__dirname, 'test.mjs'), 'export const filename = "./test.json";')
writeFileSync(resolve(__dirname, 'test.json'), '{"hello": "world"}')

// Compila um script que carrega test.mjs e depois test.json
// como se o script estivesse localizado no mesmo diretório.
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)

Existem algumas ressalvas ao carregar módulos de usuário usando o carregador padrão do contexto principal:

Quando importModuleDynamically é uma função

Quando importModuleDynamically é uma função, ela será invocada quando import() for chamado no código compilado para que os usuários personalizem como o módulo solicitado deve ser compilado e avaliado. Atualmente, a instância do Node.js deve ser iniciada com a flag --experimental-vm-modules para que essa opção funcione. Se a flag não estiver definida, este callback será ignorado. Se o código avaliado realmente chamar import(), o resultado será rejeitado com ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG.

O callback importModuleDynamically(specifier, referrer, importAttributes) tem a seguinte assinatura:

  • specifier <string> especificador passado para import()
  • referrer <vm.Script> | <Function> | <vm.SourceTextModule> | <Object> O referrer é o vm.Script compilado para new vm.Script, vm.runInThisContext, vm.runInContext e vm.runInNewContext. É a Function compilada para vm.compileFunction, o vm.SourceTextModule compilado para new vm.SourceTextModule e o contexto Object para vm.createContext().
  • importAttributes <Object> O valor "with" passado para o parâmetro opcional optionsExpression, ou um objeto vazio se nenhum valor foi fornecido.
  • Retorna: <Module Namespace Object> | <vm.Module> Retornar um vm.Module é recomendado para aproveitar o rastreamento de erros e evitar problemas com namespaces que contêm exportações de funções then.
js
// Este script deve ser executado com --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) // O 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 deve ser executado com --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) // O 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' } }
})()