Spec-Zone.ru › Node.js

Виртуальная машина (выполнение JavaScript)

Устойчивость: 2 - Стабильно

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

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

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

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

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

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

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

const x = 1;

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

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

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

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

Класс: vm.Script

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

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

new vm.Script(code[, options])

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

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

v17.0.0, v16.12.0

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

v10.6.0

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

v5.7.0

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

v0.3.1

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

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

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

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

script.cachedDataRejected

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

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

script.createCachedData()

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

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

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

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

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

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

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

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

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

global.globalVar = 0;

const script = new vm.Script('globalVar += 1', { filename: 'myfile.vm' });

for (let i = 0; i < 1000; ++i) {
  script.runInThisContext();
}

console.log(globalVar);

// 1000 copy

script.sourceMapURL

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

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

Оценить модуль.

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

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

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

module.identifier

  • <строка>

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

module.link(linker)

История
Версия Изменения
v21.1.0, v20.10.0, v18.19.0

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

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

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

    • extra <Объект>

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

  • Возвращает: <Обещание>

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

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

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

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

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

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

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

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

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

module.namespace

  • <Объект>

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

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

module.status

  • <строка>

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

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

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

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

  • 'evaluating': Модуль оценивается с помощью module.evaluate() на самом себе или родительском модуле.

  • 'evaluated': Модуль был успешно оценен.

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

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

Класс: vm.SourceTextModule

Добавлен в: v9.6.0
Устойчивость: 1 - Экспериментальная

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

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

Класс vm.SourceTextModule предоставляет запись Записи модуля исходного текста, как определено в спецификации 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() . Этот параметр является частью экспериментального API модулей. Не рекомендуется использовать его в рабочей среде. Подробную информацию см. в разделе Поддержка динамических import() в API компиляции.

Создает новый экземпляр 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 предоставляет Запись синтетического модуля, как определено в спецификации 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]])

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

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

v19.6.0, v18.15.0

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

v17.0.0, v16.12.0

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

v15.9.0

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

v14.3.0

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

v14.1.0, v13.14.0

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

v10.10.0

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

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

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

vm.constants

Добавлен в: v21.7.0, v20.12.0
  • <объект>

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

vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER

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

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

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

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

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

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

v21.2.0, v20.11.0

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

v14.6.0

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

v10.0.0

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

v10.0.0

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

v0.3.1

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

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

Если задан 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 Инспектора.

vm.isContext(object)

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

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

vm.measureMemory([options])

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

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

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

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

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

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

const context = vm.createContext({ a: 1 });
vm.measureMemory({ mode: 'detailed', execution: 'eager' })
  .then((result) => {
    // Reference the context here so that it won't be GC'ed
    // until the measurement is complete.
    console.log(context.a);
    // {
    //   total: {
    //     jsMemoryEstimate: 2574732,
    //     jsMemoryRange: [ 2574732, 2904372 ]
    //   },
    //   current: {
    //     jsMemoryEstimate: 2438996,
    //     jsMemoryRange: [ 2438996, 2768636 ]
    //   },
    //   other: [
    //     {
    //       jsMemoryEstimate: 135736,
    //       jsMemoryRange: [ 135736, 465376 ]
    //     }
    //   ]
    // }
    console.log(result);
  }); copy

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

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

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

v17.0.0, v16.12.0

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

v6.3.0

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

v0.3.1

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

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

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

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

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

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

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

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

v17.0.0, v16.12.0

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

v14.6.0

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

v10.0.0

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

v6.3.0

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

v0.3.1

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

  • code <строка> JavaScript-код для компиляции и выполнения.
  • contextObject <объект> Объект, который будет контекстуализирован. Если 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 <объект>
      • strings <логическое значение> Если установлено в значение false, любые вызовы eval или конструкторов функций (Function, GeneratorFunction, и т.д.) приведут к броску EvalError. По умолчанию: true.
      • wasm <логическое значение> Если установлено в значение false, любая попытка скомпилировать модуль WebAssembly приведет к броску WebAssembly.CompileError. По умолчанию: true.
    • cachedData <Буфер> | <Массив типов> | <DataView> Предоставляет необязательный Buffer или TypedArray, или DataView с данными кэша кода V8 для предоставленного источника.
    • importModuleDynamically <Функция> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания способа загрузки модулей во время оценки этого скрипта при вызове import() . Этот параметр является частью экспериментального API модулей. Не рекомендуется использовать его в производственной среде. Подробная информация представлена в разделе Поддержка динамического import() в API компиляции.
    • 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])

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

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

