Spec-Zone.ru › Node.js 10 LTS

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

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

Модуль vm предоставляет API для компиляции и выполнения кода в контекстах виртуальной машины V8. Модуль vm не является механизмом безопасности. Не используйте его для запуска ненадежного кода. Термин «песочница» используется в этих документах для обозначения отдельного контекста и не предоставляет никаких гарантий безопасности.

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

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

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

const vm = require('vm');

const x = 1;

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

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

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

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

Класс: vm.SourceTextModule

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

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

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

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

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

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

const vm = require('vm');

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

(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 `contextifiedSandbox` 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;
  `, { context: contextifiedSandbox });

  // 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
        // "contextifiedSandbox" when creating the context.
        export default secret;
      `, { context: referencingModule.context });

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

  // Step 3
  //
  // Instantiate the top-level Module.
  //
  // Only the top-level Module needs to be explicitly instantiated; its
  // dependencies will be recursively instantiated by instantiate().

  bar.instantiate();

  // Step 4
  //
  // Evaluate the Module. The evaluate() method returns a Promise with a single
  // property "result" that contains the result of the very last statement
  // executed in the Module. In the case of `bar`, it is `s;`, which refers to
  // the default export of the `foo` module, the `secret` we set in the
  // beginning to 42.

  const { result } = await bar.evaluate();

  console.log(result);
  // Prints 42.
})();

Конструктор: new vm.SourceTextModule(code[, options])

  • code <строка> Код модуля JavaScript для парсинга
  • options

    • url <строка> URL, используемый в разрешении модулей и трассировках стека. По умолчанию: 'vm:module(i)' где i — контекстно-специфичный возрастающий индекс.
    • context <объект> Объект контекстуализации, возвращаемый методом vm.createContext(), для компиляции и оценки данного Module.
    • lineOffset <целое число> Указывает смещение номера строки, отображаемое в трассировках стека, созданных этим Module.
    • columnOffset <целое число> Указывает смещение номера столбца, отображаемое в трассировках стека, созданных этим Module.
    • initializeImportMeta <функция> Вызывается при оценке этого Module для инициализации import.meta. Эта функция имеет сигнатуру (meta, module), где meta — объект import.meta в Module, а module — этот объект vm.SourceTextModule.
    • importModuleDynamically <функция> Вызывается при оценке данного модуля при вызове import(). Эта функция имеет сигнатуру (specifier, module), где specifier — спецификатор, переданный import(), а module — этот объект vm.SourceTextModule. Если этот параметр не указан, вызовы import() отклонят запрос с ошибкой ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING. Этот метод может возвращать объект пространства имён модуля, но рекомендуется возвращать vm.SourceTextModule, чтобы воспользоваться отслеживанием ошибок и избежать проблем с пространствами имён, содержащими then экспорты функций.

Создаёт новый объект ES Module.

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

const vm = require('vm');

const contextifiedSandbox = 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 sandbox.
        meta.prop = {};
      }
    });
  // Since module has no dependencies, the linker function will never be called.
  await module.link(() => {});
  module.instantiate();
  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('{}', contextifiedSandbox);
})();

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). Существующие обработчики события, прикреплённые через process.on('SIGINT') будут отключены во время выполнения скрипта, но продолжат работать после него. Если выполнение прервано, будет выброшено исключение Error.
  • Возвращает: <Promise>

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

Этот метод должен быть вызван после создания экземпляра модуля; в противном случае будет выброшено исключение. Его также можно вызвать, когда модуль уже был оценён, в этом случае он сделает одно из двух:

  • вернёт undefined, если начальная оценка завершилась успешно (module.status равен 'evaluated')
  • перебросит то же исключение, которое было выброшено при начальной оценке, если начальная оценка завершилась ошибкой (module.status равен 'errored')

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

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

module.instantiate()

Создаёт экземпляр модуля. Этот метод должен быть вызван после завершения связывания (linkingStatus равен 'linked'); в противном случае будет выброшено исключение. Он также может выбросить исключение, если одна из зависимостей не предоставляет экспорт, необходимый родительскому модулю.

