VM (выполнение JavaScript)
Исходный код: 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
Экземпляры класса vm.Script содержат предварительно скомпилированные скрипты, которые могут быть выполнены в определённых контекстах.
new vm.Script(code[, options])
-
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() -
module<vm.Модуль> - Возвращает: <Объект пространства имён модуля> | <vm.Модуль> Рекомендуется возвращать
vm.Module, чтобы воспользоваться отслеживанием ошибок и избежать проблем с именованными пространствами, содержащими экспорт функцийthen.
-
-
Если options является строкой, то она указывает имя файла.
Создание нового объекта vm.Script компилирует code, но не выполняет его. Скомпилированный vm.Script может быть запущен несколько раз позже. code не привязан ни к одному глобальному объекту; вместо этого он привязывается перед каждым запуском, только для этого запуска.
script.createCachedData()
- Возвращает: <Буфер>
Создаёт кэш кода, который может быть использован с опцией 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(contextifiedObject[, options])
-
contextifiedObject<Объект> Объект, контекстуализированный методомvm.createContext(). -
options<Объект>-
displayErrors<логическое значение> Еслиtrue, в случае возникновенияErrorпри компиляцииcode, строка кода, вызвавшая ошибку, будет добавлена в трассировку стека. По умолчанию:true. -
timeout<целое число> Указывает количество миллисекунд, в течение которых будет выполнятьсяcode, прежде чем произойдёт завершение выполнения. Если выполнение будет завершено, будет выброшенаError. Это значение должно быть строго положительным целым числом. -
breakOnSigint<логическое значение> Еслиtrue, выполнение будет завершено при полученииSIGINT(Ctrl+C). Существующие обработчики события, подключённые черезprocess.on('SIGINT'), будут отключены во время выполнения скрипта, но продолжат работу после него. Если выполнение будет завершено, будет выброшенаError. По умолчанию: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 приведёт к запуску новых циклов событий и соответствующих потоков, что имеет ненулевой overhead производительности.
script.runInNewContext([contextObject[, options]])
-
contextObject<Объект> Объект, который будет контекстуализирован. Еслиundefined, будет создан новый объект. -
options<Объект>-
displayErrors<логическое> Еслиtrue, при компиляцииcode, если произойдёт ошибкаError, строка кода, вызвавшая ошибку, будет добавлена в трассировку стека. По умолчанию:true. -
timeout<целое> Указывает количество миллисекунд, которое необходимо для выполненияcodeперед завершением выполнения. Если выполнение будет завершено, будет выброшено исключениеError. Это значение должно быть строго положительным целым числом. -
breakOnSigint<логическое> Еслиtrue, выполнение будет завершено при получении сигналаSIGINT(Ctrl+C). Существующие обработчики события, прикрепленные с помощьюprocess.on('SIGINT'), будут отключены во время выполнения скрипта, но продолжат работу после него. Если выполнение будет прервано, будет выброшено исключениеError. По умолчанию: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.
-
-
- Возвращает: <любой> результат последнего оператора, выполненного в скрипте.
Сначала контекстуализирует заданный 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])
-
options<Объект>-
displayErrors<логическое> Еслиtrue, при компиляцииcode, если произойдёт ошибкаError, строка кода, вызвавшая ошибку, будет добавлена в трассировку стека. По умолчанию:true. -
timeout<целое> Указывает количество миллисекунд для выполненияcodeперед прерыванием выполнения. Если выполнение прервано, будет выброшено исключениеError. Это значение должно быть строго положительным целым числом. -
breakOnSigint<логическое> Еслиtrue, выполнение будет прервано при получении сигналаSIGINT(Ctrl+C). Существующие обработчики события, прикрепленные с помощьюprocess.on('SIGINT'), будут отключены во время выполнения скрипта, но продолжат работу после него. Если выполнение прервано, будет выброшено исключениеError. По умолчанию:false.
-
- Возвращает: <любой> результат последнего оператора, выполненного в скрипте.
Выполняет скомпилированный код, содержащийся в 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.Module
Эта функция доступна только с включенным флагом командной строки --experimental-vm-modules .
Класс vm.Module предоставляет низкоуровневый интерфейс для использования модулей ECMAScript в контекстах VM. Он является аналогом класса vm.Script, который тесно соответствует Записи модулей, определённым в спецификации ECMAScript.
В отличие от vm.Script, каждый объект vm.Module привязан к контексту с момента своего создания. Операции с объектами vm.Module являются асинхронными, в отличие от синхронной природы объектов vm.Script. Использование функций 'async' может помочь при манипулировании объектами vm.Module.
Использование объекта vm.Module требует трёх отдельных шагов: создание/парсинг, привязка и оценка. Эти три шага проиллюстрированы в следующем примере.
Эта реализация находится на более низком уровне, чем загрузчик модулей ECMAScript. Пока нет способа взаимодействовать с загрузчиком, но поддержка запланирована.
const vm = require('vm');
const contextifiedObject = 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 `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;
`, { 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 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.
})(); 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. По умолчанию:false.
-
- Возвращает: <Promise>
Оценивает модуль.
Этот метод должен вызываться после того, как модуль был привязан; в противном случае он выбросит ошибку. Его также можно вызвать, когда модуль уже был оценён, в этом случае он сделает одно из следующих двух действий:
- вернёт
undefined, если начальная оценка завершилась успешно (module.statusравно'evaluated') - повторно выбросит то же исключение, которое было выброшено при начальной оценке, если начальная оценка завершилась с ошибкой (
module.statusравно'errored')
Этот метод не может быть вызван во время оценки модуля (module.status равно 'evaluating') для предотвращения бесконечной рекурсии.
Соответствует полю Evaluate() конкретного метода записей модулей Cyclic Module Record в спецификации ECMAScript.
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() конкретного метода записей модулей Cyclic Module Record в спецификации ECMAScript.
module.namespace
Объект пространства имён модуля. Доступен только после завершения связывания (module.link()).
Соответствует абстрактной операции GetModuleNamespace в спецификации ECMAScript.
module.status
Текущий статус модуля. Может быть одним из следующих:
-
'unlinked': Методmodule.link()ещё не был вызван. -
'linking': Методmodule.link()был вызван, но не все обещания, возвращённые функцией связывания, ещё не разрешены. -
'linked': Модуль успешно связан, и все его зависимости связаны, но методmodule.evaluate()ещё не был вызван. -
'evaluating': Модуль оценивается черезmodule.evaluate()на себе или родительском модуле. -
'evaluated': Модуль успешно оценён. -
'errored': Модуль оценён, но было выброшено исключение.
За исключением 'errored', эта строка статуса соответствует полю [[Status]] записи циклического модуля в спецификации. 'errored' соответствует 'evaluated' в спецификации, но с [[EvaluationError]] установлено значением, которое не undefined.
module.identifier
Идентификатор текущего модуля, установленный при создании.
Класс: vm.SourceTextModule
Эта функция доступна только при включённом флаге команды --experimental-vm-modules.
- Расширяет: <vm.Module>
Класс vm.SourceTextModule предоставляет запись модуля Source Text Module Record, как определено в спецификации ECMAScript.
new vm.SourceTextModule(code[, options])
-
code<строка> Код JavaScript-модуля для парсинга -
options-
identifier<строка> Строка, используемая в трассировках стека. По умолчанию:'vm:module(i)', гдеi— контекстно-зависимый возрастающий индекс. -
cachedData<Буфер> | <TypedArray> | <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.Module> - Возвращает: <Объект пространства имён модуля> | <vm.Module> Рекомендуется возвращать
vm.Moduleдля использования отслеживания ошибок и для предотвращения проблем со пространствами имён, содержащими экспорт функцииthen.
-
-
Создаёт новый экземпляр SourceTextModule.
Свойства, присвоенные объекту import.meta, которые являются объектами, могут позволить модулю получить доступ к информации за пределами указанного context. Используйте vm.runInContext() для создания объектов в определённом контексте.
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()
- Возвращает: <Буфер>
Создаёт кэш кода, который можно использовать с параметром cachedData конструктора SourceTextModule. Возвращает буфер. Этот метод можно вызывать любое количество раз до оценки модуля.
// 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
Эта функция доступна только при включённом флаге команды --experimental-vm-modules
- Расширяет: <vm.Module>
Класс 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])
-
exportNames<string[]> Массив имён, которые будут экспортированы из модуля. -
evaluateCallback<Function> Вызывается при оценке модуля. -
options-
identifier<string> Строка, используемая в трассировках стека. По умолчанию:'vm:module(i)'гдеi— контекстно-специфичный возрастающий индекс. -
context<Object> Объект, контекстуализированный методомvm.createContext(), для компиляции и оценки данногоModuleв.
-
Создаёт новый экземпляр SyntheticModule.
Объекты, присвоенные экспорту этого экземпляра, могут позволить импортирующим модуль получить доступ к информации за пределами указанного context. Используйте vm.runInContext(), чтобы создать объекты в определённом контексте.
syntheticModule.setExport(name, value)
-
name<string> Имя экспорта для установки. -
value<any> Значение, на которое нужно установить экспорт.
Этот метод используется после привязки модуля для установки значений экспортов. Если он вызывается до привязки модуля, будет выброшено исключение ERR_VM_MODULE_STATUS.
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]])
-
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]])
-
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.
-
-
- Возвращает: <Object> Объект contextified.
Если задан 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)
Возвращает true, если заданный oject объект был контекстуализирован с помощью vm.createContext().
vm.runInContext(code, contextifiedObject[, options])
-
code<string> Код JavaScript для компиляции и выполнения. -
contextifiedObject<Объект> контекстуализированный объект, который будет использован в качествеglobalпри компиляции и выполненииcode. -
options<Объект> | <строка>-
filename<строка> Указывает имя файла, используемое в отладке стека, создаваемой этим скриптом. По умолчанию:'evalmachine.<anonymous>'. -
lineOffset<число> Указывает смещение номера строки, отображаемое в отладке стека, создаваемой этим скриптом. По умолчанию:0. -
columnOffset<число> Указывает смещение номера столбца, отображаемое в отладке стека, создаваемой этим скриптом. По умолчанию:0. -
displayErrors<логическое значение> Еслиtrue, при возникновенииErrorво время компиляцииcode, строка кода, вызвавшая ошибку, будет добавлена к отладке стека. По умолчанию:true. -
timeout<целое число> Указывает количество миллисекунд для выполненияcodeперед завершением выполнения. При завершении выполнения будет выброшена ошибкаError. Это значение должно быть положительным целым числом. -
breakOnSigint<логическое значение> Еслиtrue, выполнение будет завершено при получении сигналаSIGINT(Ctrl+C). Существующие обработчики события, прикрепленные черезprocess.on('SIGINT'), будут отключены во время выполнения скрипта, но будут продолжать работать после него. При завершении выполнения будет выброшена ошибкаError. По умолчанию: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() -
module<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]])
-
code<string> JavaScript-код для компиляции и выполнения. -
contextObject<Object> Объект, который будет контекстуализирован. Еслиundefined, будет создан новый объект. -
options<Object> | <string>-
filename<string> Указывает имя файла, используемое в отслеживании стека, создаваемом этим скриптом. По умолчанию:'evalmachine.<anonymous>'. -
lineOffset<number> Указывает смещение номера строки, отображаемое в отслеживании стека, создаваемом этим скриптом. По умолчанию:0. -
columnOffset<number> Указывает смещение номера столбца, отображаемое в отслеживании стека, создаваемом этим скриптом. По умолчанию:0. -
displayErrors<boolean> Еслиtrue, при возникновенииErrorпри компиляцииcode, строка кода, вызвавшая ошибку, добавляется к отслеживанию стека. По умолчанию:true. -
timeout<integer> Указывает количество миллисекунд для выполненияcodeперед завершением выполнения. Если выполнение завершается, будет выброшенаError. Это значение должно быть строго положительным целым числом. -
breakOnSigint<boolean> Еслиtrue, выполнение будет завершено при полученииSIGINT(Ctrl+C). Существующие обработчики события, присоединенные черезprocess.on('SIGINT'), будут отключены во время выполнения скрипта, но продолжат работать после него. Если выполнение завершается, будет выброшенаError. По умолчанию:false. -
contextName<string> Читабельное имя вновь созданного контекста. По умолчанию:'VM Context i', гдеi— восходящий числовой индекс созданного контекста. -
contextOrigin<string> Origin, соответствующий вновь созданному контексту для отображения. Исход должен быть отформатирован как URL, но только со схемой, хостом и портом (при необходимости), как значение свойстваurl.originобъектаURL. Важно отметить, что эта строка должна опускать конечный слэш, так как он обозначает путь. По умолчанию:''. -
contextCodeGeneration<Object>-
strings<boolean> Если установлено в false, все вызовыevalили конструкторов функций (Function,GeneratorFunction, и т. д.) будут вызыватьEvalError. По умолчанию:true. -
wasm<boolean> Если установлено в false, любая попытка скомпилировать модуль WebAssembly вызоветWebAssembly.CompileError. По умолчанию:true.
-
-
cachedData<Буфер> | <Тип массива> | <DataView> Предоставляет необязательныйBufferилиTypedArray, илиDataViewс данными кэша кода V8 для предоставленного источника. При предоставлении значениеcachedDataRejectedбудет установлено наtrueилиfalseв зависимости от принятия данных V8. -
produceCachedData<boolean> Когда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() -
module<vm.Модуль> - Возвращает: <Объект пространства имен модуля> | <vm.Модуль> Возврат
vm.Moduleрекомендуется для использования отслеживания ошибок и для избежания проблем с пространствами имен, содержащимиthenэкспорт функций.
-
-
- Возвращает: <любое> результат последнего оператора, выполненного в скрипте.
Метод 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])
-
code<string> JavaScript-код для компиляции и выполнения. -
options<Object> | <string>-
filename<string> Указывает имя файла, используемое в трассировках стека, генерируемых этим скриптом. По умолчанию:'evalmachine.<anonymous>'. -
lineOffset<number> Указывает смещение номера строки, отображаемое в трассировках стека, генерируемых этим скриптом. По умолчанию:0. -
columnOffset<number> Указывает смещение номера столбца, отображаемое в трассировках стека, генерируемых этим скриптом. По умолчанию:0. -
displayErrors<boolean> Приtrue, если при компиляцииcodeвозникает ошибкаError, строка кода, вызвавшая ошибку, добавляется в трассировку стека. По умолчанию:true. -
timeout<integer> Указывает количество миллисекунд для выполненияcodeперед завершением выполнения. Если выполнение завершится, будет выброшена ошибкаError. Это значение должно быть строго положительным целым числом. -
breakOnSigint<boolean> Еслиtrue, выполнение будет завершено при полученииSIGINT(Ctrl+C). Существующие обработчики события, присоединённые с помощьюprocess.on('SIGINT'), будут отключены во время выполнения скрипта, но продолжат работать после него. Если выполнение завершится, будет выброшена ошибкаError. По умолчанию:false. -
cachedData<Buffer> | <TypedArray> | <DataView> Предоставляет необязательныеBufferилиTypedArray, илиDataViewс данными кэша кода V8 для предоставленного источника. При предоставлении значениеcachedDataRejectedбудет установлено наtrueилиfalseв зависимости от принятия данных V8. -
produceCachedData<boolean> Еслиtrueи отсутствуетcachedData, V8 попытается создать данные кэша кода дляcode. При успехе будет созданBufferс данными кэша кода V8 и сохранён в свойствеcachedDataвозвращённого экземпляраvm.Script. ЗначениеcachedDataProducedбудет установлено наtrueилиfalseв зависимости от успешного создания данных кэша кода. Этот параметр устарел в пользуscript.createCachedData(). По умолчанию:false. -
importModuleDynamically<Function> Вызывается во время оценки этого модуля, когдаimport()вызывается. Если этот параметр не указан, вызовыimport()будут отклоняться сERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING. Этот параметр является частью экспериментального API модулей и не должен рассматриваться как стабильный.-
specifier<string> спецификатор, переданный вimport() -
module<vm.Module> - Возвращает: <Объект пространства имён модуля> | <vm.Module> Рекомендуется возвращать
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. Код, переданный в этот контекст виртуальной машины, будет иметь своё изолированное пространство имен.
Для запуска простого веб-сервера с использованием модуля 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 Embedder:
В V8 контекст — это среда выполнения, позволяющая выполнять отдельные, не связанные друг с другом, JavaScript-приложения в одном экземпляре V8. Вы должны явно указать контекст, в котором вы хотите выполнить любой JavaScript-код.
При вызове метода vm.createContext(), аргумент contextObject (или новый объект, если contextObject — undefined) внутренне связывается с новым экземпляром контекста V8. Этот контекст V8 обеспечивает code с использованием методов модуля vm изолированной глобальной средой, в которой он может функционировать. Процесс создания контекста V8 и его связывание с contextObject и есть то, что в этом документе называется "контекстуализацией" объекта.
Ограничения таймаута при использовании process.nextTick(), промисов и queueMicrotask()
Из-за внутренней работы очереди process.nextTick() и очереди микробных задач, лежащей в основе промисов, в V8 и Node.js, код, выполняющийся в контексте, может "выскользнуть" из набора таймаутов, используя 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() и queueMicrotask().
Эта проблема возникает, поскольку все контексты используют одну и ту же очередь микробных задач и очередь 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-v12.x/docs/api/vm.html