Spec-Zone.ru › Node.js 8 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)

История
Версия Изменения
v5.7.0

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

v0.3.1

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

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

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

script.runInContext(contextifiedSandbox[, options])

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

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

v0.3.1

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

  • contextifiedSandbox <Объект> Контекстуализированный объект, возвращённый методом vm.createContext().
  • options <Объект>
    • filename <строка> Указывает имя файла, используемое в трассировках стека, сгенерированных этим скриптом.
    • lineOffset <число> Указывает смещение номера строки, отображаемое в трассировках стека, сгенерированных этим скриптом.
    • columnOffset <число> Указывает смещение номера столбца, отображаемое в трассировках стека, сгенерированных этим скриптом.
    • displayErrors <булево> Если true, при возникновении ошибки Error во время компиляции code, строка кода, вызвавшая ошибку, будет добавлена к трассировке стека.
    • 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, при возникновении ошибки Error во время компиляции code, строка кода, вызвавшая ошибку, будет добавлена к трассировке стека.
    • 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 будет глобальным объектом, сохраняя все свои существующие свойства, а также встроенные объекты и функции, имеющиеся у стандартного глобального объекта. Вне скриптов, выполняемых модулем 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
Стабильность: 0 - Устарело. Разрабатывается альтернатива.
  • 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-v8.x/docs/api/vm.html

Spec-Zone.ru

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