Spec-Zone.ru › Node.js 18 LTS

Виртуальная машина (выполнение 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])

История
Версия Изменения
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 <строка> Код 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.Module> Рекомендуется возвращать vm.Module, чтобы воспользоваться отслеживанием ошибок и избежать проблем с пространствами имён, содержащими then экспорт функций.

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

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

script.cachedDataRejected

Добавлен в: v5.7.0
  • <логическое значение> | <неопределено>

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

script.createCachedData()

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

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

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

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

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

Выполняет скомпилированный код, содержащийся в объекте 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]])

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

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

v10.0.0

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

v6.3.0

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

v0.3.1

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

  • contextObject <Объект> Объект, который будет контекстуализирован. Если undefined, будет создан новый объект.
  • options <Объект>
    • 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 <Объект>
      • strings <логическое значение> Если установлено в false, любые вызовы eval или конструкторов функций (Function, GeneratorFunction, и т. д.) вызовут исключение EvalError. По умолчанию: true.
      • wasm <логическое значение> Если установлено в false, любая попытка скомпилировать модуль WebAssembly вызовет исключение WebAssembly.CompileError. По умолчанию: true.
    • microtaskMode <строка> Если установлено в afterEvaluate, микрозадачи (задачи, запланированные через Promise и async function ) будут выполнены сразу после выполнения скрипта. В этом случае они включены в timeout и breakOnSigint области.
  • Возвращает: <любой> результат последнего выполненного оператора в скрипте.

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

В следующем примере компилируется код, устанавливающий глобальную переменную, а затем код выполняется несколько раз в разных контекстах. Глобальные переменные устанавливаются и содержатся в каждом отдельном 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' }] copy

script.runInThisContext([options])

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

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

v0.3.1

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

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

Выполняет скомпилированный код, содержащийся в 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

Добавлен в: v18.13.0
  • <строка> | <неопределено>

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

Модули MJS

import vm from 'node:vm';

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

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

Модули CJS

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. Использование функций 'async' может помочь в работе с объектами vm.Module.

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

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

МОДУЛИ MJS

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();

МОДУЛИ CJS

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

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

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

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

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

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

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

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

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

module.dependencySpecifiers

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

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

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

module.error

  • <любой тип>

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

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

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

