Skip to content

VM (выполнение JavaScript)

[Стабильно: 2 - Стабильно]

Стабильно: 2 Стабильность: 2 - Стабильно

Исходный код: lib/vm.js

Модуль node:vm позволяет компилировать и запускать код в контекстах виртуальной машины V8.

Модуль node:vm не является механизмом безопасности. Не используйте его для запуска ненадежного кода.

JavaScript-код можно скомпилировать и запустить немедленно или скомпилировать, сохранить и запустить позже.

Распространенный случай использования — запуск кода в другом контексте V8. Это означает, что вызываемый код имеет другой глобальный объект, чем вызывающий код.

Можно указать контекст, контекстуализировав объект. Вызываемый код обрабатывает любое свойство в контексте как глобальную переменную. Любые изменения глобальных переменных, вызванные вызываемым кодом, отражаются в объекте контекста.

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

const x = 1

const context = { x: 2 }
vm.createContext(context) // Контекстуализация объекта.

const code = 'x += 40; var y = 17;'
// `x` и `y` являются глобальными переменными в контексте.
// Изначально, x имеет значение 2, потому что это значение context.x.
vm.runInContext(code, context)

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

console.log(x) // 1; y не определена.

Класс: vm.Script

Добавлено в: v0.3.1

Экземпляры класса vm.Script содержат предварительно скомпилированные скрипты, которые могут быть выполнены в определенных контекстах.

new vm.Script(code[, options])

[История]

ВерсияИзменения
v21.7.0, v20.12.0Добавлена поддержка vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.
v17.0.0, v16.12.0Добавлена поддержка атрибутов импорта для параметра importModuleDynamically.
v10.6.0produceCachedData устарел в пользу script.createCachedData().
v5.7.0Теперь поддерживаются параметры cachedData и produceCachedData.
v0.3.1Добавлено в: v0.3.1
  • code <строка> JavaScript-код для компиляции.
  • options <Объект> | <строка>
    • filename <строка> Указывает имя файла, используемое в трассировках стека, созданных этим скриптом. По умолчанию: 'evalmachine.\<anonymous\>'.
    • lineOffset <число> Указывает смещение номера строки, отображаемое в трассировках стека, созданных этим скриптом. По умолчанию: 0.
    • columnOffset <число> Указывает смещение номера столбца первой строки, отображаемое в трассировках стека, созданных этим скриптом. По умолчанию: 0.
    • cachedData <Buffer> | <TypedArray> | <DataView> Предоставляет необязательный Buffer или TypedArray, или DataView с данными кэша кода V8 для предоставленного источника. При предоставлении значение cachedDataRejected будет установлено в true или false в зависимости от принятия данных V8.
    • produceCachedData <булево> Если true и нет cachedData, V8 попытается создать данные кэша кода для code. В случае успеха Buffer с данными кэша кода V8 будет создан и сохранен в свойстве cachedData возвращенного экземпляра vm.Script. Значение cachedDataProduced будет установлено в true или false в зависимости от того, были ли данные кэша кода успешно созданы. Этот параметр устарел в пользу script.createCachedData(). По умолчанию: false.
    • importModuleDynamically <Функция> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания способа загрузки модулей во время оценки этого скрипта при вызове import(). Этот параметр является частью экспериментального API модулей. Мы не рекомендуем использовать его в производственной среде. Для получения подробной информации см. Поддержка динамического import() в API компиляции.

Если options является строкой, то она указывает имя файла.

Создание нового объекта vm.Script компилирует code, но не запускает его. Скомпилированный vm.Script может быть запущен позже несколько раз. code не привязан ни к одному глобальному объекту; скорее, он привязывается перед каждым запуском, только для этого запуска.

script.cachedDataRejected

Добавлено в: v5.7.0

Когда cachedData предоставляется для создания vm.Script, это значение будет установлено в true или false в зависимости от принятия данных V8. В противном случае значение равно undefined.

script.createCachedData()

Добавлено в: v10.6.0

Создает кэш кода, который может использоваться с опцией cachedData конструктора Script. Возвращает Buffer. Этот метод может вызываться в любое время и любое количество раз.

Кэш кода Script не содержит никаких наблюдаемых состояний JavaScript. Кэш кода может безопасно сохраняться вместе с исходным кодом сценария и использоваться для создания новых экземпляров Script несколько раз.

Функции в исходном коде Script могут быть помечены как лениво компилируемые, и они не компилируются при создании Script. Эти функции будут скомпилированы при их первом вызове. Кэш кода сериализует метаданные, которые V8 в настоящее время знает о Script, которые он может использовать для ускорения будущих компиляций.

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

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

const cacheWithoutAdd = script.createCachedData()
// В `cacheWithoutAdd` функция `add()` помечена для полной компиляции
// при вызове.

script.runInThisContext()

const cacheWithAdd = script.createCachedData()
// `cacheWithAdd` содержит полностью скомпилированную функцию `add()`.

script.runInContext(contextifiedObject[, options])

[История]

ВерсияИзменения
v6.3.0Теперь поддерживается опция breakOnSigint.
v0.3.1Добавлено в: v0.3.1
  • contextifiedObject <Object> Объект с контекстом, возвращаемый методом vm.createContext().

  • options <Object>

    • displayErrors <boolean> Если значение true, при возникновении ошибки Error во время компиляции code, строка кода, вызывающая ошибку, прикрепляется к трассировке стека. По умолчанию: true.
    • timeout <integer> Указывает количество миллисекунд для выполнения code перед прекращением выполнения. Если выполнение прерывается, будет выброшена ошибка Error. Это значение должно быть строго положительным целым числом.
    • breakOnSigint <boolean> Если true, получение SIGINT (+) приведет к завершению выполнения и выбросу ошибки Error. Существующие обработчики события, которые были прикреплены через process.on('SIGINT'), отключаются во время выполнения скрипта, но продолжают работать после этого. По умолчанию: false.
  • Возвращает: <any> результат последнего выполненного оператора в скрипте.

Выполняет скомпилированный код, содержащийся в объекте vm.Script, в заданном contextifiedObject и возвращает результат. Выполняемый код не имеет доступа к локальной области видимости.

В следующем примере компилируется код, который увеличивает глобальную переменную, устанавливает значение другой глобальной переменной, а затем выполняет код несколько раз. Глобальные переменные содержатся в объекте 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)
// Выводит: { animal: 'cat', count: 12, name: 'kitty' }

Использование опций timeout или breakOnSigint приведет к запуску новых циклов событий и соответствующих потоков, которые имеют ненулевые накладные расходы на производительность.

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

[История]

