Виртуальная машина (выполнение JavaScript)
Модуль 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
Экземпляры класса vm.Script содержат предварительно скомпилированные скрипты, которые могут быть выполнены в конкретных песочницах (или "контекстах").
new vm.Script(code, options)
-
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])
-
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])
-
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])
-
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])
-
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)
-
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)
-
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])
-
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])
-
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