Однако, если эта функция выполнилась успешно, дальнейшие вызовы этой функции после первоначального создания экземпляра будут недействительными, чтобы соответствовать спецификации ECMAScript.

В отличие от других методов, работающих с объектами Module, эта функция завершается синхронно и не возвращает ничего.

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

module.link(linker)

  • linker <функция>
  • Возвращает: <Promise>

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

Функции linker будут переданы два параметра:

  • specifier Спецификатор запрошенного модуля:

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

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

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

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

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

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

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

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

module.linkingStatus

  • <строка>

Текущий статус связывания модуля module. Он может принимать одно из следующих значений:

  • 'unlinked': Функция module.link() ещё не была вызвана.
  • 'linking': Функция module.link() была вызвана, но ещё не все обещания, возвращённые функцией-связующим, не были разрешены.
  • 'linked': Функция module.link() была вызвана, и все её зависимости были успешно связаны.
  • 'errored': Функция module.link() была вызвана, но хотя бы одна из её зависимостей не смогла связаться, либо потому, что обратный вызов вернул объект Promise, который отклоняется, либо потому, что объект Module, возвращённый обратным вызовом, недействителен.

module.namespace

  • <Объект>

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

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

module.status

  • <строка>

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

  • 'uninstantiated': Модуль не создан. Это может быть по следующим причинам:

    • Модуль был только что создан.
    • module.instantiate() была вызвана для этого модуля, но по какой-то причине потерпела неудачу.

    Этот статус не содержит никакой информации о том, была ли вызвана module.link(). Для этого см. module.linkingStatus.

  • 'instantiating': Модуль в настоящее время создаётся через вызов module.instantiate() на нём самом или родительском модуле.

  • 'instantiated': Модуль был успешно создан, но module.evaluate() ещё не был вызван.

  • 'evaluating': Модуль оценивается через вызов module.evaluate() на нём самом или родительском модуле.

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

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

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

module.url

  • <строка>

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

Класс: 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 <строка> Указывает имя файла, используемое в отладке стека, созданной этим скриптом.
    • lineOffset <число> Указывает смещение номера строки, отображаемое в отладке стека, созданной этим скриптом.
    • columnOffset <число> Указывает смещение номера столбца, отображаемое в отладке стека, созданной этим скриптом.
    • 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().
    • importModuleDynamically <Функция> Вызывается во время оценки этого модуля, когда вызывается import(). Эта функция имеет сигнатуру (specifier, module), где specifier — спецификатор, переданный import(), а module — этот объект vm.SourceTextModule. Если эта опция не указана, вызовы import() будут отклонены с ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING. Этот метод может возвращать объект Модуль Объект Пространства Имён, но рекомендуется возвращать vm.SourceTextModule, чтобы воспользоваться отслеживанием ошибок и избежать проблем с пространствами имён, содержащими экспорт функций then.

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

script.createCachedData()

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

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

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(contextifiedSandbox[, options])[src]

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

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

v0.3.1

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

  • contextifiedSandbox <Объект> Объект, возвращаемый методом vm.createContext(), контекстуализированный (contextified).
  • options <Объект>

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

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

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

const util = require('util');
const vm = require('vm');

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

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

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

console.log(util.inspect(sandbox));

// { animal: 'cat', count: 12, name: 'kitty' }

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

script.runInNewContext([sandbox[, options]])[src]

История
Версия Изменения
v10.0.0

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