ВерсияИзменения
v22.8.0, v20.18.0Аргумент contextObject теперь принимает vm.constants.DONT_CONTEXTIFY.
v14.6.0Теперь поддерживается параметр microtaskMode.
v10.0.0Теперь поддерживается параметр contextCodeGeneration.
v6.3.0Теперь поддерживается параметр breakOnSigint.
v0.3.1Добавлено в: v0.3.1
  • contextObject <Object> | <vm.constants.DONT_CONTEXTIFY> | <undefined> Либо vm.constants.DONT_CONTEXTIFY, либо объект, который будет контекстуализирован. Если undefined, для обратной совместимости будет создан пустой контекстуализированный объект.

  • options <Object>

    • displayErrors <boolean> Если true, при возникновении ошибки Error во время компиляции code, строка кода, вызвавшая ошибку, прикрепляется к трассировке стека. По умолчанию: true.

    • timeout <integer> Задает количество миллисекунд для выполнения code перед завершением выполнения. Если выполнение прерывается, будет выброшена ошибка Error. Это значение должно быть строго положительным целым числом.

    • breakOnSigint <boolean> Если true, получение SIGINT (+) приведет к завершению выполнения и выбросу ошибки Error. Существующие обработчики события, которые были прикреплены через process.on('SIGINT'), отключаются во время выполнения скрипта, но продолжают работать после этого. По умолчанию: false.

    • contextName <string> Чёткий для человека, имя вновь созданного контекста. По умолчанию: 'VM Context i', где i — возрастающий числовой индекс созданного контекста.

    • contextOrigin <string> Происхождение , соответствующее вновь созданному контексту для отображения. Происхождение должно быть отформатировано как URL, но только со схемой, хостом и портом (при необходимости), как значение свойства url.origin объекта URL. В частности, эта строка должна опускать косую черту в конце, так как это обозначает путь. По умолчанию: ''.

    • contextCodeGeneration <Object>

    • strings <boolean> Если установлено в false, любые вызовы eval или конструкторы функций (Function, GeneratorFunction и т. д.) выбросят EvalError. По умолчанию: true.

    • wasm <boolean> Если установлено в false, любая попытка скомпилировать модуль WebAssembly выбросит WebAssembly.CompileError. По умолчанию: true.

    • microtaskMode <string> Если установлено в afterEvaluate, микрозадачи (задачи, запланированные с помощью Promise и async function) будут выполнены сразу после выполнения скрипта. В этом случае они включены в области действия timeout и breakOnSigint.

  • Возвращает: <any> результат последнего выполненного оператора в скрипте.

Этот метод является сокращением для script.runInContext(vm.createContext(options), options). Он делает несколько вещей одновременно:

В следующем примере компилируется код, который устанавливает глобальную переменную, а затем выполняет код несколько раз в разных контекстах. Глобальные переменные устанавливаются и содержатся в каждом отдельном context.

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

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

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

console.log(contexts)
// Выведет: [{ globalVar: 'set' }, { globalVar: 'set' }, { globalVar: 'set' }]

// Это вызовет ошибку, если контекст создан из контекстуализированного объекта.
// vm.constants.DONT_CONTEXTIFY позволяет создавать контексты с обычными
// глобальными объектами, которые могут быть заморожены.
const freezeScript = new vm.Script('Object.freeze(globalThis); globalThis;')
const frozenContext = freezeScript.runInNewContext(vm.constants.DONT_CONTEXTIFY)

script.runInThisContext([options])

[История]

ВерсияИзменения
v6.3.0Теперь поддерживается параметр breakOnSigint.
v0.3.1Добавлено в: v0.3.1
  • options <Object>

    • displayErrors <boolean> Если значение true, и при компиляции code возникает ошибка Error, строка кода, вызвавшая ошибку, добавляется к трассировке стека. По умолчанию: true.
    • timeout <integer> Указывает количество миллисекунд для выполнения code перед завершением выполнения. Если выполнение прерывается, будет выброшена ошибка Error. Это значение должно быть строго положительным целым числом.
    • breakOnSigint <boolean> Если true, получение SIGINT (+) завершит выполнение и выбросит ошибку Error. Существующие обработчики события, которые были прикреплены с помощью process.on('SIGINT'), отключаются во время выполнения скрипта, но продолжают работать после этого. По умолчанию: false.
  • Возвращает: <any> результат последнего выполненного в скрипте оператора.

Выполняет скомпилированный код, содержащийся в vm.Script, в контексте текущего глобального объекта global. Выполняемый код не имеет доступа к локальной области видимости, но имеет доступ к текущему глобальному объекту global.

В следующем примере компилируется код, который увеличивает глобальную переменную, а затем выполняет этот код несколько раз:

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

Добавлено в: v19.1.0, v18.13.0

Если скрипт скомпилирован из источника, содержащего комментарий с волшебной строкой source map, это свойство будет установлено в URL source map.

js
import vm from 'node:vm'

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

console.log(script.sourceMapURL)
// Выведет: sourcemap.json
js
const vm = require('node:vm')

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

console.log(script.sourceMapURL)
// Выведет: sourcemap.json

Класс: vm.Module

Добавлено в: v13.0.0, v12.16.0

[Стабильность: 1 - Экспериментальный]

Стабильность: 1 Стабильность: 1 - Экспериментальный

Эта функция доступна только при включенном флаге командной строки --experimental-vm-modules.

Класс vm.Module предоставляет низкоуровневый интерфейс для использования модулей ECMAScript в контекстах VM. Это аналог класса vm.Script, который точно отражает записи модулей, как определено в спецификации ECMAScript.

Однако, в отличие от vm.Script, каждый объект vm.Module привязан к контексту с момента его создания. Операции с объектами vm.Module по своей природе асинхронны, в отличие от синхронной природы объектов vm.Script. Использование функций 'async' может помочь в работе с объектами vm.Module.

Использование объекта vm.Module требует трех отдельных шагов: создание/разбор, компоновка и вычисление. Эти три шага показаны в следующем примере.

Эта реализация находится на более низком уровне, чем загрузчик модулей ECMAScript. Пока нет возможности взаимодействовать с загрузчиком, хотя поддержка планируется.

js
import vm from 'node:vm'

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

// Шаг 1
//
// Создайте модуль, создав новый объект `vm.SourceTextModule`. Это
// разбирает предоставленный исходный текст, вызывая `SyntaxError`, если что-то пойдет
// не так. По умолчанию модуль создается в верхнем контексте. Но здесь мы
// указываем `contextifiedObject` в качестве контекста, которому принадлежит этот модуль.
//
// Здесь мы пытаемся получить экспорт по умолчанию из модуля "foo" и
// поместить его в локальное связывание "secret".

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

// Шаг 2
//
// "Свяжите" импортированные зависимости этого модуля с ним.
//
// Предоставленный callback компоновки (the "linker") принимает два аргумента:
// родительский модуль (`bar` в данном случае) и строку, которая является спецификатором
// импортированного модуля.  Callback должен вернуть модуль,
// соответствующий предоставленному спецификатору, с определенными требованиями, описанными
// в `module.link()`.
//
// Если компоновка не началась для возвращаемого модуля, тот же callback компоновки
// будет вызван для возвращаемого модуля.
//
// Даже модули верхнего уровня без зависимостей должны быть явно связаны. The
// предоставленный callback никогда не будет вызван, однако.
//
// Метод link() возвращает Promise, который будет выполнен, когда все
// Promises, возвращаемые компоновщиком, будут выполнены.
//
// Примечание: это искусственный пример, поскольку функция компоновщика создает новый
// модуль "foo" каждый раз, когда он вызывается. В полноценной системе модулей,
// кэш, вероятно, будет использоваться для избежания дублированных модулей.

