Spec-Zone.ru › Node.js 20 LTS

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

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

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

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

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

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

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

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

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

const x = 1;

const context = { x: 2 };
vm.createContext(context); // Contextify the object.

const code = 'x += 40; var y = 17;';
// `x` and `y` are global variables in the context.
// Initially, x has the value 2 because that is the value of context.x.
vm.runInContext(code, context);

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

console.log(x); // 1; y is not defined. copy

Класс: vm.Script

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

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

new vm.Script(code[, options])

История
Версия Изменения
v20.12.0

Добавлена поддержка vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.

v17.0.0, v16.12.0

Добавлена поддержка атрибутов импорта для параметра importModuleDynamically.

v10.6.0

Параметр produceCachedData объявлен устаревшим в пользу script.createCachedData().

v5.7.0

Теперь поддерживаются параметры cachedData и produceCachedData.

v0.3.1

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

  • code <string> Код JavaScript для компиляции.
  • options <Object> | <string>
    • filename <string> Задаёт имя файла, используемое в трассировках стека, создаваемых этим скриптом. По умолчанию: 'evalmachine.<anonymous>'.
    • lineOffset <number> Задаёт смещение номера строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию: 0.
    • columnOffset <number> Задаёт смещение номера столбца первой строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию: 0.
    • cachedData <Buffer> | <TypedArray> | <DataView> Предоставляет необязательный Buffer или TypedArray либо DataView с данными кэша кода V8 для указанного исходного кода. Если значение задано, свойству cachedDataRejected будет присвоено значение true или false в зависимости от того, примет ли V8 эти данные.
    • produceCachedData <boolean> Если задано значение true и параметр cachedData отсутствует, V8 попытается создать данные кэша кода для code. В случае успеха будет создан Buffer с данными кэша кода V8, который будет сохранён в свойстве cachedData возвращённого экземпляра vm.Script. Свойству cachedDataProduced будет присвоено значение true или false в зависимости от того, удалось ли создать данные кэша кода. Этот параметр устарел; вместо него следует использовать script.createCachedData(). По умолчанию: false.
    • importModuleDynamically <Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания способа загрузки модулей при вычислении этого скрипта, если вызывается import(). Этот параметр является частью экспериментального API модулей. Не рекомендуется использовать его в рабочей среде. Подробную информацию см. в разделе Поддержка динамического import() в API компиляции.

Если options — строка, она задаёт имя файла.

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

script.cachedDataRejected

Добавлено в: v5.7.0
  • <boolean> | <undefined>

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

script.createCachedData()

Добавлено в: v10.6.0
  • Возвращает: <Buffer>

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

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

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

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

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

const cacheWithoutAdd = script.createCachedData();
// In `cacheWithoutAdd` the function `add()` is marked for full compilation
// upon invocation.

script.runInThisContext();

const cacheWithAdd = script.createCachedData();
// `cacheWithAdd` contains fully compiled function `add()`. copy

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 (Ctrl+C) приведёт к завершению выполнения и выбрасыванию Error. Обработчики события, добавленные ранее с помощью process.on('SIGINT'), отключаются на время выполнения скрипта, но продолжают работать после его завершения. По умолчанию: false.
  • Возвращает: <any> результат выполнения последнего оператора скрипта.

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

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

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

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

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

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

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

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

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

История
Версия Изменения
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 (Ctrl+C) приведёт к завершению выполнения и выбрасыванию 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). Он выполняет сразу несколько действий:

  1. Создаёт новый контекст.
  2. Если contextObject — объект, он контекстуализируется в новом контексте. Если contextObject не определён, создаётся новый объект и контекстуализируется. Если contextObject — vm.constants.DONT_CONTEXTIFY, контекстуализация не выполняется.
  3. Выполняет скомпилированный код, содержащийся в объекте vm.Script, в созданном контексте. Код не имеет доступа к области видимости, в которой вызывается этот метод.
  4. Возвращает результат.

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

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

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

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

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

// This would throw if the context is created from a contextified object.
// vm.constants.DONT_CONTEXTIFY allows creating contexts with ordinary
// global objects that can be frozen.
const freezeScript = new vm.Script('Object.freeze(globalThis); globalThis;');
const frozenContext = freezeScript.runInNewContext(vm.constants.DONT_CONTEXTIFY); copy

script.runInThisContext([options])

История
Версия Изменения
v6.3.0

