Spec-Zone.ru › Node.js 6 LTS

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

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

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

Код 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 не является механизмом безопасности. Не используйте его для выполнения недоверенного кода.

Класс: vm.Script

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

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

new vm.Script(code, options)

Добавлен в: v0.3.1
  • code <строка> Код JavaScript для компиляции.
  • options
    • filename <строка> Указывает имя файла, используемое в трассировках стека, созданных этим скриптом.
    • lineOffset <число> Указывает смещение номера строки, отображаемое в трассировках стека, созданных этим скриптом.
    • columnOffset <число> Указывает смещение номера столбца, отображаемое в трассировках стека, созданных этим скриптом.
    • displayErrors <логическое значение> Когда true, если при компиляции code возникает ошибка Error, строка кода, вызвавшая ошибку, добавляется к трассировке стека.
    • timeout <число> Указывает количество миллисекунд для выполнения code перед завершением выполнения. Если выполнение завершается, будет выброшена ошибка Error.
    • cachedData <Буфер> Предоставляет необязательные данные Buffer с данными кэша кода V8 для предоставленного исходного кода. При предоставлении значение cachedDataRejected будет установлено на true или false в зависимости от принятия данных V8.
    • produceCachedData <логическое значение> Когда true и отсутствует cachedData, V8 попытается создать данные кэша кода для code. В случае успеха будет создан Buffer с данными кэша кода V8 и сохранён в свойстве cachedData возвращённого экземпляра vm.Script. Значение cachedDataProduced будет установлено на true или false в зависимости от успешного создания данных кэша кода.

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

script.runInContext(contextifiedSandbox[, options])

Добавлен в: v0.3.1
  • contextifiedSandbox <Объект> Объект, контекст которого установлен, возвращаемый методом vm.createContext().
  • options <Объект>
    • filename <строка> Указывает имя файла, используемое в трассировках стека, созданных этим скриптом.
    • lineOffset <число> Указывает смещение номера строки, отображаемое в трассировках стека, созданных этим скриптом.
    • columnOffset <число> Указывает смещение номера столбца, отображаемое в трассировках стека, созданных этим скриптом.
    • displayErrors <логическое значение> Когда true, если при компиляции code возникает ошибка Error, строка кода, вызвавшая ошибку, добавляется к трассировке стека.
    • timeout <число> Указывает количество миллисекунд для выполнения code перед завершением выполнения. Если выполнение завершается, будет выброшена ошибка Error.
    • breakOnSigint: если true, выполнение будет завершено при получении SIGINT (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])

Добавлен в: v0.3.1
  • sandbox <Объект> Объект, контекст которого будет установлен. Если undefined, будет создан новый объект.
  • options <Объект>
    • filename <строка> Указывает имя файла, используемое в трассировках стека, созданных этим скриптом.
    • lineOffset <число> Указывает смещение номера строки, отображаемое в трассировках стека, созданных этим скриптом.
    • columnOffset <число> Указывает смещение номера столбца, отображаемое в трассировках стека, созданных этим скриптом.
    • displayErrors <логическое значение> Когда true, если при компиляции code возникает ошибка Error, строка кода, вызвавшая ошибку, добавляется к трассировке стека.
    • timeout <число> Указывает количество миллисекунд для выполнения code перед завершением выполнения. Если выполнение завершается, будет выброшена ошибка Error.

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

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

Добавлен в: v0.3.1
  • options <Объект>
    • filename <строка> Указывает имя файла, используемое в трассировках стека, созданных этим скриптом.
    • lineOffset <число> Указывает смещение номера строки, отображаемое в трассировках стека, созданных этим скриптом.
    • columnOffset <число> Указывает смещение номера столбца, отображаемое в трассировках стека, созданных этим скриптом.
    • displayErrors <логическое> Когда true, если при компиляции code возникает ошибка Error, строка кода, вызвавшая ошибку, прикрепляется к трассировке стека.
    • 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.createContext([sandbox])

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

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

vm.isContext(sandbox)

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

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

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

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

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

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

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.runInDebugContext(code)

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

Метод vm.runInDebugContext() компилирует и выполняет code в контексте отладки V8. Основной случай использования — получение доступа к объекту V8 Debug.

const vm = require('vm');
const Debug = vm.runInDebugContext('Debug');
console.log(Debug.findScript(process.emit).name);  // 'events.js'
console.log(Debug.findScript(process.exit).name);  // 'internal/process.js'

Примечание: Контекст отладки и объект тесно связаны с реализацией отладчика V8 и могут быть изменены (или даже удалены) без предварительного уведомления.

Объект Debug также можно сделать доступным, используя V8-специфический --expose_debug_as= флаг командной строки.

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

Добавлен в: v0.3.1
  • code <строка> JavaScript-код для компиляции и выполнения.
  • sandbox <Объект> Объект, который будет подготовлен к использованию в контексте. Если undefined, будет создан новый объект.
  • options
    • filename <строка> Указывает имя файла, используемое в трассировках стека, созданных этим скриптом.
    • lineOffset <число> Указывает смещение номера строки, отображаемое в трассировках стека, созданных этим скриптом.
    • columnOffset <число> Указывает смещение номера столбца, отображаемое в трассировках стека, созданных этим скриптом.
    • displayErrors <логическое> Когда true, если при компиляции code возникает ошибка Error, строка кода, вызвавшая ошибку, прикрепляется к трассировке стека.
    • timeout <число> Указывает количество миллисекунд для выполнения code перед завершением выполнения. Если выполнение прервано, будет брошено исключение Error.

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

Следующий пример компилирует и выполняет код, увеличивающий глобальную переменную и устанавливающий новую. Эти глобальные переменные находятся в 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])

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

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

Следующий пример демонстрирует использование как 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. Код, переданный в этот контекст виртуальной машины, будет иметь свое изолированное пространство имен.

Для запуска простого веб-сервера с помощью модуля 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.

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

Spec-Zone.ru

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