async function linker(specifier, referencingModule) {
  if (specifier === 'foo') {
    return new vm.SourceTextModule(
      `
      // Переменная "secret" ссылается на глобальную переменную, которую мы добавили к
      // "contextifiedObject" при создании контекста.
      export default secret;
    `,
      { context: referencingModule.context }
    )

    // Использование `contextifiedObject` вместо `referencingModule.context`
    // здесь также сработает.
  }
  throw new Error(`Невозможно разрешить зависимость: ${specifier}`)
}
await bar.link(linker)

// Шаг 3
//
// Вычислите модуль. Метод evaluate() возвращает promise, который
// будет выполнен после завершения вычисления модуля.

// Выведет 42.
await bar.evaluate()
js
const vm = require('node:vm')

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

;(async () => {
  // Шаг 1
  //
  // Создайте модуль, создав новый объект `vm.SourceTextModule`. Это
  // разбирает предоставленный исходный текст, вызывая `SyntaxError`, если что-то пойдет
  // не так. По умолчанию модуль создается в верхнем контексте. Но здесь мы
  // указываем `contextifiedObject` в качестве контекста, которому принадлежит этот модуль.
  //
  // Здесь мы пытаемся получить экспорт по умолчанию из модуля "foo" и
  // поместить его в локальное связывание "secret".

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

  // Шаг 2
  //
  // "Свяжите" импортированные зависимости этого модуля с ним.
  //
  // Предоставленный callback компоновки (the "linker") принимает два аргумента:
  // родительский модуль (`bar` в данном случае) и строку, которая является спецификатором
  // импортированного модуля.  Callback должен вернуть модуль,
  // соответствующий предоставленному спецификатору, с определенными требованиями, описанными
  // в `module.link()`.
  //
  // Если компоновка не началась для возвращаемого модуля, тот же callback компоновки
  // будет вызван для возвращаемого модуля.
  //
  // Даже модули верхнего уровня без зависимостей должны быть явно связаны. The
  // предоставленный callback никогда не будет вызван, однако.
  //
  // Метод link() возвращает Promise, который будет выполнен, когда все
  // Promises, возвращаемые компоновщиком, будут выполнены.
  //
  // Примечание: это искусственный пример, поскольку функция компоновщика создает новый
  // модуль "foo" каждый раз, когда он вызывается. В полноценной системе модулей,
  // кэш, вероятно, будет использоваться для избежания дублированных модулей.

  async function linker(specifier, referencingModule) {
    if (specifier === 'foo') {
      return new vm.SourceTextModule(
        `
        // Переменная "secret" ссылается на глобальную переменную, которую мы добавили к
        // "contextifiedObject" при создании контекста.
        export default secret;
      `,
        { context: referencingModule.context }
      )

      // Использование `contextifiedObject` вместо `referencingModule.context`
      // здесь также сработает.
    }
    throw new Error(`Невозможно разрешить зависимость: ${specifier}`)
  }
  await bar.link(linker)

  // Шаг 3
  //
  // Вычислите модуль. Метод evaluate() возвращает promise, который
  // будет выполнен после завершения вычисления модуля.

  // Выведет 42.
  await bar.evaluate()
})()

module.dependencySpecifiers

Спецификаторы всех зависимостей этого модуля. Возвращаемый массив заморожен, чтобы запретить любые изменения в нем.

Соответствует полю [[RequestedModules]] записей Циклического модуля в спецификации ECMAScript.

module.error

Если module.status имеет значение 'errored', это свойство содержит исключение, выброшенное модулем во время вычисления. Если статус — что-либо другое, обращение к этому свойству приведет к выбросу исключения.

Значение undefined не может использоваться для случаев, когда исключение не было выброшено, из-за возможной неоднозначности с throw undefined;.

Соответствует полю [[EvaluationError]] записей Циклического модуля в спецификации ECMAScript.

module.evaluate([options])

  • options <Object>

    • timeout <integer> Задает количество миллисекунд для вычисления перед завершением выполнения. Если выполнение прерывается, будет выброшено исключение Error. Это значение должно быть строго положительным целым числом.
    • breakOnSigint <boolean> Если true, получение SIGINT (+) завершит выполнение и выбросит исключение Error. Существующие обработчики события, которые были прикреплены через process.on('SIGINT'), отключаются во время выполнения скрипта, но продолжают работать после этого. По умолчанию: false.
  • Возвращает: <Promise> Выполняется с undefined в случае успеха.

Вычислить модуль.

Это должно быть вызвана после того, как модуль был связан; в противном случае он отклонит. Его также можно вызвать, когда модуль уже был вычислен, в этом случае он либо ничего не будет делать, если первоначальное вычисление завершилось успешно (module.status равно 'evaluated'), либо он перебросит исключение, которое вызвало первоначальное вычисление (module.status равно 'errored').

Этот метод не может быть вызван, пока модуль вычисляется (module.status равно 'evaluating').

Соответствует полю Конкретного метода Evaluate() записей Циклического модуля в спецификации ECMAScript.

module.identifier

Идентификатор текущего модуля, установленный в конструкторе.

module.link(linker)

[История]

ВерсияИзменения
v21.1.0, v20.10.0, v18.19.0Опция extra.assert переименована в extra.attributes. Прежнее имя по-прежнему поддерживается для обратной совместимости.
  • linker <Функция>

    • specifier <строка> Спецификатор запрашиваемого модуля:

    • referencingModule <vm.Module> Объект Module, для которого вызывается метод link().

    • extra <Объект>

    • attributes <Объект> Данные из атрибута: согласно ECMA-262, хосты должны генерировать ошибку, если присутствует неподдерживаемый атрибут.

    • assert <Объект> Псевдоним для extra.attributes.

    • Возвращает: <vm.Module> | <Promise>

  • Возвращает: <Promise>

Связывание зависимостей модулей. Этот метод должен быть вызван перед вычислением и может быть вызван только один раз для каждого модуля.

Функция должна возвращать объект Module или Promise, который в конечном итоге разрешается в объект Module. Возвращаемый Module должен удовлетворять следующим двум инвариантам:

  • Он должен принадлежать тому же контексту, что и родительский Module.
  • Его status не должен быть 'errored'.

Если status возвращаемого Module равен 'unlinked', этот метод будет рекурсивно вызван для возвращаемого Module с той же предоставленной функцией linker.

link() возвращает Promise, который либо разрешается, когда все экземпляры связывания разрешаются в допустимый Module, либо отклоняется, если функция linker вызывает исключение или возвращает недопустимый Module.

Функция linker приблизительно соответствует определяемой реализацией абстрактной операции HostResolveImportedModule в спецификации ECMAScript, с некоторыми ключевыми различиями:

  • Функция linker может быть асинхронной, в то время как HostResolveImportedModule является синхронной.

Фактическая реализация HostResolveImportedModule, используемая во время связывания модулей, возвращает модули, связанные во время связывания. Поскольку к этому моменту все модули уже были бы полностью связаны, реализация HostResolveImportedModule является полностью синхронной согласно спецификации.