Теперь поддерживается параметр breakOnSigint.

v0.3.1

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

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

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

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

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 copy

script.sourceMapURL

Добавлено в: v19.1.0, v18.13.0
  • <string> | <undefined>

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

Модули JavaScript
import vm from 'node:vm';

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

console.log(script.sourceMapURL);
// Prints: sourcemap.json
CommonJS
const vm = require('node:vm');

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

console.log(script.sourceMapURL);
// Prints: sourcemap.json

Class: vm.Module

Добавлено в: v13.0.0, v12.16.0
Стабильность: 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. Пока нет возможности взаимодействовать с загрузчиком, хотя такая поддержка планируется.

Модули JavaScript
import vm from 'node:vm';

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

// Step 1
//
// Create a Module by constructing a new `vm.SourceTextModule` object. This
// parses the provided source text, throwing a `SyntaxError` if anything goes
// wrong. By default, a Module is created in the top context. But here, we
// specify `contextifiedObject` as the context this Module belongs to.
//
// Here, we attempt to obtain the default export from the module "foo", and
// put it into local binding "secret".

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

// Step 2
//
// "Link" the imported dependencies of this Module to it.
//
// The provided linking callback (the "linker") accepts two arguments: the
// parent module (`bar` in this case) and the string that is the specifier of
// the imported module. The callback is expected to return a Module that
// corresponds to the provided specifier, with certain requirements documented
// in `module.link()`.
//
// If linking has not started for the returned Module, the same linker
// callback will be called on the returned Module.
//
// Even top-level Modules without dependencies must be explicitly linked. The
// callback provided would never be called, however.
//
// The link() method returns a Promise that will be resolved when all the
// Promises returned by the linker resolve.
//
// Note: This is a contrived example in that the linker function creates a new
// "foo" module every time it is called. In a full-fledged module system, a
// cache would probably be used to avoid duplicated modules.

async function linker(specifier, referencingModule) {
  if (specifier === 'foo') {
    return new vm.SourceTextModule(`
      // The "secret" variable refers to the global variable we added to
      // "contextifiedObject" when creating the context.
      export default secret;
    `, { context: referencingModule.context });

    // Using `contextifiedObject` instead of `referencingModule.context`
    // here would work as well.
  }
  throw new Error(`Unable to resolve dependency: ${specifier}`);
}
await bar.link(linker);

// Step 3
//
// Evaluate the Module. The evaluate() method returns a promise which will
// resolve after the module has finished evaluating.

// Prints 42.
await bar.evaluate();
CommonJS
const vm = require('node:vm');

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

(async () => {
  // Step 1
  //
  // Create a Module by constructing a new `vm.SourceTextModule` object. This
  // parses the provided source text, throwing a `SyntaxError` if anything goes
  // wrong. By default, a Module is created in the top context. But here, we
  // specify `contextifiedObject` as the context this Module belongs to.
  //
  // Here, we attempt to obtain the default export from the module "foo", and
  // put it into local binding "secret".

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

  // Step 2
  //
  // "Link" the imported dependencies of this Module to it.
  //
  // The provided linking callback (the "linker") accepts two arguments: the
  // parent module (`bar` in this case) and the string that is the specifier of
  // the imported module. The callback is expected to return a Module that
  // corresponds to the provided specifier, with certain requirements documented
  // in `module.link()`.
  //
  // If linking has not started for the returned Module, the same linker
  // callback will be called on the returned Module.
  //
  // Even top-level Modules without dependencies must be explicitly linked. The
  // callback provided would never be called, however.
  //
  // The link() method returns a Promise that will be resolved when all the
  // Promises returned by the linker resolve.
  //
  // Note: This is a contrived example in that the linker function creates a new
  // "foo" module every time it is called. In a full-fledged module system, a
  // cache would probably be used to avoid duplicated modules.

  async function linker(specifier, referencingModule) {
    if (specifier === 'foo') {
      return new vm.SourceTextModule(`
        // The "secret" variable refers to the global variable we added to
        // "contextifiedObject" when creating the context.
        export default secret;
      `, { context: referencingModule.context });

      // Using `contextifiedObject` instead of `referencingModule.context`
      // here would work as well.
    }
    throw new Error(`Unable to resolve dependency: ${specifier}`);
  }
  await bar.link(linker);

  // Step 3
  //
  // Evaluate the Module. The evaluate() method returns a promise which will
  // resolve after the module has finished evaluating.

  // Prints 42.
  await bar.evaluate();
})();

