Spec-Zone.ru › Node.js 22 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])

История
Версия Изменения
v21.7.0, 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]])

История
Версия Изменения
v22.8.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

Класс: 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. Для работы с объектами vm.Module могут быть полезны функции «async».

Использование объекта 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.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)

История
Версия Изменения
v21.1.0, v20.10.0, v18.19.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]] циклической записи модуля из спецификации. '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

sourceTextModule.dependencySpecifiers

История
Версия Изменения
v22.20.0

Этот элемент устарел; вместо него следует использовать sourceTextModule.moduleRequests.

Стабильность: 0 — Устарело: вместо этого используйте sourceTextModule.moduleRequests.
  • <string[]>

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

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

sourceTextModule.moduleRequests

Добавлено в: v22.20.0
  • <ModuleRequest[]> Зависимости этого модуля.

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

Например, для следующего исходного текста:

import foo from 'foo';
import fooAlias from 'foo';
import bar from './bar.js';
import withAttrs from '../with-attrs.ts' with { arbitraryAttr: 'attr-val' }; copy

Значение sourceTextModule.moduleRequests будет следующим:

[
  {
    specifier: 'foo',
    attributes: {},
  },
  {
    specifier: 'foo',
    attributes: {},
  },
  {
    specifier: './bar.js',
    attributes: {},
  },
  {
    specifier: '../with-attrs.ts',
    attributes: { arbitraryAttr: 'attr-val' },
  },
]; 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);
})();

Тип: ModuleRequest

Добавлено в: v22.20.0
  • <Object>
    • specifier <string> Спецификатор запрашиваемого модуля.
    • attributes <Object> Значение "with", переданное в WithClause в ImportDeclaration, либо пустой объект, если значение не было указано.

ModuleRequest представляет запрос на импорт модуля с заданными атрибутами и фазой импорта.

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

История
Версия Изменения
v21.7.0, 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

Добавлено в: v21.7.0, v20.12.0
  • Тип: <Object>

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

vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER

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

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

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

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

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

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 - Экспериментальный API

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

  • options <Object> Необязательный параметр.
    • mode <string> Либо 'summary', либо 'detailed'. В режиме сводки будут возвращены только данные об измеренной памяти основного контекста. В подробном режиме будут возвращены данные об измеренной памяти всех контекстов, известных текущему изоляту 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.

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

История
Версия Изменения
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 <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]])

История
Версия Изменения
v22.8.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 <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])

История
Версия Изменения
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 <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

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

Promises и async functions могут планировать задачи, выполняемые движком 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, либо имеет значение undefined, разрешение будет выполняться относительно текущего рабочего каталога процесса. В случае 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-v22.x/docs/api/vm.html

Spec-Zone.ru

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