Соответствует полю Link() конкретного метода циклических записей модулей в спецификации ECMAScript.

module.namespace

Объект пространства имён модуля. Он доступен только после завершения компоновки (module.link()).

Соответствует абстрактной операции GetModuleNamespace в спецификации ECMAScript.

module.status

Текущий статус модуля. Будет одним из:

  • 'unlinked': module.link() ещё не вызывался.
  • 'linking': module.link() был вызван, но не все Promise, возвращаемые функцией компоновщика, ещё разрешены.
  • 'linked': Модуль был успешно скомпонован, и все его зависимости скомпонованы, но module.evaluate() ещё не вызывался.
  • 'evaluating': Модуль оценивается через module.evaluate() самого себя или родительского модуля.
  • 'evaluated': Модуль был успешно оценен.
  • 'errored': Модуль был оценен, но было выброшено исключение.

За исключением 'errored', эта строка состояния соответствует полю [[Status]] записи Циклический модуль в спецификации. 'errored' соответствует 'evaluated' в спецификации, но с [[EvaluationError]], установленным в значение, отличное от undefined.

Класс: vm.SourceTextModule

Добавлено в: v9.6.0

[Стабильно: 1 - Экспериментально]

Стабильно: 1 Стабильность: 1 - Экспериментально

Эта функция доступна только при включенном флаге командной строки --experimental-vm-modules.

Класс vm.SourceTextModule предоставляет Запись модуля исходного текста, как определено в спецификации ECMAScript.

new vm.SourceTextModule(code[, options])

[История]

ВерсияИзменения
v17.0.0, v16.12.0Добавлена поддержка атрибутов импорта для параметра importModuleDynamically.
  • code <string> Код модуля JavaScript для разбора

  • options

    • identifier <string> Строка, используемая в трассировках стека. По умолчанию: 'vm:module(i)', где i — возрастающий индекс, зависящий от контекста.

    • cachedData <Buffer> | <TypedArray> | <DataView> Предоставляет необязательный Buffer или TypedArray, или DataView с данными кэша кода V8 для предоставленного источника. code должен быть таким же, как и модуль, из которого были созданы эти cachedData.

    • context <Object> Контекстуализированный объект, возвращаемый методом vm.createContext(), для компиляции и оценки этого Module. Если контекст не указан, модуль оценивается в текущем контексте выполнения.

    • lineOffset <integer> Указывает смещение номера строки, отображаемое в трассировках стека, созданных этим Module. По умолчанию: 0.

    • columnOffset <integer> Указывает смещение номера столбца первой строки, отображаемое в трассировках стека, созданных этим Module. По умолчанию: 0.

    • initializeImportMeta <Function> Вызывается во время оценки этого Module для инициализации import.meta.

    • meta <import.meta>

    • module <vm.SourceTextModule>

    • importModuleDynamically <Function> Используется для указания способа загрузки модулей во время оценки этого модуля при вызове import(). Эта опция является частью экспериментального API модулей. Мы не рекомендуем использовать её в производственной среде. Для получения подробной информации см. Поддержка динамического import() в API компиляции.

Создаёт новый экземпляр SourceTextModule.

Свойства, назначенные объекту import.meta, которые являются объектами, могут позволить модулю получать доступ к информации за пределами указанного context. Используйте vm.runInContext(), чтобы создавать объекты в определённом контексте.

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) {
    // Примечание: этот объект создаётся в верхнем контексте. Таким образом,
    // Object.getPrototypeOf(import.meta.prop) указывает на
    // Object.prototype в верхнем контексте, а не в
    // контекстуализированном объекте.
    meta.prop = {}
  },
})
// Поскольку модуль не имеет зависимостей, функция компоновщика никогда не будет вызвана.
await module.link(() => {})
await module.evaluate()

// Теперь Object.prototype.secret будет равен 42.
//
// Чтобы исправить эту проблему, замените
//     meta.prop = {};
// выше на
//     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) {
      // Примечание: этот объект создаётся в верхнем контексте. Таким образом,
      // Object.getPrototypeOf(import.meta.prop) указывает на
      // Object.prototype в верхнем контексте, а не в
      // контекстуализированном объекте.
      meta.prop = {}
    },
  })
  // Поскольку модуль не имеет зависимостей, функция компоновщика никогда не будет вызвана.
  await module.link(() => {})
  await module.evaluate()
  // Теперь Object.prototype.secret будет равен 42.
  //
  // Чтобы исправить эту проблему, замените
  //     meta.prop = {};
  // выше на
  //     meta.prop = vm.runInContext('{}', contextifiedObject);
})()

sourceTextModule.createCachedData()

Добавлено в: v13.7.0, v12.17.0

Создает кэш кода, который может использоваться с опцией cachedData конструктора SourceTextModule. Возвращает Buffer. Этот метод может вызываться любое количество раз до того, как модуль будет оценен.

Кэш кода SourceTextModule не содержит никаких наблюдаемых состояний JavaScript. Кэш кода можно безопасно сохранять вместе с исходным кодом скрипта и использовать для многократного создания новых экземпляров SourceTextModule.

Функции в исходном коде SourceTextModule могут быть помечены как лениво компилируемые, и они не компилируются при создании SourceTextModule. Эти функции будут компилироваться при их первом вызове. Кэш кода сериализует метаданные, которые V8 в настоящее время знает о SourceTextModule, которые он может использовать для ускорения будущих компиляций.

js
// Создаем начальный модуль
const module = new vm.SourceTextModule('const a = 1;')

// Создаем кэшированные данные из этого модуля
const cachedData = module.createCachedData()

// Создаем новый модуль, используя кэшированные данные. Код должен быть тем же.
const module2 = new vm.SourceTextModule('const a = 1;', { cachedData })

Класс: vm.SyntheticModule

Добавлено в: v13.0.0, v12.16.0

[Стабильно: 1 - Экспериментально]

Стабильно: 1 Стабильность: 1 - Экспериментально

Эта функция доступна только при включенном флаге командной строки --experimental-vm-modules.

Класс vm.SyntheticModule предоставляет запись синтетического модуля, как определено в спецификации WebIDL. Цель синтетических модулей — предоставить универсальный интерфейс для предоставления источников, отличных от JavaScript, графам модулей 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)
})

// Использование `module` при компоновке...

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

Добавлено в: v13.0.0, v12.16.0

  • exportNames <string[]> Массив имен, которые будут экспортированы из модуля.
  • evaluateCallback <Function> Вызывается при вычислении модуля.
  • options
    • identifier <string> Строка, используемая в трассировках стека. По умолчанию: 'vm:module(i)', где i — индекс возрастания, зависящий от контекста.
    • context <Object> Контекстуализированный объект, возвращаемый методом vm.createContext(), для компиляции и вычисления этого Module.

Создает новый экземпляр SyntheticModule.

Объекты, назначенные экспорту этого экземпляра, могут позволить импортерам модуля получать доступ к информации за пределами указанного context. Используйте vm.runInContext(), чтобы создавать объекты в определенном контексте.

syntheticModule.setExport(name, value)

