Spec-Zone.ru › Node.js 16 LTS

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

Уровень стабильности: 2 - Стабильно

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

Модуль vm позволяет компилировать и запускать код в контексте виртуальной машины V8. Модуль vm не является механизмом защиты. Не используйте его для запуска кода, не являющегося надёжным.

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

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

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

const vm = require('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.

Класс: vm.Script

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

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

new vm.Script(code[, options])

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

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

v10.6.0

Параметр produceCachedData устарел в пользу script.createCachedData().

v5.7.0

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

v0.3.1

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

  • code <строка> Код JavaScript для компиляции.
  • options <объект> | <строка>
    • filename <строка> Указывает имя файла, используемое в трассировках стека, созданных этим скриптом. По умолчанию: 'evalmachine.<anonymous>'.
    • lineOffset <число> Указывает смещение номера строки, отображаемое в трассировках стека, созданных этим скриптом. По умолчанию: 0.
    • columnOffset <число> Указывает смещение номера столбца первой строки, отображаемое в трассировках стека, созданных этим скриптом. По умолчанию: 0.
    • cachedData <Буфер> | <Массив типов> | <DataView> Предоставляет необязательные Buffer или TypedArray, или DataView с данными кэша кода V8 для предоставленного исходного кода. Если предоставлено, значение cachedDataRejected будет установлено на true или false, в зависимости от принятия данных V8.
    • produceCachedData <логическое значение> Если true и нет cachedData, V8 попытается создать данные кэша кода для code. При успехе будет создан Buffer с данными кэша кода V8 и сохранён в свойстве cachedData возвращаемого экземпляра vm.Script. Значение cachedDataProduced будет установлено на true или false, в зависимости от успешного создания данных кэша кода. Этот параметр устарел в пользу script.createCachedData(). По умолчанию: false.
    • importModuleDynamically <Функция> Вызывается во время оценки модуля, когда вызывается import() . Если этот параметр не указан, вызовы import() будут отклонены с ошибкой ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING. Этот параметр является частью экспериментального API модулей. Мы не рекомендуем использовать его в производственной среде.
      • specifier <строка> спецификатор, переданный import()
      • script <vm.Script>
      • importAssertions <объект> Значение "assert" , переданное в необязательный параметр optionsExpression, или пустой объект, если значение не было предоставлено.
      • Возвращает: <Объект пространства имён модуля> | <vm.Модуль> Рекомендуется возвращать vm.Module для использования отслеживания ошибок и для избежания проблем с именованными пространствами, содержащими then экспорт функций.

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

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

script.createCachedData()

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

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

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

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

const cacheWithoutX = script.createCachedData();

script.runInThisContext();

const cacheWithX = script.createCachedData();

script.runInContext(contextifiedObject[, options])

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

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

v0.3.1

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

  • contextifiedObject <объект> Объект, привязанный к контексту, как возвращается методом vm.createContext().
  • options <объект>
    • displayErrors <логическое значение> Если true, если во время компиляции code произойдёт ошибка Error, строка кода, вызвавшая ошибку, будет добавлена к трассировке стека. По умолчанию: true.
    • timeout <целое число> Указывает количество миллисекунд для выполнения code до завершения выполнения. Если выполнение прервано, будет выброшена ошибка Error. Это значение должно быть строго положительным целым числом.
    • breakOnSigint <логическое значение> Если true, получение SIGINT (Ctrl+C) прервёт выполнение и выбросит ошибку Error. Существующие обработчики события, присоединённые через process.on('SIGINT'), будут отключены во время выполнения скрипта, но продолжат работать после него. По умолчанию: false.
  • Возвращает: <любое> результат последнего оператора, выполненного в скрипте.

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

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

const vm = require('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' }

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

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

История изменений
Версия Изменения
v14.6.0

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

v10.0.0

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

v6.3.0

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

v0.3.1

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

  • contextObject <Объект> Объект, который будет контекстуализирован. Если undefined, будет создан новый объект.
  • options <Объект>
    • displayErrors <boolean> Когда true, если при компиляции code возникает Error, строка кода, вызвавшая ошибку, добавляется в стек вызовов. По умолчанию: true.
    • timeout <целое> Указывает количество миллисекунд, в течение которых выполняется code перед завершением выполнения. Если выполнение прервано, будет брошено исключение Error. Это значение должно быть строго положительным целым числом.
    • breakOnSigint <boolean> Если true, получение SIGINT (Ctrl+C) завершит выполнение и выбросит исключение Error. Существующие обработчики события, прикрепленные через process.on('SIGINT'), будут отключены во время выполнения скрипта, но продолжат работать после него. По умолчанию: false.
    • contextName <строка> Читабельное имя нового контекста. По умолчанию: 'VM Context i', где i — возрастающий числовой индекс созданного контекста.
    • contextOrigin <строка> Происхождение, соответствующее новому контексту для отображения. Происхождение должно быть отформатировано как URL, но содержать только схему, хост и порт (при необходимости), как значение свойства url.origin объекта URL. Важно отметить, что эта строка должна опускать конечный слэш, поскольку он обозначает путь. По умолчанию: ''.
    • contextCodeGeneration <Объект>
      • strings <boolean> Если установлено в false, любые вызовы eval или конструкторов функций (Function, GeneratorFunction, и т.д.) выбросят исключение EvalError. По умолчанию: true.
      • wasm <boolean> Если установлено в false, любая попытка скомпилировать модуль WebAssembly выбросит исключение WebAssembly.CompileError. По умолчанию: true.
    • microtaskMode <строка> Если установлено в afterEvaluate, микрозадачи (задачи, запланированные через Promise и async function ) будут выполняться сразу после выполнения скрипта. В этом случае они включаются в области timeout и breakOnSigint.
  • Возвращает: <любое> результат последнего оператора, выполненного в скрипте.

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

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

const vm = require('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' }]