module.dependencySpecifiers

  • <string[]>

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

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

module.error

  • <any>

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

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

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

module.evaluate([options])

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

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

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

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

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

module.identifier

  • <string>

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

module.link(linker)

История
Версия Изменения
v20.10.0

Параметр extra.assert переименован в extra.attributes. Прежнее имя по-прежнему поддерживается для обратной совместимости.

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

      import foo from 'foo';
      //              ^^^^^ the module specifier copy
    • referencingModule <vm.Module> Объект Module, для которого вызывается link().

    • extra <Object>

      • attributes <Object> Данные из атрибута:
        import foo from 'foo' with { name: 'value' };
        //                         ^^^^^^^^^^^^^^^^^ the attribute copy
        В соответствии с ECMA-262, ожидается, что хосты вызовут ошибку при наличии неподдерживаемого атрибута.
      • assert <Object> Псевдоним для extra.attributes.
    • Возвращает: <vm.Module> | <Promise>

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

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

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

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

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

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

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

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

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

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

module.namespace

  • <Object>

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

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

module.status

  • <string>

Текущий статус модуля. Может иметь одно из следующих значений:

  • 'unlinked': module.link() еще не вызывался.

  • 'linking': module.link() был вызван, но еще не все промисы, возвращенные функцией связывания, разрешились.

  • 'linked': Модуль успешно связан, и все его зависимости связаны, но module.evaluate() еще не вызывался.

  • 'evaluating': Модуль вычисляется посредством module.evaluate() для самого себя или родительского модуля.

  • 'evaluated': Модуль успешно вычислен.

  • 'errored': Модуль вычислен, но было вызвано исключение.

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

Класс: vm.SourceTextModule

Добавлено в: v9.6.0
Стабильность: 1 — Экспериментальный

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

  • Расширяет: <vm.Module>

Класс 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() для создания объектов в определенном контексте.

Модули JavaScript
import vm from 'node:vm';

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

const module = new vm.SourceTextModule(
  'Object.getPrototypeOf(import.meta.prop).secret = secret;',
  {
    initializeImportMeta(meta) {
      // Note: this object is created in the top context. As such,
      // Object.getPrototypeOf(import.meta.prop) points to the
      // Object.prototype in the top context rather than that in
      // the contextified object.
      meta.prop = {};
    },
  });
// Since module has no dependencies, the linker function will never be called.
await module.link(() => {});
await module.evaluate();

// Now, Object.prototype.secret will be equal to 42.
//
// To fix this problem, replace
//     meta.prop = {};
// above with
//     meta.prop = vm.runInContext('{}', contextifiedObject);
CommonJS
const vm = require('node:vm');
const contextifiedObject = vm.createContext({ secret: 42 });
(async () => {
  const module = new vm.SourceTextModule(
    'Object.getPrototypeOf(import.meta.prop).secret = secret;',
    {
      initializeImportMeta(meta) {
        // Note: this object is created in the top context. As such,
        // Object.getPrototypeOf(import.meta.prop) points to the
        // Object.prototype in the top context rather than that in
        // the contextified object.
        meta.prop = {};
      },
    });
  // Since module has no dependencies, the linker function will never be called.
  await module.link(() => {});
  await module.evaluate();
  // Now, Object.prototype.secret will be equal to 42.
  //
  // To fix this problem, replace
  //     meta.prop = {};
  // above with
  //     meta.prop = vm.runInContext('{}', contextifiedObject);
})();

sourceTextModule.createCachedData()

Добавлено в: v13.7.0, v12.17.0
  • Возвращает: <Buffer>

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

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

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

// Create an initial module
const module = new vm.SourceTextModule('const a = 1;');

// Create cached data from this module
const cachedData = module.createCachedData();

// Create a new module using the cached data. The code must be the same.
const module2 = new vm.SourceTextModule('const a = 1;', { cachedData }); copy

Класс: vm.SyntheticModule

Добавлено в: v13.0.0, v12.16.0
Стабильность: 1 — Экспериментальный

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

  • Расширяет: <vm.Module>

Класс vm.SyntheticModule предоставляет запись синтетического модуля, определенную в спецификации WebIDL. Синтетические модули предназначены для предоставления универсального интерфейса, позволяющего включать источники, отличные от JavaScript, в графы модулей ECMAScript.

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

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