Добавлено в: v13.0.0, v12.16.0

  • name <string> Имя экспорта для установки.
  • value <any> Значение, которое нужно установить для экспорта.

Этот метод используется после связывания модуля для установки значений экспорта. Если он вызывается до связывания модуля, будет выброшена ошибка ERR_VM_MODULE_STATUS.

js
import vm from 'node:vm'

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

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

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

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

[История]

ВерсияИзменения
v21.7.0, v20.12.0Добавлена поддержка vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.
v19.6.0, v18.15.0Возвращаемое значение теперь включает cachedDataRejected с той же семантикой, что и версия vm.Script, если был передан параметр cachedData.
v17.0.0, v16.12.0Добавлена поддержка атрибутов импорта для параметра importModuleDynamically.
v15.9.0Опция importModuleDynamically добавлена снова.
v14.3.0Удаление importModuleDynamically из-за проблем совместимости.
v14.1.0, v13.14.0Теперь поддерживается опция importModuleDynamically.
v10.10.0Добавлено в: v10.10.0
  • code <string> Тело функции для компиляции.

  • params <string[]> Массив строк, содержащий все параметры для функции.

  • options <Object>

    • filename <string> Указывает имя файла, используемое в трассировках стека, создаваемых этим скриптом. По умолчанию: ''.
    • lineOffset <number> Указывает смещение номера строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию: 0.
    • columnOffset <number> Указывает смещение номера столбца первой строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию: 0.
    • cachedData <Buffer> | <TypedArray> | <DataView> Предоставляет необязательный Buffer или TypedArray, или DataView с данными кэша кода V8 для предоставленного источника. Он должен быть создан предыдущим вызовом vm.compileFunction() с тем же code и params.
    • produceCachedData <boolean> Указывает, следует ли создавать новые данные кэша. По умолчанию: false.
    • parsingContext <Object> Контекстуализированный объект, в котором должна быть скомпилирована указанная функция.
    • contextExtensions <Object[]> Массив, содержащий набор расширений контекста (объекты, инкапсулирующие текущую область видимости), которые должны быть применены во время компиляции. По умолчанию: [].
  • importModuleDynamically <Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания способа загрузки модулей во время вычисления этой функции, когда вызывается import(). Эта опция является частью экспериментального API модулей. Мы не рекомендуем использовать её в производственной среде. Для получения подробной информации см. Поддержка динамического import() в API компиляции.

  • Возвращает: <Function>

Компилирует заданный код в предоставленный контекст (если контекст не предоставлен, используется текущий контекст) и возвращает его, заключённым в функцию с заданными params.

vm.constants

Добавлен в: v21.7.0, v20.12.0

Возвращает объект, содержащий часто используемые константы для операций VM.

vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER

Добавлен в: v21.7.0, v20.12.0

[Стабильно: 1 - Экспериментально]

Стабильно: 1 Стабильность: 1.1 - Активная разработка

Константа, которая может использоваться в качестве параметра importModuleDynamically для vm.Script и vm.compileFunction(), чтобы Node.js использовал загрузчик ESM по умолчанию из основного контекста для загрузки запрошенного модуля.

Для получения подробной информации см. Поддержка динамического import() в API компиляции.

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

[История]

ВерсияИзменения
v22.8.0, v20.18.0Аргумент contextObject теперь принимает vm.constants.DONT_CONTEXTIFY.
v21.7.0, v20.12.0Добавлена поддержка vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.
v21.2.0, v20.11.0Теперь поддерживается параметр importModuleDynamically.
v14.6.0Теперь поддерживается параметр microtaskMode.
v10.0.0Первый аргумент больше не может быть функцией.
v10.0.0Теперь поддерживается параметр codeGeneration.
v0.3.1Добавлено в: v0.3.1
  • contextObject <Object> | <vm.constants.DONT_CONTEXTIFY> | <undefined> Либо vm.constants.DONT_CONTEXTIFY, либо объект, который будет контекстуализирован. Если undefined, для обратной совместимости будет создан пустой контекстуализированный объект.

  • options <Object>

    • name <string> Чёткие имя нового созданного контекста. По умолчанию: 'VM Context i', где i — возрастающий числовой индекс созданного контекста.

    • origin <string> Источник, соответствующий вновь созданному контексту для отображения. Источник должен быть отформатирован как URL, но только со схемой, хостом и портом (при необходимости), как значение свойства url.origin объекта URL. В частности, эта строка должна опускать конечный слэш, так как он обозначает путь. По умолчанию: ''.

    • codeGeneration <Object>

    • strings <boolean> Если установлено в false, любые вызовы eval или конструкторов функций (Function, GeneratorFunction и т. д.) будут вызывать EvalError. По умолчанию: true.

    • wasm <boolean> Если установлено в false, любая попытка скомпилировать модуль WebAssembly вызовет WebAssembly.CompileError. По умолчанию: true.

    • microtaskMode <string> Если установлено в afterEvaluate, микрозадачи (задачи, запланированные через Promise и async function) будут выполнены сразу после того, как скрипт будет выполнен через script.runInContext(). В этом случае они включены в области действия timeout и breakOnSigint.

    • importModuleDynamically <Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания способа загрузки модулей, когда import() вызывается в этом контексте без скрипта или модуля ссылки. Этот параметр является частью экспериментального API модулей. Мы не рекомендуем использовать его в производственной среде. Для получения подробной информации см. Поддержка динамического import() в API компиляции.

  • Возвращает: <Object> контекстуализированный объект.

Если заданный contextObject является объектом, метод vm.createContext() подготовит этот объект и вернет ссылку на него, чтобы его можно было использовать в вызовах vm.runInContext() или script.runInContext(). Внутри таких скриптов глобальный объект будет обернут contextObject, сохраняя все его существующие свойства, но также имея встроенные объекты и функции, которые есть у любого стандартного глобального объекта. За пределами скриптов, запускаемых модулем vm, глобальные переменные останутся неизменными.

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

global.globalVar = 3

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

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

console.log(context)
// Выведет: { globalVar: 2 }

console.log(global.globalVar)
// Выведет: 3

Если contextObject опущен (или передан явно как undefined), будет возвращен новый, пустой контекстуализированный объект.

Когда глобальный объект в вновь созданном контексте контекстуализирован, он имеет некоторые особенности по сравнению с обычными глобальными объектами. Например, его нельзя заморозить. Чтобы создать контекст без особенностей контекстуализации, передайте vm.constants.DONT_CONTEXTIFY в качестве аргумента contextObject. Подробности см. в документации по vm.constants.DONT_CONTEXTIFY.

Метод vm.createContext() в основном полезен для создания одного контекста, который можно использовать для запуска нескольких скриптов. Например, при эмуляции веб-браузера этот метод можно использовать для создания одного контекста, представляющего глобальный объект окна, а затем запускать все теги \<script\> вместе в этом контексте.

Предоставленные name и origin контекста отображаются через API инспектора.

vm.isContext(object)

Добавлено в: v0.11.7

Возвращает true, если заданный объект object был контекстуализирован с помощью vm.createContext() или если это глобальный объект контекста, созданного с помощью vm.constants.DONT_CONTEXTIFY.