script.runInThisContext([options])

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

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

v0.3.1

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

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

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

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

const vm = require('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

Класс: 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.

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

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

Модули MJS

import vm from '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();

Модули CJS

const vm = require('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

  • <массив строк>

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

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

module.error

  • <любой тип>

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

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

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

module.evaluate([options])

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

Выполняет оценку модуля.

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

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

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

module.identifier

  • <строка>

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

module.link(linker)

  • linker <Функция>
    • specifier <строка> Указатель запрашиваемого модуля:

      import foo from 'foo';
      //              ^^^^^ the module specifier
    • extra <Объект>

      • assert <Объект> Данные из проверки:
        import foo from 'foo' assert { name: 'value' };
        //                           ^^^^^^^^^^^^^^^^^ the assertion
        По ECMA-262, ожидается, что хосты проигнорируют проверки, которые они не поддерживают, в отличие, например, от выдачи ошибки, если присутствует неподдерживаемая проверка.
    • referencingModule <vm.Module> Объект Module, на котором вызывается link().

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

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

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

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

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

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

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

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

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

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

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

module.namespace

  • <Объект>

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

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

module.status

  • <строка>

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

  • 'unlinked': Метод module.link() ещё не был вызван.

  • 'linking': Метод module.link() был вызван, но ещё не были разрешены все Promises, возвращённые функцией связывания.

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

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

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

  • code <строка> Код модуля JavaScript для парсинга
  • options
    • identifier <строка> Строка, используемая в следах стека. По умолчанию: 'vm:module(i)', где i — контекстно-зависимый возрастающий индекс.
    • cachedData <Буфер> | <Массив с типом данных> | <DataView> Предоставляет необязательные Buffer или TypedArray, или DataView с данными кэша кода V8 для предоставленного исходного кода. code должен быть таким же, как модуль, из которого был создан этот cachedData.
    • context <Объект> Контекстуализированный объект, возвращённый методом vm.createContext(), для компиляции и оценки этого Module.
    • lineOffset <целое> Указывает смещение номера строки, отображаемое в следах стека, созданных этим Module. По умолчанию: 0.
    • columnOffset <целое> Указывает смещение числа столбца первой строки, отображаемое в следах стека, созданных этим Module. По умолчанию: 0.
    • initializeImportMeta <Функция> Вызывается во время оценки этого Module для инициализации import.meta.
      • meta <import.meta>
      • module <vm.SourceTextModule>
    • importModuleDynamically <Функция> Вызывается во время оценки этого модуля, когда вызывается import(). Если этот параметр не указан, вызовы import() отклонятся с ошибкой ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING.
      • specifier <строка> Спецификатор, переданный import()
      • module <vm.Module>
      • importAssertions <Объект> Значение "assert", переданное необязательному параметру optionsExpression, или пустой объект, если значение не было предоставлено.
      • Возвращает: <Объект пространства имён модуля> | <vm.Module> Рекомендуется возвращать vm.Module, чтобы воспользоваться отслеживанием ошибок и избежать проблем с пространствами имён, которые содержат экспорт функций then.

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

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

MJS-модули

import vm from '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);

CJS-модули

const vm = require('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
  • Возвращает: <Буфер>

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

// 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 });

Класс: vm.SyntheticModule

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

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

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

Класс vm.SyntheticModule предоставляет Запись синтетического модуля, как определено в спецификации WebIDL. Цель синтетических модулей — предоставить общий интерфейс для экспонирования источников, не являющихся JavaScript, в графах модулей ECMAScript.

const vm = require('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...

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

Добавлен в: v13.0.0, v12.16.0
  • exportNames <массив строк> Массив имён, которые будут экспортированы из модуля.
  • evaluateCallback <Функция> Вызывается при оценке модуля.
  • options
    • identifier <строка> Строка, используемая в следах стека. По умолчанию: 'vm:module(i)', где i — контекстно-зависимый возрастающий индекс.
    • context <Объект> Контекстуализированный объект, возвращённый методом vm.createContext(), для компиляции и оценки этого Module.

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

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

syntheticModule.setExport(name, value)

Добавлен в: v13.0.0, v12.16.0
  • name <строка> Имя экспорта для установки.
  • value <любое> Значение для установки экспорта.

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

MJS-модули

import vm from 'vm';

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

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

assert.strictEqual(m.namespace.x, 1);

CJS-модули

const vm = require('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]])

История
Версия Изменения
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 <строка> Тело функции для компиляции.
  • params <массив строк> Массив строк, содержащий все параметры функции.
  • options <объект>
    • filename <строка> Указывает имя файла, используемое в отладке стека, созданной этим скриптом. По умолчанию: ''.
    • lineOffset <число> Указывает смещение номера строки, отображаемое в отладке стека, созданной этим скриптом. По умолчанию: 0.
    • columnOffset <число> Указывает смещение номера столбца первой строки, отображаемое в отладке стека, созданной этим скриптом. По умолчанию: 0.
    • cachedData <Буфер> | <Тип массива> | <DataView> Предоставляет необязательный Buffer или TypedArray, или DataView с данными кэша кода V8 для предоставленного исходного кода.
    • produceCachedData <логическое значение> Указывает, нужно ли создавать новые данные кэша. По умолчанию: false.
    • parsingContext <объект> Объект с контекстом, в котором должна быть скомпилирована функция.
    • contextExtensions <массив объектов> Массив, содержащий набор расширений контекста (объекты, обертывающие текущую область видимости), которые должны быть применены при компиляции. По умолчанию: [].
    • importModuleDynamically <функция> Вызывается во время оценки этого модуля, когда вызывается import(). Если этот параметр не указан, вызовы к import() вернут ошибку ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING. Этот параметр является частью экспериментального API модулей и не считается стабильным.
      • specifier <строка> спецификатор, переданный в import()
      • function <функция>
      • importAssertions <объект> Значение "assert", переданное в необязательный параметр optionsExpression, или пустой объект, если значение не было предоставлено.
      • Возвращает: <Объект пространства имён модуля> | <vm.Модуль> Рекомендуется возвращать vm.Module, чтобы воспользоваться отслеживанием ошибок и избежать проблем с пространствами имён, содержащими then экспорт функций.
  • Возвращает: <функция>

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

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

История
Версия Изменения
v14.6.0

Теперь поддерживается опция microtaskMode.

v10.0.0

Первый аргумент больше не может быть функцией.

v10.0.0

Теперь поддерживается опция codeGeneration.

v0.3.1

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

  • contextObject <объект>
  • options <объект>
    • name <строка> Читабельное имя вновь созданного контекста. По умолчанию: 'VM Context i', где i — нарастающий числовой индекс созданного контекста.
    • origin <строка> Происхождение, соответствующее вновь созданному контексту для отображения. Происхождение должно быть отформатировано как URL, но содержать только схему, хост и порт (при необходимости), подобно значению свойства url.origin объекта URL. Важно, что эта строка должна опускать конечный слэш, так как он обозначает путь. По умолчанию: ''.
    • codeGeneration <объект>
      • strings <логическое значение> Если установлено в false, любые вызовы к eval или конструкторам функций (Function, GeneratorFunction, и т. д.) будут вызывать EvalError. По умолчанию: true.
      • wasm <логическое значение> Если установлено в false, любая попытка скомпилировать модуль WebAssembly вызовет WebAssembly.CompileError. По умолчанию: true.
    • microtaskMode <строка> Если установлено в afterEvaluate, микрозадачи (задачи, запланированные через Promise и async function ) будут выполняться немедленно после того, как скрипт пройдёт script.runInContext(). В этом случае они включены в области видимости timeout и breakOnSigint.
  • Возвращает: <объект> объект с контекстом.

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

const vm = require('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

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

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

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

vm.isContext(object)

Добавлена в: v0.11.7
  • object <объект>
  • Возвращает: <логическое значение>

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

vm.measureMemory([options])

Added in: v13.10.0
Stability: 1 - Experimental

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

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

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

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

const vm = require('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);
  });

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

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

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

v6.3.0

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

v0.3.1

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

  • code <строка> JavaScript-код для компиляции и выполнения.
  • contextifiedObject <Объект> контекстуализированный объект, который будет использоваться в качестве global при компиляции и выполнении code.
  • options <Объект> | <строка>
    • filename <строка> Указывает имя файла, используемое в трассировках стека, созданных этим скриптом. По умолчанию: 'evalmachine.<anonymous>'.
    • lineOffset <число> Указывает смещение номера строки, отображаемое в трассировках стека, созданных этим скриптом. По умолчанию: 0.
    • columnOffset <число> Указывает смещение номера столбца первой строки, отображаемое в трассировках стека, созданных этим скриптом. По умолчанию: 0.
    • displayErrors <логическое> Когда true, если при компиляции code возникает Error, строка кода, вызвавшая ошибку, добавляется в трассировку стека. По умолчанию: true.
    • timeout <целое> Указывает количество миллисекунд для выполнения code перед завершением выполнения. Если выполнение завершается, будет брошено исключение Error. Это значение должно быть строго положительным целым числом.
    • breakOnSigint <логическое> Если true, получение SIGINT (клавиша Ctrl+C) завершит выполнение и выбросит Error. Существующие обработчики события, присоединённые с помощью process.on('SIGINT'), будут отключены во время выполнения скрипта, но продолжат работать после него. По умолчанию: false.
    • cachedData <Буфер> | <Массив_типа_данных> | <DataView> Предоставляет необязательный Buffer или TypedArray, или DataView с данными кэша кода V8 для указанного исходного кода. При предоставлении значение cachedDataRejected будет установлено либо true, либо false, в зависимости от принятия данных V8.
    • produceCachedData <логическое> Когда true и нет cachedData, V8 попытается создать данные кэша кода для code. При успехе будет создан Buffer с данными кэша кода V8 и сохранён в свойстве cachedData возвращаемого экземпляра vm.Script. Значение cachedDataProduced будет установлено либо true, либо false, в зависимости от успешного создания данных кэша кода. Эта опция устарела в пользу script.createCachedData(). По умолчанию: false.
    • importModuleDynamically <Функция> Вызывается во время оценки модуля, когда import() вызывается. Если эта опция не указана, вызовы к import() отклонят с ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING. Эта опция входит в API экспериментальных модулей. Не рекомендуется использовать её в производственной среде.
      • specifier <строка> спецификатор, переданный в import()
      • script <vm.Скрипт>
      • importAssertions <Объект> Значение "assert" , переданное в необязательный параметр optionsExpression, или пустой объект, если значение не было предоставлено.
      • Возвращает: <Объект_пространства_имён_модуля> | <vm.Модуль> Возвращение vm.Module рекомендуется для отслеживания ошибок и для избежания проблем с пространствами имён, содержащими then экспорт функций.
  • Возвращает: <любой> результат последнего оператора, выполненного в скрипте.

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

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

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

const vm = require('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 }

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

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

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

v14.6.0

Теперь поддерживается опция microtaskMode.

v10.0.0

Теперь поддерживается опция contextCodeGeneration.

v6.3.0

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

v0.3.1

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

  • code <строка> JavaScript-код для компиляции и выполнения.
  • contextObject <объект> Объект, который будет контекстуализирован. Если undefined, будет создан новый объект.
  • options <объект> | <строка>
    • filename <строка> Указывает имя файла, используемое в отслеживании стека, созданном этим скриптом. По умолчанию: 'evalmachine.<anonymous>'.
    • lineOffset <число> Указывает смещение номера строки, отображаемое в отслеживании стека, созданном этим скриптом. По умолчанию: 0.
    • columnOffset <число> Указывает смещение номера столбца первой строки, отображаемое в отслеживании стека, созданном этим скриптом. По умолчанию: 0.
    • displayErrors <булево> Когда true, если во время компиляции code возникает ошибка Error, строка кода, вызвавшая ошибку, прикрепляется к отслеживанию стека. По умолчанию: true.
    • timeout <целое> Указывает количество миллисекунд для выполнения code перед завершением выполнения. Если выполнение завершено, будет выброшена ошибка Error. Это значение должно быть строго положительным целым числом.
    • breakOnSigint <булево> Если true, получение SIGINT (клавиши Ctrl+C) завершит выполнение и выбросит ошибку Error. Существующие обработчики события, присоединенные с помощью process.on('SIGINT'), будут отключены во время выполнения скрипта, но продолжат работать после него. По умолчанию: false.
    • contextName <строка> Читабельное имя нового контекста. По умолчанию: 'VM Context i', где i — возрастающий числовой индекс созданного контекста.
    • contextOrigin <строка> Происхождение, соответствующее новому созданному контексту для отображения. Происхождение должно быть отформатировано как URL, но содержать только схему, хост и порт (при необходимости), как значение свойства url.origin объекта URL. Важно, что эта строка должна опускать конечный слэш, поскольку он обозначает путь. По умолчанию: ''.
    • contextCodeGeneration <Буфер> | <Массив_типов> | <DataView> Предоставляет необязательный Buffer или TypedArray, или DataView с данными кэша кода V8 для предоставленного источника. При предоставлении значение cachedDataRejected будет установлено в true или false, в зависимости от принятия данных V8.
    • produceCachedData <булево> При true и отсутствии cachedData, V8 попытается создать данные кэша кода для code. При успехе будет создан Buffer с данными кэша кода V8 и сохранен в свойстве cachedData возвращенного экземпляра vm.Script. Значение cachedDataProduced будет установлено в true или false, в зависимости от успешного создания данных кэша кода. Эта опция устарела в пользу script.createCachedData(). По умолчанию: false.
    • importModuleDynamically <Функция> Вызывается во время оценки этого модуля при вызове import(). Если эта опция не указана, вызовы import() отклонят запрос с ошибкой ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING. Эта опция является частью экспериментального API модулей. Мы не рекомендуем использовать ее в рабочей среде.
      • specifier <строка> спецификатор, переданный import()
      • script <vm.Скрипт>
      • importAssertions <объект> Значение "assert", переданное в необязательный параметр optionsExpression, или пустой объект, если значение не было предоставлено.
      • Возвращает: <Объект_пространства_имен_модуля> | <vm.Модуль> Рекомендуется возвращать vm.Module, чтобы воспользоваться отслеживанием ошибок и избежать проблем со пространствами имен, содержащими экспорт функций then.
    • microtaskMode <строка> Если установлено значение afterEvaluate, микрозадачи (задачи, запланированные с помощью Promise и async function) будут выполняться немедленно после выполнения скрипта. В этом случае они включены в области timeout и breakOnSigint.
  • Возвращает: <любой> результат последнего оператора, выполненного в скрипте.

vm.runInNewContext() Сначала contextObject (или создает новый contextObject при передаче как undefined), компилирует code, выполняет его в созданном контексте и возвращает результат. Выполнение кода не имеет доступа к локальному пространству имен.

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

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

const vm = require('vm');

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

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

vm.runInThisContext(code[, options])

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

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

v6.3.0

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

v0.3.1

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

  • code <строка> Код JavaScript для компиляции и выполнения.
  • options <Объект> | <строка>
    • filename <строка> Указывает имя файла, используемое в трассировках стека, создаваемых этим скриптом. По умолчанию: 'evalmachine.<anonymous>'.
    • lineOffset <число> Указывает смещение номера строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию: 0.
    • columnOffset <число> Указывает смещение номера столбца первой строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию: 0.
    • displayErrors <логическое значение> Когда true, если при компиляции code возникает ошибка Error, строка кода, вызвавшая ошибку, добавляется к трассировке стека. По умолчанию: true.
    • timeout <целое число> Указывает количество миллисекунд для выполнения code перед завершением выполнения. Если выполнение завершается, будет выброшена ошибка Error. Это значение должно быть строго положительным целым числом.
    • breakOnSigint <логическое значение> Если true, получение SIGINT (Ctrl+C) завершит выполнение и выбросит ошибку Error. Существующие обработчики события, подключенные через process.on('SIGINT'), отключаются во время выполнения скрипта, но продолжают работать после него. По умолчанию: false.
    • cachedData <Буфер> | <Массив типизированных данных> | <DataView> Предоставляет необязательный Buffer или TypedArray, или DataView с данными кэша кода V8 для предоставленного исходного кода. При предоставлении значение cachedDataRejected будет установлено в true или false в зависимости от принятия данных V8.
    • produceCachedData <логическое значение> Когда true и отсутствует cachedData, V8 попытается создать данные кэша кода для code. При успехе будет создан Buffer с данными кэша кода V8 и сохранен в свойстве cachedData возвращенного экземпляра vm.Script . Значение cachedDataProduced будет установлено в true или false в зависимости от успешного создания данных кэша кода. Эта опция устарела в пользу script.createCachedData(). По умолчанию: false.
    • importModuleDynamically <Функция> Вызывается во время оценки этого модуля, когда вызывается import() . Если эта опция не указана, вызовы import() отклонятся с ошибкой ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING. Эта опция является частью экспериментального API модулей. Мы не рекомендуем использовать её в рабочей среде.
      • specifier <строка> спецификатор, переданный import()
      • script <vm.Скрипт>
      • importAssertions <Объект> Значение "assert" , переданное в необязательный параметр optionsExpression, или пустой объект, если значение не было предоставлено.
      • Возвращает: <Объект пространства имён модуля> | <vm.Модуль> Возвращение vm.Module рекомендуется для отслеживания ошибок и для предотвращения проблем с именами пространств, содержащими then экспорт функций.
  • Возвращает: <любой> результат последнего оператора, выполненного в скрипте.

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

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

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

const vm = require('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'

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

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

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

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

'use strict';
const vm = require('vm');

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

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

  console.log('Server running at http://127.0.0.1:8124/');
})`;

vm.runInThisContext(code)(require);

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

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

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

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

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

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

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

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

const vm = require('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');

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

const vm = require('vm');

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

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

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

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

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

© 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-v16.x/docs/api/vm.html

Spec-Zone.ru

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