// Use `module` in linking... copy

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.

Модули JavaScript
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);
CommonJS
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]])

История
Версия Изменения
v20.12.0

Добавлена поддержка vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.

v19.6.0, v18.15.0

Если был передан параметр cachedData, возвращаемое значение теперь включает cachedDataRejected с той же семантикой, что и версия vm.Script.

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

Добавлено в: v20.12.0
  • <Object>

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

vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER

Добавлено в: v20.12.0
Стабильность: 1.1 — Активная разработка

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

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

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

История
Версия Изменения
v20.18.0

Аргумент contextObject теперь принимает vm.constants.DONT_CONTEXTIFY.

v20.12.0

Добавлена поддержка vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER.

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, глобальные переменные останутся неизменными.

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

global.globalVar = 3;

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

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

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

console.log(global.globalVar);
// Prints: 3 copy

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

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

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

Переданные name и origin контекста видны через API Inspector.

vm.isContext(object)

Добавлено в: v0.11.7
  • object <Object>
  • Возвращает: <boolean>

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

vm.measureMemory([options])

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

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

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

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

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

const vm = require('node:vm');
// Measure the memory used by the main context.
vm.measureMemory({ mode: 'summary' })
  // This is the same as vm.measureMemory()
  .then((result) => {
    // The current format is:
    // {
    //   total: {
    //      jsMemoryEstimate: 2418479, jsMemoryRange: [ 2418479, 2745799 ]
    //    }
    // }
    console.log(result);
  });

const context = vm.createContext({ a: 1 });
vm.measureMemory({ mode: 'detailed', execution: 'eager' })
  .then((result) => {
    // Reference the context here so that it won't be GC'ed
    // until the measurement is complete.
    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);
  }); copy

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

История
Версия Изменения
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 <string> Код JavaScript для компиляции и выполнения.
  • contextifiedObject <Object> Объект, преобразованный в контекст, который будет использоваться как global при компиляции и выполнении code.
  • options <Object> | <string>
    • filename <string> Задает имя файла, используемое в трассировках стека, созданных этим скриптом. По умолчанию: 'evalmachine.<anonymous>'.
    • lineOffset <number> Задает смещение номера строки, отображаемое в трассировках стека, созданных этим скриптом. По умолчанию: 0.
    • columnOffset <number> Задает смещение номера столбца первой строки, отображаемое в трассировках стека, созданных этим скриптом. По умолчанию: 0.
    • displayErrors <boolean> Если значение true, при возникновении Error во время компиляции code строка кода, вызвавшая ошибку, добавляется в трассировку стека. По умолчанию: true.
    • timeout <integer> Задает количество миллисекунд, в течение которых выполняется code до завершения выполнения. При завершении выполнения будет выброшен Error. Значение должно быть строго положительным целым числом.
    • breakOnSigint <boolean> Если true, получение SIGINT (Ctrl+C) завершит выполнение и приведет к выбросу Error. Существующие обработчики события, добавленные с помощью process.on('SIGINT'), отключаются на время выполнения скрипта, но продолжают работать после этого. По умолчанию: false.
    • cachedData <Buffer> | <TypedArray> | <DataView> Предоставляет необязательный Buffer или TypedArray либо DataView с данными кэша кода V8 для указанного исходного кода.
    • importModuleDynamically <Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания способа загрузки модулей при вычислении этого скрипта, когда вызывается import(). Этот параметр является частью экспериментального API модулей. Не рекомендуется использовать его в рабочей среде. Подробную информацию см. в разделе Поддержка динамического import() в API компиляции.

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

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

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

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);
// Prints: { globalVar: 1024 } copy

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

История
Версия Изменения
v20.18.0

