Spec-Zone.ru › Node.js 14 LTS

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

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

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

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

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

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

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

const vm = require('vm');

const x = 1;

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

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

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

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

Класс: vm.Script

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

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

new vm.Script(code[, options])

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

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

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

script.createCachedData()

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

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

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

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

const cacheWithoutX = script.createCachedData();

script.runInThisContext();

const cacheWithX = script.createCachedData();

script.runInContext(contextifiedObject[, options])

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

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

v0.3.1

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

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

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

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

const vm = require('vm');

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

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

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

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

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

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

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

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

v10.0.0

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

v6.3.0

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

v0.3.1

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

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

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

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

const vm = require('vm');

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

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

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

script.runInThisContext([options])

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

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

v0.3.1

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

  • options <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('vm');

global.globalVar = 0;

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

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

console.log(globalVar);

// 1000

vm.measureMemory([options])

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

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

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

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

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

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

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

Класс: vm.Module

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

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

Класс vm.Module предоставляет низкоуровневый интерфейс для использования модулей ECMAScript в контекстах VM. Он является аналогом класса vm.Script, который тесно соответствует записям модулей Module Record, как определено в спецификации ECMAScript.

В отличие от vm.Script, каждый объект vm.Module привязан к контексту с момента его создания. Операции с объектами vm.Module по своей природе асинхронны, в отличие от синхронного характера объектов vm.Script. Использование асинхронных функций может помочь при работе с объектами vm.Module.

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

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

import vm from 'vm';

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

module.dependencySpecifiers

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

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

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

module.error

  • <любое значение>

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

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

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

module.evaluate([options])

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

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

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

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

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

module.identifier

  • <строка>

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

module.link(linker)

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

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

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

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

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

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

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

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

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

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

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

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

Соответствует полю Link() concrete method записей модулей Cyclic Module Record в спецификации 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]] записи модуля Cyclic Module Record в спецификации. '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])

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

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

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

import vm from 'vm';

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

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

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

sourceTextModule.createCachedData()

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

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

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

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

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

Класс: vm.SyntheticModule

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

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

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

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

const vm = require('vm');

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

// Use `module` in linking...

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

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

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

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

syntheticModule.setExport(name, value)

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

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

import vm from 'vm';

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

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

assert.strictEqual(m.namespace.x, 1);
const vm = require('vm');
(async () => {
  const m = new vm.SyntheticModule(['x'], () => {
    m.setExport('x', 1);
  });
  await m.link(() => {});
  await m.evaluate();
  assert.strictEqual(m.namespace.x, 1);
})();

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

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

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

v14.1.0, v13.14.0

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

v10.10.0

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

  • code <string> Тело функции для компиляции.
  • params <string[]> Массив строк, содержащих все параметры функции.
  • options <Object>
    • filename <string> Указывает имя файла, используемое в отладке стека, созданной этим скриптом. По умолчанию: ''.
    • lineOffset <number> Указывает смещение номера строки, отображаемое в отладке стека, созданной этим скриптом. По умолчанию: 0.
    • columnOffset <number> Указывает смещение номера столбца первой строки, отображаемое в отладке стека, созданной этим скриптом. По умолчанию: 0.
    • cachedData <Buffer> | <TypedArray> | <DataView> Предоставляет необязательный Buffer или TypedArray, или DataView с данными кэша кода V8 для предоставленного источника.
    • produceCachedData <boolean> Указывает, нужно ли генерировать новые данные кэша. По умолчанию: false.
    • parsingContext <Object> Объект контексте, в котором данная функция должна быть скомпилирована.
    • contextExtensions <Object[]> Массив, содержащий набор расширений контекста (объекты, оборачивающие текущую область видимости), которые должны быть применены во время компиляции. По умолчанию: [].
  • Возвращает: <Function>

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

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

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

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

v10.0.0

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

v10.0.0

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

v0.3.1

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

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

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

const vm = require('vm');

global.globalVar = 3;

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

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

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

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

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

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

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

vm.isContext(object)

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

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

END_OF_DOCUMENT_MARKER

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

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

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

v0.3.1

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

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

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

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

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

const vm = require('vm');

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

for (let i = 0; i < 10; ++i) {
  vm.runInContext('globalVar *= 2;', contextObject);
}
console.log(contextObject);
// Prints: { globalVar: 1024 }

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

История
Версия Изменения
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 для предоставленного исходного кода. При предоставлении значение 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>
      • Возвращает: <объект пространства имён модуля> | <vm.Module> Возврат vm.Module рекомендуется для использования отслеживания ошибок и для избежания проблем с пространствами имён, содержащими экспорт функций then.
    • microtaskMode <строка> Если установлено в afterEvaluate, микрозадачи (задачи, запланированные с помощью Promise и async function ) будут выполнены немедленно после выполнения скрипта. В этом случае они включены в области timeout и breakOnSigint.
  • Возвращает: <любое> результат последнего оператора, выполненного в скрипте.

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

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

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

const vm = require('vm');

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

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

vm.runInThisContext(code[, options])

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

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

v0.3.1

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

vm.runInThisContext(code)(require);

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

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

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

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

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

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

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

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

const vm = require('vm');

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

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

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

const vm = require('vm');

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

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

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

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

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

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

Spec-Zone.ru

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