vm.measureMemory([options])

Добавлено в: v13.10.0

[Стабильность: 1 - Экспериментально]

Стабильность: 1 Стабильность: 1 - Экспериментально

Измеряет объем памяти, известный V8 и используемый всеми контекстами, известными текущему изоляту V8, или основным контекстом.

  • options <Object> Необязательно.

    • mode <string> Либо 'summary', либо 'detailed'. В режиме summary возвращается только память, измеренная для основного контекста. В подробном режиме возвращается память, измеренная для всех контекстов, известных текущему изоляту V8. По умолчанию: 'summary'
    • execution <string> Либо 'default', либо 'eager'. При выполнении по умолчанию promise не разрешается до начала следующей запланированной сборки мусора, что может занять некоторое время (или никогда, если программа завершится до следующей сборки мусора). При усердном выполнении сборка мусора будет запущена немедленно для измерения памяти. По умолчанию: 'default'
  • Возвращает: <Promise> Если память успешно измерена, promise разрешается с объектом, содержащим информацию об использовании памяти. В противном случае он отклоняется с ошибкой ERR_CONTEXT_NOT_INITIALIZED.

Формат объекта, который может разрешить возвращенный Promise, специфичен для движка V8 и может изменяться от одной версии V8 к другой.

Возвращаемый результат отличается от статистики, возвращаемой v8.getHeapSpaceStatistics(), тем, что vm.measureMemory() измеряет память, доступную для каждого контекста, специфичного для V8, в текущем экземпляре движка V8, тогда как результат v8.getHeapSpaceStatistics() измеряет память, занимаемую каждым пространством кучи в текущем экземпляре V8.

js
const vm = require('node:vm')
// Измерение памяти, используемой основным контекстом.
vm.measureMemory({ mode: 'summary' })
  // Это то же самое, что и vm.measureMemory()
  .then(result => {
    // Текущий формат:
    // {
    //   total: {
    //      jsMemoryEstimate: 2418479, jsMemoryRange: [ 2418479, 2745799 ]
    //    }
    // }
    console.log(result)
  })

const context = vm.createContext({ a: 1 })
vm.measureMemory({ mode: 'detailed', execution: 'eager' }).then(result => {
  // Ссылка на контекст здесь, чтобы он не был очищен сборщиком мусора
  // до завершения измерения.
  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])

[История]

ВерсияИзменения
v21.7.0, v20.12.0Добавлена поддержка vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.
v17.0.0, v16.12.0Добавлена поддержка атрибутов импорта для параметра importModuleDynamically.
v6.3.0Теперь поддерживается параметр breakOnSigint.
v0.3.1Добавлено в: v0.3.1
  • code <строка> JavaScript-код для компиляции и выполнения.
  • contextifiedObject <Объект> Контекстуализированный объект, который будет использоваться в качестве global при компиляции и выполнении code.
  • options <Объект> | <строка>
    • filename <строка> Указывает имя файла, используемое в трассировках стека, создаваемых этим скриптом. По умолчанию: 'evalmachine.<anonymous>'.
    • lineOffset <число> Указывает смещение номера строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию: 0.
    • columnOffset <число> Указывает смещение номера столбца первой строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию: 0.
    • displayErrors <булево> Если true, то при возникновении ошибки Error во время компиляции code, строка кода, вызвавшая ошибку, прикрепляется к трассировке стека. По умолчанию: true.
    • timeout <целое число> Указывает количество миллисекунд для выполнения code перед прекращением выполнения. Если выполнение прерывается, будет выброшена ошибка Error. Это значение должно быть строго положительным целым числом.
    • breakOnSigint <булево> Если true, получение SIGINT (+) приведет к прекращению выполнения и выбросу ошибки Error. Существующие обработчики события, прикрепленные через process.on('SIGINT'), отключаются во время выполнения скрипта, но продолжают работать после этого. По умолчанию: false.
    • cachedData <Buffer> | <TypedArray> | <DataView> Предоставляет необязательный Buffer или TypedArray, или DataView с данными кэша кода V8 для предоставленного источника.
    • importModuleDynamically <Функция> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания способа загрузки модулей во время оценки этого скрипта при вызове import(). Этот параметр является частью экспериментального API модулей. Мы не рекомендуем использовать его в рабочей среде. Для подробной информации см. Поддержка динамического import() в API компиляции.

Метод vm.runInContext() компилирует code, выполняет его в контексте contextifiedObject, а затем возвращает результат. Выполняемый код не имеет доступа к локальной области видимости. Объект contextifiedObject должен быть предварительно контекстуализирован с помощью метода vm.createContext().

Если options является строкой, то она указывает имя файла.

В следующем примере компилируются и выполняются различные скрипты с использованием одного контекстуализированного объекта:

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)
// Выведет: { globalVar: 1024 }

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

[История]

ВерсияИзменения
v22.8.0, v20.18.0Аргумент contextObject теперь принимает vm.constants.DONT_CONTEXTIFY.
v21.7.0, v20.12.0Добавлена поддержка vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.
v17.0.0, v16.12.0Добавлена поддержка атрибутов импорта для параметра importModuleDynamically.
v14.6.0Теперь поддерживается параметр microtaskMode.
v10.0.0Теперь поддерживается параметр contextCodeGeneration.
v6.3.0Теперь поддерживается параметр breakOnSigint.
v0.3.1Добавлено в: v0.3.1
  • code <строка> JavaScript-код для компиляции и выполнения.

  • contextObject <Объект> | <vm.constants.DONT_CONTEXTIFY> | <неопределено> Либо vm.constants.DONT_CONTEXTIFY, либо объект, который будет контекстуализирован. Если undefined, для обратной совместимости будет создан пустой контекстуализированный объект.

  • options <Объект> | <строка>

    • filename <строка> Указывает имя файла, используемое в трассировках стека, создаваемых этим скриптом. По умолчанию: 'evalmachine.\<anonymous\>'.

    • lineOffset <число> Указывает смещение номера строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию: 0.

    • columnOffset <число> Указывает смещение номера столбца первой строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию: 0.

    • displayErrors <булево> Если true, при возникновении ошибки Error во время компиляции code, строка кода, вызвавшая ошибку, добавляется к трассировке стека. По умолчанию: true.

    • timeout <целое число> Указывает количество миллисекунд для выполнения code перед завершением выполнения. Если выполнение прерывается, будет выброшена ошибка Error. Это значение должно быть строго положительным целым числом.

    • breakOnSigint <булево> Если true, получение SIGINT (+) приведет к завершению выполнения и выбросу ошибки Error. Существующие обработчики события, присоединенные через process.on('SIGINT'), отключаются во время выполнения скрипта, но продолжают работать после этого. По умолчанию: false.

    • contextName <строка> Чёткие имя недавно созданного контекста. По умолчанию: 'VM Context i', где i - возрастающий числовой индекс созданного контекста.

    • contextOrigin <строка> Происхождение, соответствующее недавно созданному контексту для отображения. Происхождение должно быть отформатировано как URL, но только со схемой, хостом и портом (при необходимости), как значение свойства url.origin объекта URL. Важно отметить, что эта строка должна опускать конечный слэш, так как он обозначает путь. По умолчанию: ''.

    • contextCodeGeneration <Объект>

    • strings <булево> Если установлено в false, любые вызовы eval или конструкторы функций (Function, GeneratorFunction и т. д.) выбросят EvalError. По умолчанию: true.

    • wasm <булево> Если установлено в false, любая попытка скомпилировать модуль WebAssembly выбросит WebAssembly.CompileError. По умолчанию: true.

    • cachedData <Buffer> | <TypedArray> | <DataView> Предоставляет необязательный Buffer или TypedArray, или DataView с данными кэша кода V8 для предоставленного источника.

    • importModuleDynamically <Функция> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания того, как должны загружаться модули во время вычисления этого скрипта, когда вызывается import(). Этот параметр является частью экспериментального API модулей. Мы не рекомендуем использовать его в рабочей среде. Для получения подробной информации см. Поддержка динамического import() в API компиляции.

    • microtaskMode <строка> Если установлено в afterEvaluate, микрозадачи (задачи, запланированные через Promise и async function) будут выполняться сразу после выполнения скрипта. В этом случае они включены в области действия timeout и breakOnSigint.

  • Возвращает: <любое> результат последнего выполненного оператора в скрипте.