v0.3.1

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

  • sandbox <Объект> Объект, который будет контекстуализирован. Если undefined, будет создан новый объект.
  • options <Объект>

    • filename <строка> Указывает имя файла, используемое в отслеживании стека, создаваемом этим скриптом.
    • lineOffset <число> Указывает смещение номера строки, отображаемое в отслеживании стека, создаваемом этим скриптом.
    • columnOffset <число> Указывает смещение номера столбца, отображаемое в отслеживании стека, создаваемом этим скриптом.
    • displayErrors <логическое> Если true, при возникновении ошибки Error во время компиляции code, строка кода, вызвавшая ошибку, будет добавлена к отслеживанию стека.
    • timeout <целое> Указывает количество миллисекунд для выполнения code перед завершением выполнения. Если выполнение будет завершено, будет брошена ошибка Error. Это значение должно быть строго положительным целым числом.
    • contextName <строка> Читаемое человеком имя нового контекста. По умолчанию: 'VM Context i', где i — возрастающий числовой индекс созданного контекста.
    • contextOrigin <строка> Происхождение соответствующего созданному контексту для отображения. Происхождение должно быть отформатировано как URL, но содержать только схему, хост и порт (при необходимости), подобно значению свойства url.origin объекта URL. Важно, что эта строка должна опускать конечный слеш, так как он обозначает путь. По умолчанию: ''.
    • contextCodeGeneration <Объект>

      • strings <логическое> Если установлено в false, любые вызовы eval или конструкторов функций (Function, GeneratorFunction, и т.д.) будут вызывать ошибку EvalError. По умолчанию: true.
      • wasm <логическое> Если установлено в false, любая попытка скомпилировать модуль WebAssembly вызовет ошибку WebAssembly.CompileError. По умолчанию: true.

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

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

const util = require('util');
const vm = require('vm');

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

const sandboxes = [{}, {}, {}];
sandboxes.forEach((sandbox) => {
  script.runInNewContext(sandbox);
});

console.log(util.inspect(sandboxes));

// [{ globalVar: 'set' }, { globalVar: 'set' }, { globalVar: 'set' }]

script.runInThisContext([options])[src]

Добавлена в: v0.3.1
  • options <Объект>

    • filename <строка> Указывает имя файла, используемое в отслеживании стека, создаваемом этим скриптом.
    • lineOffset <число> Указывает смещение номера строки, отображаемое в отслеживании стека, создаваемом этим скриптом.
    • columnOffset <число> Указывает смещение номера столбца, отображаемое в отслеживании стека, создаваемом этим скриптом.
    • displayErrors <логическое> Если true, при возникновении ошибки Error во время компиляции code, строка кода, вызвавшая ошибку, будет добавлена к отслеживанию стека.
    • timeout <целое> Указывает количество миллисекунд для выполнения code перед завершением выполнения. Если выполнение будет завершено, будет брошена ошибка Error. Это значение должно быть строго положительным целым числом.

Выполняет скомпилированный код, содержащийся в 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.compileFunction(code[, params[, options]])[src]

Добавлена в: 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[]> Массив, содержащий набор расширений контекста (объекты, оборачивающие текущую область видимости), которые должны быть применены во время компиляции. По умолчанию: [].

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

vm.createContext([sandbox[, options]])[src]

История
Версия Изменения
v10.0.0

Опция sandbox больше не может быть функцией.

v10.0.0

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

v0.3.1

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

  • sandbox <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.

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

const util = require('util');
const vm = require('vm');

global.globalVar = 3;

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

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

console.log(util.inspect(sandbox)); // { globalVar: 2 }

console.log(util.inspect(globalVar)); // 3

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

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

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

vm.isContext(sandbox)[src]

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

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

vm.runInContext(code, contextifiedSandbox[, options])[src]

  • code <string> JavaScript-код для компиляции и выполнения.
  • contextifiedSandbox <Object> Объект контекстуализации, который будет использован в качестве global при компиляции и выполнении code.
  • options <Object> | <string>

    • filename <string> Указывает имя файла, используемое в трассировках стека, созданных этим скриптом.
    • lineOffset <number> Указывает смещение номера строки, отображаемое в трассировках стека, созданных этим скриптом.
    • columnOffset <number> Указывает смещение номера столбца, отображаемое в трассировках стека, созданных этим скриптом.
    • displayErrors <boolean> При true, если при компиляции code возникает ошибка Error, строка кода, вызвавшая ошибку, добавляется к трассировке стека.
    • timeout <integer> Указывает количество миллисекунд для выполнения code перед завершением выполнения. Если выполнение прерывается, будет выброшено исключение Error. Это значение должно быть целым положительным числом.

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

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

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

const util = require('util');
const vm = require('vm');

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