Аргумент contextObject теперь принимает vm.constants.DONT_CONTEXTIFY.

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 <string> Код JavaScript для компиляции и выполнения.
  • contextObject <Object> | <vm.constants.DONT_CONTEXTIFY> | <undefined> Либо vm.constants.DONT_CONTEXTIFY, либо объект, который будет преобразован в контекст. Если undefined, для обратной совместимости будет создан пустой объект, преобразованный в контекст.
  • options <Object> | <string>
    • filename <string> Задает имя файла, используемое в трассировках стека, созданных этим скриптом. По умолчанию: 'evalmachine.<anonymous>'.
    • lineOffset <number> Задает смещение номера строки, отображаемое в трассировках стека, созданных этим скриптом. По умолчанию: 0.
    • columnOffset <number> Задает смещение номера столбца первой строки, отображаемое в трассировках стека, созданных этим скриптом. По умолчанию: 0.
    • displayErrors <boolean> Если значение true, при возникновении Error во время компиляции code строка кода, вызвавшая ошибку, добавляется в трассировку стека. По умолчанию: true.
    • timeout <integer> Задает количество миллисекунд, в течение которых выполняется code до завершения выполнения. При завершении выполнения будет выброшен Error. Значение должно быть строго положительным целым числом.
    • breakOnSigint <boolean> Если true, получение SIGINT (Ctrl+C) завершит выполнение и приведет к выбросу 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.
    • cachedData <Buffer> | <TypedArray> | <DataView> Предоставляет необязательный Buffer или TypedArray либо DataView с данными кэша кода V8 для указанного исходного кода.
    • importModuleDynamically <Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания способа загрузки модулей при вычислении этого скрипта, когда вызывается import(). Этот параметр является частью экспериментального API модулей. Не рекомендуется использовать его в рабочей среде. Подробную информацию см. в разделе Поддержка динамического import() в API компиляции.
    • microtaskMode <string> Если задано значение afterEvaluate, микрозадачи (задачи, запланированные с помощью Promise и async function) будут выполняться сразу после запуска скрипта. В этом случае они входят в области timeout и breakOnSigint.
  • Возвращает: <any> результат последнего выполненного в скрипте оператора.

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

Он выполняет сразу несколько действий:

  1. Создает новый контекст.
  2. Если contextObject является объектом, преобразует его в контекст нового контекста. Если contextObject не определен, создает новый объект и преобразует его в контекст. Если contextObject — vm.constants.DONT_CONTEXTIFY, не выполняет преобразование в контекст.
  3. Компилирует код как vm.Script
  4. Выполняет скомпилированный код в созданном контексте. Код не имеет доступа к области видимости, в которой вызван этот метод.
  5. Возвращает результат.

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

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

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

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

// This would throw if the context is created from a contextified object.
// vm.constants.DONT_CONTEXTIFY allows creating contexts with ordinary global objects that
// can be frozen.
const frozenContext = vm.runInNewContext('Object.freeze(globalThis); globalThis;', vm.constants.DONT_CONTEXTIFY); copy

vm.runInThisContext(code[, options])

История
Версия Изменения
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 <string> Код JavaScript для компиляции и выполнения.
  • options <Object> | <string>
    • filename <string> Задает имя файла, используемое в трассировках стека, создаваемых этим скриптом. По умолчанию: 'evalmachine.<anonymous>'.
    • lineOffset <number> Задает смещение номера строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию: 0.
    • columnOffset <number> Задает смещение номера столбца первой строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию: 0.
    • displayErrors <boolean> Если значение равно true, то при возникновении Error во время компиляции code строка кода, вызвавшая ошибку, добавляется в трассировку стека. По умолчанию: true.
    • timeout <integer> Задает количество миллисекунд, в течение которых выполняется code, прежде чем выполнение будет прервано. При прерывании выполнения будет выброшен Error. Значение должно быть строго положительным целым числом.
    • breakOnSigint <boolean> Если значение равно true, получение SIGINT (Ctrl+C) прервет выполнение и выбросит Error. Существующие обработчики события, подключенные с помощью process.on('SIGINT'), отключаются на время выполнения скрипта, но продолжают работать после его завершения. По умолчанию: false.
    • cachedData <Buffer> | <TypedArray> | <DataView> Предоставляет необязательные Buffer или TypedArray либо DataView с данными кэша кода V8 для указанного исходного кода.
    • importModuleDynamically <Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания способа загрузки модулей при вычислении этого скрипта, когда вызывается import(). Этот параметр является частью экспериментального API модулей. Не рекомендуется использовать его в рабочей среде. Подробную информацию см. в разделе Поддержка динамического import() в API компиляции.
  • Возвращает: <any> результат выполнения последнего оператора в скрипте.

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

Если options — строка, она задает имя файла.

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

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

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

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

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

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

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

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

'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); copy

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

Что означает «контекстуализировать» объект?

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

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

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

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

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

// An undefined `contextObject` option makes the global object contextified.
const context = vm.createContext();
console.log(vm.runInContext('globalThis', context) === context);  // false
// A contextified global object cannot be frozen.
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 copy

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