Этот метод является сокращением для (new vm.Script(code, options)).runInContext(vm.createContext(options), options). Если options является строкой, то она указывает имя файла.

Он делает несколько вещей одновременно:

В следующем примере компилируется и выполняется код, который увеличивает глобальную переменную и устанавливает новую. Эти глобальные переменные содержатся в contextObject.

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

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

vm.runInNewContext('count += 1; name = "kitty"', contextObject)
console.log(contextObject)
// Выведет: { animal: 'cat', count: 3, name: 'kitty' }

// Это вызовет ошибку, если контекст создан из контекстуализированного объекта.
// vm.constants.DONT_CONTEXTIFY позволяет создавать контексты с обычными глобальными объектами, которые
// могут быть заморожены.
const frozenContext = vm.runInNewContext('Object.freeze(globalThis); globalThis;', vm.constants.DONT_CONTEXTIFY)

vm.runInThisContext(code[, options])

[История]

ВерсияИзменения
v21.7.0, v20.12.0Добавлена поддержка vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.
v17.0.0, v16.12.0Добавлена поддержка атрибутов импорта для параметра importModuleDynamically.
v6.3.0Теперь поддерживается опция breakOnSigint.
v0.3.1Добавлено в: v0.3.1
  • code <строка> JavaScript-код для компиляции и выполнения.

  • options <Объект> | <строка>

    • filename <строка> Указывает имя файла, используемое в трассировках стека, создаваемых этим скриптом. По умолчанию: 'evalmachine.\<anonymous\>'.
    • lineOffset <число> Указывает смещение номера строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию: 0.
    • columnOffset <число> Указывает смещение номера столбца первой строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию: 0.
    • displayErrors <булево> Если значение true, то при возникновении ошибки Error во время компиляции code, строка кода, вызывающая ошибку, прикрепляется к трассировке стека. По умолчанию: true.
    • timeout <целое число> Указывает количество миллисекунд для выполнения code перед прекращением выполнения. Если выполнение прерывается, будет выброшена ошибка Error. Это значение должно быть строго положительным целым числом.
    • breakOnSigint <булево> Если true, получение SIGINT (+) приведет к прекращению выполнения и выбросу ошибки Error. Существующие обработчики события, прикрепленные с помощью process.on('SIGINT'), отключаются во время выполнения скрипта, но продолжают работать после этого. По умолчанию: false.
    • cachedData <Buffer> | <TypedArray> | <DataView> Предоставляет необязательный Buffer или TypedArray, или DataView с данными кэша кода V8 для предоставленного источника.
    • importModuleDynamically <Функция> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания способа загрузки модулей во время оценки этого скрипта при вызове import(). Эта опция является частью экспериментального API модулей. Мы не рекомендуем использовать её в производственной среде. Для подробной информации см. Поддержка динамического import() в API компиляции.
  • Возвращает: <любое> результат последнего выполненного оператора в скрипте.

vm.runInThisContext() компилирует code, выполняет его в контексте текущего global и возвращает результат. Выполняемый код не имеет доступа к локальной области видимости, но имеет доступ к текущему объекту global.

Если options является строкой, то она указывает имя файла.

В следующем примере показано использование как vm.runInThisContext(), так и функции JavaScript eval() для выполнения одного и того же кода:

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

const vmResult = vm.runInThisContext('localVar = "vm";')
console.log(`vmResult: '${vmResult}', localVar: '${localVar}'`)
// Выведет: vmResult: 'vm', localVar: 'initial value'

const evalResult = eval('localVar = "eval";')
console.log(`evalResult: '${evalResult}', localVar: '${localVar}'`)
// Выведет: evalResult: 'eval', localVar: 'eval'

Поскольку vm.runInThisContext() не имеет доступа к локальной области видимости, localVar не изменяется. В отличие от этого, eval() имеет доступ к локальной области видимости, поэтому значение localVar изменяется. Таким образом, vm.runInThisContext() очень похож на косвенный вызов eval(), например, (0,eval)('code').

Пример: Запуск HTTP-сервера внутри виртуальной машины

При использовании script.runInThisContext() или vm.runInThisContext() код выполняется в текущем глобальном контексте V8. Код, передаваемый в этот контекст VM, будет иметь свою собственную изолированную область видимости.