for (let i = 0; i < 10; ++i) {
  vm.runInContext('globalVar *= 2;', sandbox);
}
console.log(util.inspect(sandbox));

// { globalVar: 1024 }

vm.runInNewContext(code[, sandbox[, options]])[src]

Добавлена в: v0.3.1
  • code <строка> JavaScript-код для компиляции и выполнения.
  • sandbox <Объект> Объект, который будет контекстуализирован. Если undefined, будет создан новый объект.
  • options <Объект> | <строка>

    • filename <строка> Указывает имя файла, используемое в отслеживании стека, созданном этим скриптом.
    • lineOffset <число> Указывает смещение номера строки, отображаемое в отслеживании стека, созданном этим скриптом.
    • columnOffset <число> Указывает смещение номера столбца, отображаемое в отслеживании стека, созданном этим скриптом.
    • displayErrors <логическое значение> При true, если при компиляции code возникает ошибка Error, строка кода, вызвавшая ошибку, добавляется в отслеживание стека.
    • timeout <целое число> Указывает количество миллисекунд для выполнения code перед завершением выполнения. Если выполнение завершается, будет выброшена ошибка Error. Это значение должно быть строго положительным целым числом.
    • contextName <строка> Читаемое человеком имя нового контекста. По умолчанию: 'VM Context i', где i — возрастающий числовой индекс созданного контекста.
    • contextOrigin <строка> Происхождение, соответствующее новому контексту для отображения. Происхождение должно быть отформатировано как URL, но содержать только схему, хост и порт (при необходимости), как значение свойства url.origin объекта URL. В первую очередь, эта строка должна опускать конечный слэш, поскольку он обозначает путь. По умолчанию: ''.

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

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

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

const util = require('util');
const vm = require('vm');

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

vm.runInNewContext('count += 1; name = "kitty"', sandbox);
console.log(util.inspect(sandbox));

// { animal: 'cat', count: 3, name: 'kitty' }

vm.runInThisContext(code[, options])[src]

Добавлена в: v0.3.1
  • code <строка> JavaScript-код для компиляции и выполнения.
  • options <Объект> | <строка>

    • filename <строка> Указывает имя файла, используемое в отслеживании стека, созданном этим скриптом.
    • lineOffset <число> Указывает смещение номера строки, отображаемое в отслеживании стека, созданном этим скриптом.
    • columnOffset <число> Указывает смещение номера столбца, отображаемое в отслеживании стека, созданном этим скриптом.
    • displayErrors <логическое значение> При true, если при компиляции code возникает ошибка Error, строка кода, вызвавшая ошибку, добавляется в отслеживание стека.
    • timeout <целое число> Указывает количество миллисекунд для выполнения code перед завершением выполнения. Если выполнение завершается, будет выброшена ошибка Error. Это значение должно быть строго положительным целым числом.

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);
console.log('localVar:', localVar);

const evalResult = eval('localVar = "eval";');
console.log('evalResult:', evalResult);
console.log('localVar:', localVar);

// vmResult: 'vm', localVar: 'initial value'
// 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(), объект sandbox, переданный в метод (или новый объект, если sandbox — undefined), связывается внутренне с новой инстанцией контекста V8. Этот контекст V8 предоставляет code для использования методами модуля vm с изолированной глобальной средой, в которой он может работать. Процесс создания контекста V8 и его связывания с объектом sandbox и есть то, что в этом документе называется «контекстуализацией» объекта sandbox.

Ограничения таймаутов при использовании process.nextTick() и Promises

Из-за внутренней механики реализации очереди process.nextTick() и очереди микрозадач, на которой основаны Promises в V8 и Node.js, код, работающий в контексте, может «вырваться» из набора timeout с помощью vm.runInContext(), vm.runInNewContext() и vm.runInThisContext().

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

const vm = require('vm');

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

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

Эта проблема также возникает, когда вызов loop() планируется с помощью функции process.nextTick().

Эта проблема возникает, потому что все контексты делят одну и ту же очередь микрозадач и nextTick.

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

Spec-Zone.ru

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