vm.constants.DONT_CONTEXTIFY

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

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

// Use vm.constants.DONT_CONTEXTIFY to freeze the global object.
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
} copy

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

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

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

// Returned object is reference equal to globalThis in the new context.
console.log(vm.runInContext('globalThis', context) === context);  // true

// Can be used to access globals in the new context directly.
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

// Can be frozen and it affects the inner context.
Object.freeze(context);
try {
  vm.runInContext('baz = 1; baz;', context);
} catch (e) {
  console.log(e); // Uncaught ReferenceError: baz is not defined
} copy

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

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

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

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 },
);
// This is printed *before* 'entering loop' (!)
console.log('done executing'); copy

Это можно исправить, передав microtaskMode: 'afterEvaluate' коду, который создает Context:

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

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

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

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

Обратные вызовы промисов помещаются в очередь микрозадач того контекста, в котором они были созданы. Например, если в приведенном выше примере заменить () => 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 модулей. Не рекомендуется использовать его в рабочей среде.

Если параметр importModuleDynamically не указан или имеет значение undefined

Если этот параметр не указан или имеет значение 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 в новом контексте.

CommonJS
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 loaded from the main context is not an instance of the Function
// class in the new context.
script.runInNewContext().then(console.log);
Модули JavaScript
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 loaded from the main context is not an instance of the Function
// class in the new context.
script.runInNewContext().then(console.log);

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

Модули JavaScript
import { Script, constants } from 'node:vm';
import { resolve } from 'node:path';
import { writeFileSync } from 'node:fs';

// Write test.js and test.txt to the directory where the current script
// being run is located.
writeFileSync(resolve(import.meta.dirname, 'test.mjs'),
              'export const filename = "./test.json";');
writeFileSync(resolve(import.meta.dirname, 'test.json'),
              '{"hello": "world"}');

// Compile a script that loads test.mjs and then test.json
// as if the script is placed in the same directory.
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);
CommonJS
const { Script, constants } = require('node:vm');
const { resolve } = require('node:path');
const { writeFileSync } = require('node:fs');

// Write test.js and test.txt to the directory where the current script
// being run is located.
writeFileSync(resolve(__dirname, 'test.mjs'),
              'export const filename = "./test.json";');
writeFileSync(resolve(__dirname, 'test.json'),
              '{"hello": "world"}');

// Compile a script that loads test.mjs and then test.json
// as if the script is placed in the same directory.
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);

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

  1. Модуль будет разрешаться относительно параметра filename, переданного в vm.Script или vm.compileFunction(). Разрешение может выполняться относительно filename, заданного абсолютным путем или строкой URL. Если filename — строка, которая не является ни абсолютным путем, ни URL, или если он не задан, разрешение будет выполняться относительно текущего рабочего каталога процесса. В случае vm.createContext() разрешение всегда выполняется относительно текущего рабочего каталога, поскольку этот параметр используется только при отсутствии ссылающегося скрипта или модуля.
  2. Для любого filename, разрешающегося в определенный путь, после того как процесс загрузит модуль из этого пути, результат может быть кэширован, и последующие загрузки того же модуля из того же пути будут возвращать тот же объект. Если filename — строка URL, кэш не будет использоваться, если строка содержит другие параметры поиска. Для filename, которые не являются строками URL, в настоящее время нет способа обойти кэширование.

Если 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. Для vm.compileFunction это скомпилированный Function, для new vm.SourceTextModule — скомпилированный vm.SourceTextModule, а для vm.createContext() — контекст Object.
  • importAttributes <Object> Значение "with", переданное необязательному параметру optionsExpression, или пустой объект, если значение не было передано.
  • Возвращает: <Module Namespace Object> | <vm.Module> Рекомендуется возвращать vm.Module, чтобы воспользоваться отслеживанием ошибок и избежать проблем с пространствами имен, содержащими экспорты функций then.
Модули JavaScript
// This script must be run with --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);   // The compiled script
    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' } }
CommonJS
// This script must be run with --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);   // The compiled script
      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' } }
})();

© Joyent, Inc. and other Node contributors
Licensed under the MIT License.
Node.js is a trademark of Joyent, Inc. and is used with its permission.
We are not endorsed by or affiliated with Joyent.
https://nodejs.org/dist/latest-v20.x/docs/api/vm.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API