Для запуска простого веб-сервера с использованием модуля node:http код, передаваемый в контекст, должен либо сам вызвать require('node:http'), либо иметь ссылку на модуль node:http, переданную ему. Например:

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('Server running at http://127.0.0.1:8124/');
})`

vm.runInThisContext(code)(require)

require() в данном случае разделяет состояние с контекстом, из которого он передаётся. Это может привести к рискам при выполнении ненадежного кода, например, к нежелательному изменению объектов в контексте.

Что означает «контекстуализация» объекта?

Весь JavaScript, выполняемый в Node.js, работает в рамках «контекста». Согласно Руководству разработчика V8:

Когда метод vm.createContext() вызывается с объектом, аргумент contextObject будет использоваться для обертывания глобального объекта нового экземпляра контекста V8 (если contextObject равен undefined, новый объект будет создан из текущего контекста перед его контекстуализацией). Этот контекст V8 предоставляет коду, выполняемому с помощью методов модуля node:vm, изолированную глобальную среду, в которой он может работать. Процесс создания контекста V8 и его связывания с contextObject во внешнем контексте — это то, что в данном документе называется «контекстуализацией» объекта.

Контекстуализация внесёт некоторые особенности в значение globalThis в контексте. Например, его нельзя заморозить, и он не является ссылочно равным contextObject во внешнем контексте.

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

// Неопределённый параметр `contextObject` контекстуализирует глобальный объект.
const context = vm.createContext()
console.log(vm.runInContext('globalThis', context) === context) // false
// Контекстуализированный глобальный объект нельзя заморозить.
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

Для создания контекста с обычным глобальным объектом и получения доступа к глобальному прокси во внешнем контексте с меньшим количеством особенностей укажите vm.constants.DONT_CONTEXTIFY в качестве аргумента contextObject.

vm.constants.DONT_CONTEXTIFY

Эта константа, используемая в качестве аргумента contextObject в API vm, предписывает Node.js создавать контекст без обертывания его глобального объекта другим объектом специфическим для Node.js образом. В результате значение globalThis внутри нового контекста будет вести себя более похоже на обычное.

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

// Используйте vm.constants.DONT_CONTEXTIFY для замораживания глобального объекта.
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
}

Когда vm.constants.DONT_CONTEXTIFY используется в качестве аргумента contextObject для vm.createContext(), возвращаемый объект представляет собой похожий на прокси объект для глобального объекта в вновь созданном контексте с меньшим количеством специфичных для Node.js особенностей. Он ссылочно эквивалентен значению globalThis в новом контексте, может быть изменен извне контекста и может использоваться для прямого доступа к встроенным функциям в новом контексте.

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

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

// Возвращаемый объект ссылочно эквивалентен globalThis в новом контексте.
console.log(vm.runInContext('globalThis', context) === context) // true

// Может использоваться для прямого доступа к глобальным переменным в новом контексте.
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

// Может быть заморожен, и это влияет на внутренний контекст.
Object.freeze(context)
try {
  vm.runInContext('baz = 1; baz;', context)
} catch (e) {
  console.log(e) // Uncaught ReferenceError: baz is not defined
}

Взаимодействие таймаута с асинхронными задачами и Promise

Promise и async function могут планировать задачи, выполняемые движком JavaScript асинхронно. По умолчанию эти задачи выполняются после завершения выполнения всех функций JavaScript в текущем стеке. Это позволяет обойти функциональность параметров timeout и breakOnSigint.

Например, следующий код, выполненный vm.runInNewContext() с таймаутом 5 миллисекунд, планирует бесконечный цикл для выполнения после разрешения promise. Планируемый цикл никогда не прерывается таймаутом:

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 })
// Это выводится *до* 'entering loop' (!)
console.log('done executing')

Это можно исправить, передав microtaskMode: 'afterEvaluate' в код, который создает 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' }
)

В этом случае микрозадача, запланированная через promise.then(), будет выполнена до возврата из vm.runInNewContext() и будет прервана функциональностью timeout. Это относится только к коду, выполняющемуся в vm.Context, поэтому, например, vm.runInThisContext() не принимает этот параметр.

Обратные вызовы Promise помещаются в очередь микрозадач контекста, в котором они были созданы. Например, если () => loop() заменить просто на loop в приведенном выше примере, то loop будет помещен в глобальную очередь микрозадач, поскольку это функция из внешнего (главного) контекста, и, таким образом, также сможет избежать таймаута.

Если асинхронные функции планирования, такие как process.nextTick(), queueMicrotask(), setTimeout(), setImmediate() и т. д., становятся доступными внутри vm.Context, функции, переданные им, будут добавлены в глобальные очереди, которые являются общими для всех контекстов. Поэтому обратные вызовы, переданные этим функциям, также не контролируются с помощью таймаута.

Поддержка динамического import() в API компиляции

Следующие API поддерживают опцию importModuleDynamically для включения динамического import() в коде, скомпилированном модулем vm.

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

Эта опция все еще является частью экспериментального API модулей. Мы не рекомендуем использовать ее в production-окружении.

Когда опция importModuleDynamically не указана или не определена

Если эта опция не указана или имеет значение undefined, код, содержащий import(), все еще может быть скомпилирован API vm, но когда скомпилированный код выполняется и фактически вызывает import(), результат будет отклонен с ошибкой ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING.

Когда importModuleDynamically равно vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER

Эта опция в настоящее время не поддерживается для vm.SourceTextModule.

С этой опцией, когда import() инициируется в скомпилированном коде, Node.js будет использовать загрузчик ESM по умолчанию из основного контекста для загрузки запрошенного модуля и возврата его в выполняемый код.

Это обеспечивает доступ к встроенным модулям Node.js, таким как fs или http, для компилируемого кода. Если код выполняется в другом контексте, помните, что объекты, созданные модулями, загруженными из основного контекста, по-прежнему принадлежат основному контексту и не являются instanceof встроенных классов в новом контексте.

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, загруженный из основного контекста, не является экземпляром класса Function
// в новом контексте.
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, загруженный из основного контекста, не является экземпляром класса Function
// в новом контексте.
script.runInNewContext().then(console.log)

Эта опция также позволяет скрипту или функции загружать пользовательские модули:

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

// Запись test.js и test.txt в директорию, где выполняется текущий скрипт.
writeFileSync(resolve(import.meta.dirname, 'test.mjs'), 'export const filename = "./test.json";')
writeFileSync(resolve(import.meta.dirname, 'test.json'), '{"hello": "world"}')

// Компиляция скрипта, который загружает test.mjs, а затем test.json
// как если бы скрипт находился в той же директории.
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')

// Запись test.js и test.txt в директорию, где выполняется текущий скрипт.
writeFileSync(resolve(__dirname, 'test.mjs'), 'export const filename = "./test.json";')
writeFileSync(resolve(__dirname, 'test.json'), '{"hello": "world"}')

// Компиляция скрипта, который загружает test.mjs, а затем test.json
// как если бы скрипт находился в той же директории.
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)

Существует несколько предостережений при загрузке пользовательских модулей с помощью загрузчика по умолчанию из основного контекста:

Когда importModuleDynamically — функция

Когда importModuleDynamically является функцией, она вызывается при вызове import() в скомпилированном коде, позволяя пользователям настраивать компиляцию и оценку запрашиваемого модуля. В настоящее время для работы этой опции экземпляр Node.js должен запускаться с флагом --experimental-vm-modules. Если флаг не установлен, этот обратный вызов игнорируется. Если оцененный код фактически вызывает import(), результат завершится с ошибкой ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG.

Обратный вызов importModuleDynamically(specifier, referrer, importAttributes) имеет следующую сигнатуру:

  • specifier <string> спецификатор, переданный в import()
  • referrer <vm.Script> | <Function> | <vm.SourceTextModule> | <Object> Ссылка — это скомпилированный vm.Script для new vm.Script, vm.runInThisContext, vm.runInContext и vm.runInNewContext. Это скомпилированная Function для vm.compileFunction, скомпилированный vm.SourceTextModule для new vm.SourceTextModule и контекстный Object для vm.createContext().
  • importAttributes <Object> Значение "with", переданное в необязательный параметр optionsExpression, или пустой объект, если значение не было предоставлено.
  • Возвращаемое значение: <Module Namespace Object> | <vm.Module> Рекомендуется возвращать vm.Module, чтобы воспользоваться преимуществами отслеживания ошибок и избежать проблем с пространствами имен, содержащими экспортируемые функции then.
js
// Этот скрипт должен запускаться с --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) // Скомпилированный скрипт
    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
// Этот скрипт должен запускаться с --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) // Скомпилированный скрипт
      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' } }
})()