module.evaluate([options])

  • options <объект>
    • timeout <целое число> Устанавливает количество миллисекунд для оценки перед завершением выполнения. Если выполнение прервано, будет брошено исключение Error. Это значение должно быть положительным целым числом.
    • breakOnSigint <логическое значение> Если true, получение сигнала прерывания (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 copy
    • referencingModule <vm.Module> Объект Module, на котором вызывается link().

    • extra <объект>

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

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

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

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

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

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

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

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

  • Функция связывания может быть асинхронной, тогда как 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.Модуль>

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

new vm.SourceTextModule(code[, options])

История
Версия Изменения
v17.0.0, 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.Модуль>
      • importAssertions <Объект> значение "assert", переданное в необязательный параметр optionsExpression, или пустой объект, если значение не было предоставлено.
      • Возвращает: <Объект пространства имен модуля> | <vm.Модуль> Возврат vm.Module рекомендуется для отслеживания ошибок и для устранения проблем с именованными пространствами, которые содержат экспорт функций then.

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

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

Модули MJS

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

Модули CJS

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
  • Возвращает: <Буфер>

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

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

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

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

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

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

Класс: vm.SyntheticModule

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

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

  • Расширяет: <vm.Модуль>

Класс vm.SyntheticModule предоставляет Synthetic Module Record, как определено в спецификации 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 <массив строк> Массив имён, которые будут экспортированы из модуля.
  • 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 'node: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('node:vm');
(async () => {
  const m = new vm.SyntheticModule(['x'], () => {
    m.setExport('x', 1);
  });
  await m.link(() => {});
  await m.evaluate();
  assert.strictEqual(m.namespace.x, 1);
})();

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

История
Версия Изменения
v18.15.0

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

v17.0.0, v16.12.0

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

v15.9.0

Опять добавлен параметр importModuleDynamically.

v14.3.0

Удален importModuleDynamically из-за проблем с совместимостью.

v14.1.0, v13.14.0

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

v10.10.0

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

  • code <строка> Тело функции для компиляции.
  • params <массив строк> Массив строк, содержащих все параметры функции.
  • options <объект>
    • filename <строка> Указывает имя файла, используемое в отслеживании стека, созданном этим скриптом. По умолчанию: ''.
    • lineOffset <число> Указывает смещение номера строки, отображаемое в отслеживании стека, созданном этим скриптом. По умолчанию: 0.
    • columnOffset <число> Указывает смещение номера столбца первой строки, отображаемое в отслеживании стека, созданном этим скриптом. По умолчанию: 0.
    • cachedData <Буфер> | <Тип массива> | <DataView> Предоставляет необязательные Buffer или TypedArray, или DataView с данными кэша кода V8 для предоставленного источника. Это должно быть получено в результате предыдущего вызова vm.compileFunction() с теми же code и params.
    • 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('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.createContext() в первую очередь полезен для создания единого контекста, который можно использовать для выполнения нескольких скриптов. Например, при эмуляции веб-браузера, метод можно использовать для создания единственного контекста, представляющего глобальный объект окна, а затем выполнить все теги <script> вместе в этом контексте.

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

END_OF_DOCUMENT_MARKER ```

vm.isContext(object)

Added in: v0.11.7
  • object <Объект>
  • Returns: <логическое значение>

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

vm.measureMemory([options])

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

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

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

Формат объекта, с которым возвращаемое обещание может быть разрешено, специфичен для движка 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])

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

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

v6.3.0

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

v0.3.1

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

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

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

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

v14.6.0

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

v10.0.0

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

v6.3.0

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

v0.3.1

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

  • code <строка> JavaScript-код для компиляции и выполнения.
  • contextObject <Объект> Объект, который будет контекстуализирован. Если 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 для предоставленного исходного кода.
    • 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('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' } copy

vm.runInThisContext(code[, options])

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

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

v6.3.0

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

v0.3.1

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

  • code <строка> Код JavaScript для компиляции и выполнения.
  • options <Объект> | <строка>
    • filename <строка> Указывает имя файла, используемое в отслеживании стека, созданном этим скриптом. По умолчанию: 'evalmachine.<anonymous>'.
    • lineOffset <число> Указывает смещение номера строки, отображаемое в отслеживании стека, созданном этим скриптом. По умолчанию: 0.
    • columnOffset <число> Указывает смещение номера столбца первой строки, отображаемое в отслеживании стека, созданном этим скриптом. По умолчанию: 0.
    • displayErrors <логическое значение> Если true, если при компиляции code произойдёт ошибка Error, строка кода, вызвавшая ошибку, добавляется в трассировку стека. По умолчанию: true.
    • timeout <целое число> Указывает количество миллисекунд для выполнения code перед завершением выполнения. Если выполнение будет завершено, будет выброшено исключение Error. Это значение должно быть строго положительным целым числом.
    • breakOnSigint <логическое значение> Если true, получение SIGINT (Ctrl+C) завершит выполнение и выбросит исключение Error. Существующие обработчики события, присоединённые через process.on('SIGINT'), будут отключены во время выполнения скрипта, но продолжат работать после этого. По умолчанию: false.
    • cachedData <Буфер> | <Массив типизированных данных> | <DataView> Предоставляет необязательный Buffer или TypedArray, или DataView с данными кэша кода V8 для предоставленного источника.
    • 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('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. Код, переданный в этот контекст VM, будет иметь свой изолированный объём.

Для запуска простого веб-сервера с помощью модуля 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 (или новый созданный объект, если contextObject равен undefined ) внутренне связывается с новой инстанцией контекста V8. Этот контекст V8 предоставляет code выполнить методы модуля node:vm в изолированной глобальной среде, в которой он может функционировать. Процесс создания контекста V8 и его связывания с contextObject и есть то, что в этом документе описывается как "контекстуализация" объекта.

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

Обещания и асинхронные задачи могут планировать задачи, выполняемые 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.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-v18.x/docs/api/vm.html

Spec-Zone.ru

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