v17.0.0, v16.12.0

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

v6.3.0

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

v0.3.1

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

vm.runInThisContext(code)(require); copy

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

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

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

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

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

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

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

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

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

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

vm.runInNewContext(
  'Promise.resolve().then(() => loop());',
  { loop, console },
  { timeout: 5 },
);
// This is printed *before* 'entering loop' (!)
console.log('done executing'); copy

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

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

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

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

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

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

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

END_OF_DOCUMENT_MARKER

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

Следующие API поддерживают параметр importModuleDynamically, чтобы включить динамический импорт в коде, скомпилированном модулем vm.

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

Этот параметр всё ещё является частью экспериментального API модулей. Не рекомендуется использовать его в рабочей среде.

Если параметр importModuleDynamically не указан или не определён

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

Если importModuleDynamically имеет значение vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER

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

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

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

Модули CJS

const { Script, constants } = require('node:vm');
const script = new Script(
  'import("node:fs").then(({readFile}) => readFile instanceof Function)',
  { importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER });

// false: URL loaded from the main context is not an instance of the Function
// class in the new context.
script.runInNewContext().then(console.log);

Модули MJS

import { Script, constants } from 'node:vm';

const script = new Script(
  'import("node:fs").then(({readFile}) => readFile instanceof Function)',
  { importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER });

// false: URL loaded from the main context is not an instance of the Function
// class in the new context.
script.runInNewContext().then(console.log);

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

Модули MJS

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

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

// Compile a script that loads test.mjs and then test.json
// as if the script is placed in the same directory.
const script = new Script(
  `(async function() {
    const { filename } = await import('./test.mjs');
    return import(filename, { with: { type: 'json' } })
  })();`,
  {
    filename: resolve(import.meta.dirname, 'test-with-default.js'),
    importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER,
  });

// { default: { hello: 'world' } }
script.runInThisContext().then(console.log);

Модули CJS

const { Script, constants } = require('node:vm');
const { resolve } = require('node:path');
const { writeFileSync } = require('node:fs');

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

// Compile a script that loads test.mjs and then test.json
// as if the script is placed in the same directory.
const script = new Script(
  `(async function() {
    const { filename } = await import('./test.mjs');
    return import(filename, { with: { type: 'json' } })
  })();`,
  {
    filename: resolve(__dirname, 'test-with-default.js'),
    importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER,
  });

// { default: { hello: 'world' } }
script.runInThisContext().then(console.log);

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

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

Если importModuleDynamically является функцией

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

У коллбэка importModuleDynamically(specifier, referrer, importAttributes) следующий сигнатура:

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

Модули MJS

// This script must be run with --experimental-vm-modules.
import { Script, SyntheticModule } from 'node:vm';

const script = new Script('import("foo.json", { with: { type: "json" } })', {
  async importModuleDynamically(specifier, referrer, importAttributes) {
    console.log(specifier);  // 'foo.json'
    console.log(referrer);   // The compiled script
    console.log(importAttributes);  // { type: 'json' }
    const m = new SyntheticModule(['bar'], () => { });
    await m.link(() => { });
    m.setExport('bar', { hello: 'world' });
    return m;
  },
});
const result = await script.runInThisContext();
console.log(result);  //  { bar: { hello: 'world' } }

Модули CJS

// This script must be run with --experimental-vm-modules.
const { Script, SyntheticModule } = require('node:vm');

(async function main() {
  const script = new Script('import("foo.json", { with: { type: "json" } })', {
    async importModuleDynamically(specifier, referrer, importAttributes) {
      console.log(specifier);  // 'foo.json'
      console.log(referrer);   // The compiled script
      console.log(importAttributes);  // { type: 'json' }
      const m = new SyntheticModule(['bar'], () => { });
      await m.link(() => { });
      m.setExport('bar', { hello: 'world' });
      return m;
    },
  });
  const result = await script.runInThisContext();
  console.log(result);  //  { bar: { hello: 'world' } }
})();

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

Spec-Zone.ru

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