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() -
script<vm.Script> -
importAssertions<объект> Значение"assert", переданное в необязательный параметрoptionsExpression, или пустой объект, если значение не было предоставлено. - Возвращает: <Объект пространства имён модуля> | <vm.Модуль> Рекомендуется возвращать
vm.Moduleдля использования отслеживания ошибок и для избежания проблем с именованными пространствами, содержащимиthenэкспорт функций.
-
-
Если options является строкой, то она указывает имя файла.
Создание нового объекта vm.Script компилирует code, но не выполняет его. Скомпилированный vm.Script может быть запущен позже многократно. code не привязан ни к одному глобальному объекту; вместо этого он привязывается перед каждым запуском, только для этого запуска.
script.createCachedData()
- Возвращает: <Буфер>
Создаёт кэш кода, который может быть использован с параметром cachedData конструктора Script. Возвращает Buffer. Этот метод может быть вызван в любое время и любое количество раз.
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, если во время компиляцииcodeпроизойдёт ошибкаError, строка кода, вызвавшая ошибку, будет добавлена к трассировке стека. По умолчанию:true. -
timeout<целое число> Указывает количество миллисекунд для выполненияcodeдо завершения выполнения. Если выполнение прервано, будет выброшена ошибкаError. Это значение должно быть строго положительным целым числом. -
breakOnSigint<логическое значение> Еслиtrue, получениеSIGINT(Ctrl+C) прервёт выполнение и выбросит ошибкуError. Существующие обработчики события, присоединённые черезprocess.on('SIGINT'), будут отключены во время выполнения скрипта, но продолжат работать после него. По умолчанию: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 приведёт к запуску новых циклов событий и соответствующих потоков, что приведёт к ненулевой производительности.
script.runInNewContext([contextObject[, options]])
-
contextObject<Объект> Объект, который будет контекстуализирован. Еслиundefined, будет создан новый объект. -
options<Объект>-
displayErrors<boolean> Когдаtrue, если при компиляцииcodeвозникаетError, строка кода, вызвавшая ошибку, добавляется в стек вызовов. По умолчанию:true. -
timeout<целое> Указывает количество миллисекунд, в течение которых выполняетсяcodeперед завершением выполнения. Если выполнение прервано, будет брошено исключениеError. Это значение должно быть строго положительным целым числом. -
breakOnSigint<boolean> Еслиtrue, получениеSIGINT(Ctrl+C) завершит выполнение и выбросит исключениеError. Существующие обработчики события, прикрепленные черезprocess.on('SIGINT'), будут отключены во время выполнения скрипта, но продолжат работать после него. По умолчанию:false. -
contextName<строка> Читабельное имя нового контекста. По умолчанию:'VM Context i', гдеi— возрастающий числовой индекс созданного контекста. -
contextOrigin<строка> Происхождение, соответствующее новому контексту для отображения. Происхождение должно быть отформатировано как URL, но содержать только схему, хост и порт (при необходимости), как значение свойстваurl.originобъектаURL. Важно отметить, что эта строка должна опускать конечный слэш, поскольку он обозначает путь. По умолчанию:''. -
contextCodeGeneration<Объект>-
strings<boolean> Если установлено в false, любые вызовыevalили конструкторов функций (Function,GeneratorFunction, и т.д.) выбросят исключениеEvalError. По умолчанию:true. -
wasm<boolean> Если установлено в false, любая попытка скомпилировать модуль WebAssembly выбросит исключениеWebAssembly.CompileError. По умолчанию:true.
-
-
microtaskMode<строка> Если установлено вafterEvaluate, микрозадачи (задачи, запланированные черезPromiseиasync function) будут выполняться сразу после выполнения скрипта. В этом случае они включаются в областиtimeoutиbreakOnSigint.
-
- Возвращает: <любое> результат последнего оператора, выполненного в скрипте.
Сначала контекстуализирует заданный 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<boolean> Когдаtrue, если при компиляцииcodeвозникаетError, строка кода, вызвавшая ошибку, добавляется в стек вызовов. По умолчанию:true. -
timeout<целое> Указывает количество миллисекунд, в течение которых выполняетсяcodeперед завершением выполнения. Если выполнение прервано, будет брошено исключениеError. Это значение должно быть строго положительным целым числом. -
breakOnSigint<boolean> Еслиtrue, получениеSIGINT(Ctrl+C) завершит выполнение и выбросит исключениеError. Существующие обработчики события, прикрепленные черезprocess.on('SIGINT'), будут отключены во время выполнения скрипта, но продолжат работать после него. По умолчанию: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. Использование асинхронных функций может помочь в управлении объектами vm.Module.
Использование объекта vm.Module требует трёх отдельных шагов: создание/парсинг, связывание и оценка. Эти три шага проиллюстрированы в следующем примере.
Эта реализация расположена на более низком уровне, чем загрузчик модулей ECMAScript. Пока нет возможности взаимодействовать с Загрузчиком, хотя поддержка планируется.
Модули MJS
import vm from 'vm';
const contextifiedObject = vm.createContext({
secret: 42,
print: console.log,
});
// 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;
print(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 which will
// resolve after the module has finished evaluating.
// Prints 42.
await bar.evaluate();
Модули CJS
const vm = require('vm');
const contextifiedObject = vm.createContext({
secret: 42,
print: console.log,
});
(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;
print(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 which will
// resolve after the module has finished evaluating.
// Prints 42.
await bar.evaluate();
})();
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#) завершит выполнение и выбросит исключениеError. Существующие обработчики события, присоединённые черезprocess.on('SIGINT'), будут отключены во время выполнения скрипта, но продолжат работать после этого. По умолчанию:false.
-
- Возвращает: <Promise> Успешно выполняется с
undefinedпри успешном выполнении.
Выполняет оценку модуля.
Это должно быть вызвано после связывания модуля; в противном случае произойдёт отклонение. Также может быть вызвано, когда модуль уже был оценен, в этом случае либо ничего не произойдёт, если начальная оценка завершилась успешно (module.status равно 'evaluated' ), либо будет повторно выброшено исключение, с которым завершилась начальная оценка (module.status равно 'errored').
Этот метод нельзя вызывать, пока модуль оценивается (module.status равно 'evaluating').
Соответствует полю Evaluate() concrete method записей циклических модулей в спецификации ECMAScript.
module.identifier
Идентификатор текущего модуля, установленный в конструкторе.
module.link(linker)
-
linker<Функция>-
specifier<строка> Указатель запрашиваемого модуля:import foo from 'foo'; // ^^^^^ the module specifier
-
extra<Объект>-
assert<Объект> Данные из проверки:import foo from 'foo' assert { name: 'value' }; // ^^^^^^^^^^^^^^^^^ the assertionПо ECMA-262, ожидается, что хосты проигнорируют проверки, которые они не поддерживают, в отличие, например, от выдачи ошибки, если присутствует неподдерживаемая проверка.
-
-
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.
Функция связывания примерно соответствует определённой в спецификации ECMAScript абстрактной операции HostResolveImportedModule с некоторыми ключевыми различиями:
- Функции связывания разрешено быть асинхронной, в то время как HostResolveImportedModule является синхронной.
Фактическая реализация HostResolveImportedModule, используемая во время связывания модулей, - это такая, которая возвращает модули, связанные во время связывания. Поскольку на этом этапе все модули уже будут полностью связаны, реализация HostResolveImportedModule полностью синхронна в соответствии со спецификацией.
Соответствует полю Link() concrete method записей циклических модулей в спецификации ECMAScript.
module.namespace
Объект пространства имён модуля. Доступен только после завершения связывания (module.link()).
Соответствует абстрактной операции GetModuleNamespace в спецификации ECMAScript.
module.status
Текущий статус модуля. Может быть одним из:
-
'unlinked': Методmodule.link()ещё не был вызван. -
'linking': Методmodule.link()был вызван, но ещё не были разрешены все Promises, возвращённые функцией связывания. -
'linked': Модуль был успешно связан, и все его зависимости связаны, ноmodule.evaluate()ещё не был вызван. -
'evaluating': Модуль оценивается черезmodule.evaluate()на себе или родительском модуле. -
'evaluated': Модуль успешно оценён. -
'errored': Модуль был оценён, но было выброшено исключение.
За исключением 'errored', эта строка состояния соответствует полю [[Status]] записи циклического модуля в спецификации. 'errored' соответствует 'evaluated' в спецификации, но с [[EvaluationError]] установленным в значение, которое не равно undefined.
Класс: vm.SourceTextModule
Эта функция доступна только при включённом флаге командной строки --experimental-vm-modules.
- Расширяет: <vm.Module>
Класс vm.SourceTextModule предоставляет запись Записи модуля исходного текста, как определено в спецификации ECMAScript.
new vm.SourceTextModule(code[, options])
-
code<строка> Код модуля JavaScript для парсинга -
options-
identifier<строка> Строка, используемая в следах стека. По умолчанию:'vm:module(i)', гдеi— контекстно-зависимый возрастающий индекс. -
cachedData<Буфер> | <Массив с типом данных> | <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> -
importAssertions<Объект> Значение"assert", переданное необязательному параметруoptionsExpression, или пустой объект, если значение не было предоставлено. - Возвращает: <Объект пространства имён модуля> | <vm.Module> Рекомендуется возвращать
vm.Module, чтобы воспользоваться отслеживанием ошибок и избежать проблем с пространствами имён, которые содержат экспорт функцийthen.
-
-
Создаёт новый экземпляр SourceTextModule.
Свойства, присвоенные объекту import.meta, которые являются объектами, могут позволить модулю получить доступ к информации за пределами указанного context. Используйте vm.runInContext() для создания объектов в определённом контексте.
MJS-модули
import vm from 'vm';
const contextifiedObject = vm.createContext({ secret: 42 });
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);
CJS-модули
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. Возвращает Buffer. Этот метод может быть вызван любое количество раз до оценки модуля.
// 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 предоставляет Запись синтетического модуля, как определено в спецификации 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<массив строк> Массив имён, которые будут экспортированы из модуля. -
evaluateCallback<Функция> Вызывается при оценке модуля. -
options
Создаёт новый экземпляр SyntheticModule.
Объекты, присвоенные экспорту этого экземпляра, могут позволить импортёрам модуля получить доступ к информации за пределами указанного context. Используйте vm.runInContext() для создания объектов в определённом контексте.
syntheticModule.setExport(name, value)
Этот метод используется после связывания модуля для установки значений экспортов. Если он вызывается до связывания модуля, будет выброшена ошибка ERR_VM_MODULE_STATUS.
MJS-модули
import vm from 'vm';
const m = new vm.SyntheticModule(['x'], () => {
m.setExport('x', 1);
});
await m.link(() => {});
await m.evaluate();
assert.strictEqual(m.namespace.x, 1);
CJS-модули
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<строка> Тело функции для компиляции. -
params<массив строк> Массив строк, содержащий все параметры функции. -
options<объект>-
filename<строка> Указывает имя файла, используемое в отладке стека, созданной этим скриптом. По умолчанию:''. -
lineOffset<число> Указывает смещение номера строки, отображаемое в отладке стека, созданной этим скриптом. По умолчанию:0. -
columnOffset<число> Указывает смещение номера столбца первой строки, отображаемое в отладке стека, созданной этим скриптом. По умолчанию:0. -
cachedData<Буфер> | <Тип массива> | <DataView> Предоставляет необязательныйBufferилиTypedArray, илиDataViewс данными кэша кода V8 для предоставленного исходного кода. -
produceCachedData<логическое значение> Указывает, нужно ли создавать новые данные кэша. По умолчанию:false. -
parsingContext<объект> Объект с контекстом, в котором должна быть скомпилирована функция. -
contextExtensions<массив объектов> Массив, содержащий набор расширений контекста (объекты, обертывающие текущую область видимости), которые должны быть применены при компиляции. По умолчанию:[]. -
importModuleDynamically<функция> Вызывается во время оценки этого модуля, когда вызываетсяimport(). Если этот параметр не указан, вызовы кimport()вернут ошибкуERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING. Этот параметр является частью экспериментального API модулей и не считается стабильным.-
specifier<строка> спецификатор, переданный вimport() -
function<функция> -
importAssertions<объект> Значение"assert", переданное в необязательный параметрoptionsExpression, или пустой объект, если значение не было предоставлено. - Возвращает: <Объект пространства имён модуля> | <vm.Модуль> Рекомендуется возвращать
vm.Module, чтобы воспользоваться отслеживанием ошибок и избежать проблем с пространствами имён, содержащимиthenэкспорт функций.
-
-
- Возвращает: <функция>
Компилирует заданный код в предоставленном контексте (если контекст не указан, используется текущий контекст) и возвращает его, обернутый функцией с указанным params.
vm.createContext([contextObject[, options]])
-
contextObject<объект> -
options<объект>-
name<строка> Читабельное имя вновь созданного контекста. По умолчанию:'VM Context i', гдеi— нарастающий числовой индекс созданного контекста. -
origin<строка> Происхождение, соответствующее вновь созданному контексту для отображения. Происхождение должно быть отформатировано как URL, но содержать только схему, хост и порт (при необходимости), подобно значению свойстваurl.originобъектаURL. Важно, что эта строка должна опускать конечный слэш, так как он обозначает путь. По умолчанию:''. -
codeGeneration<объект>-
strings<логическое значение> Если установлено в false, любые вызовы кevalили конструкторам функций (Function,GeneratorFunction, и т. д.) будут вызыватьEvalError. По умолчанию:true. -
wasm<логическое значение> Если установлено в false, любая попытка скомпилировать модуль WebAssembly вызоветWebAssembly.CompileError. По умолчанию:true.
-
-
microtaskMode<строка> Если установлено вafterEvaluate, микрозадачи (задачи, запланированные черезPromiseиasync function) будут выполняться немедленно после того, как скрипт пройдётscript.runInContext(). В этом случае они включены в области видимостиtimeoutиbreakOnSigint.
-
- Возвращает: <объект> объект с контекстом.
Если дан 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)
-
object<объект> - Возвращает: <логическое значение>
Возвращает true, если данный объект object был объектом с контекстом, используя vm.createContext().
vm.measureMemory([options])
Измерить память, известную V8, и используемую всеми контекстами, известными текущему изолированному V8, или основному контексту.
-
options<Object> Необязательно.-
mode<string> Либо'summary'или'detailed'. В режиме сводки будет возвращена только измеренная память для основного контекста. В подробном режиме будет возвращена измеренная память для всех контекстов, известных текущему изолированному V8. По умолчанию:'summary' -
execution<string> Либо'default'или'eager'. При выполнении по умолчанию промис не будет разрешен до запуска следующего запланированного сбора мусора, что может занять некоторое время (или никогда, если программа завершится до следующего сбора мусора). При принудительном выполнении сборка мусора будет запущена немедленно для измерения памяти. По умолчанию:'default'
-
- Возвращает: <Promise> Если память успешно измерено, промис разрешится с объектом, содержащим информацию об использовании памяти.
Формат объекта, с которым может разрешиться возвращенный промис, специфичен для движка V8 и может изменяться от одной версии V8 к другой.
Возвращаемый результат отличается от статистики, возвращаемой v8.getHeapSpaceStatistics() тем, что vm.measureMemory() измеряет память, доступную каждому контексту V8 в текущей инстанции движка V8, тогда как результат v8.getHeapSpaceStatistics() измеряет память, занимаемую каждым пространством кучи в текущей инстанции V8.
const vm = require('vm');
// Measure the memory used by the main context.
vm.measureMemory({ mode: 'summary' })
// This is the same as vm.measureMemory()
.then((result) => {
// The current format is:
// {
// total: {
// jsMemoryEstimate: 2418479, jsMemoryRange: [ 2418479, 2745799 ]
// }
// }
console.log(result);
});
const context = vm.createContext({ a: 1 });
vm.measureMemory({ mode: 'detailed', execution: 'eager' })
.then((result) => {
// Reference the context here so that it won't be GC'ed
// until the measurement is complete.
console.log(context.a);
// {
// total: {
// jsMemoryEstimate: 2574732,
// jsMemoryRange: [ 2574732, 2904372 ]
// },
// current: {
// jsMemoryEstimate: 2438996,
// jsMemoryRange: [ 2438996, 2768636 ]
// },
// other: [
// {
// jsMemoryEstimate: 135736,
// jsMemoryRange: [ 135736, 465376 ]
// }
// ]
// }
console.log(result);
});
vm.runInContext(code, contextifiedObject[, options])
-
code<строка> JavaScript-код для компиляции и выполнения. -
contextifiedObject<Объект> контекстуализированный объект, который будет использоваться в качествеglobalпри компиляции и выполненииcode. -
options<Объект> | <строка>-
filename<строка> Указывает имя файла, используемое в трассировках стека, созданных этим скриптом. По умолчанию:'evalmachine.<anonymous>'. -
lineOffset<число> Указывает смещение номера строки, отображаемое в трассировках стека, созданных этим скриптом. По умолчанию:0. -
columnOffset<число> Указывает смещение номера столбца первой строки, отображаемое в трассировках стека, созданных этим скриптом. По умолчанию:0. -
displayErrors<логическое> Когдаtrue, если при компиляцииcodeвозникаетError, строка кода, вызвавшая ошибку, добавляется в трассировку стека. По умолчанию:true. -
timeout<целое> Указывает количество миллисекунд для выполненияcodeперед завершением выполнения. Если выполнение завершается, будет брошено исключениеError. Это значение должно быть строго положительным целым числом. -
breakOnSigint<логическое> Еслиtrue, получениеSIGINT(клавиша Ctrl+C) завершит выполнение и выброситError. Существующие обработчики события, присоединённые с помощьюprocess.on('SIGINT'), будут отключены во время выполнения скрипта, но продолжат работать после него. По умолчанию: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() -
script<vm.Скрипт> -
importAssertions<Объект> Значение"assert", переданное в необязательный параметрoptionsExpression, или пустой объект, если значение не было предоставлено. - Возвращает: <Объект_пространства_имён_модуля> | <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<строка> JavaScript-код для компиляции и выполнения. -
contextObject<объект> Объект, который будет контекстуализирован. Еслиundefined, будет создан новый объект. -
options<объект> | <строка>-
filename<строка> Указывает имя файла, используемое в отслеживании стека, созданном этим скриптом. По умолчанию:'evalmachine.<anonymous>'. -
lineOffset<число> Указывает смещение номера строки, отображаемое в отслеживании стека, созданном этим скриптом. По умолчанию:0. -
columnOffset<число> Указывает смещение номера столбца первой строки, отображаемое в отслеживании стека, созданном этим скриптом. По умолчанию:0. -
displayErrors<булево> Когдаtrue, если во время компиляцииcodeвозникает ошибкаError, строка кода, вызвавшая ошибку, прикрепляется к отслеживанию стека. По умолчанию:true. -
timeout<целое> Указывает количество миллисекунд для выполненияcodeперед завершением выполнения. Если выполнение завершено, будет выброшена ошибкаError. Это значение должно быть строго положительным целым числом. -
breakOnSigint<булево> Еслиtrue, получениеSIGINT(клавиши Ctrl+C) завершит выполнение и выбросит ошибкуError. Существующие обработчики события, присоединенные с помощьюprocess.on('SIGINT'), будут отключены во время выполнения скрипта, но продолжат работать после него. По умолчанию:false. -
contextName<строка> Читабельное имя нового контекста. По умолчанию:'VM Context i', гдеi— возрастающий числовой индекс созданного контекста. -
contextOrigin<строка> Происхождение, соответствующее новому созданному контексту для отображения. Происхождение должно быть отформатировано как URL, но содержать только схему, хост и порт (при необходимости), как значение свойстваurl.originобъектаURL. Важно, что эта строка должна опускать конечный слэш, поскольку он обозначает путь. По умолчанию:''. -
contextCodeGeneration<Буфер> | <Массив_типов> | <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() -
script<vm.Скрипт> -
importAssertions<объект> Значение"assert", переданное в необязательный параметрoptionsExpression, или пустой объект, если значение не было предоставлено. - Возвращает: <Объект_пространства_имен_модуля> | <vm.Модуль> Рекомендуется возвращать
vm.Module, чтобы воспользоваться отслеживанием ошибок и избежать проблем со пространствами имен, содержащими экспорт функцийthen.
-
-
microtaskMode<строка> Если установлено значениеafterEvaluate, микрозадачи (задачи, запланированные с помощьюPromiseиasync function) будут выполняться немедленно после выполнения скрипта. В этом случае они включены в областиtimeoutиbreakOnSigint.
-
- Возвращает: <любой> результат последнего оператора, выполненного в скрипте.
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<строка> Код JavaScript для компиляции и выполнения. -
options<Объект> | <строка>-
filename<строка> Указывает имя файла, используемое в трассировках стека, создаваемых этим скриптом. По умолчанию:'evalmachine.<anonymous>'. -
lineOffset<число> Указывает смещение номера строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию:0. -
columnOffset<число> Указывает смещение номера столбца первой строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию:0. -
displayErrors<логическое значение> Когдаtrue, если при компиляцииcodeвозникает ошибкаError, строка кода, вызвавшая ошибку, добавляется к трассировке стека. По умолчанию:true. -
timeout<целое число> Указывает количество миллисекунд для выполненияcodeперед завершением выполнения. Если выполнение завершается, будет выброшена ошибкаError. Это значение должно быть строго положительным целым числом. -
breakOnSigint<логическое значение> Еслиtrue, получениеSIGINT(Ctrl+C) завершит выполнение и выбросит ошибкуError. Существующие обработчики события, подключенные черезprocess.on('SIGINT'), отключаются во время выполнения скрипта, но продолжают работать после него. По умолчанию: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() -
script<vm.Скрипт> -
importAssertions<Объект> Значение"assert", переданное в необязательный параметрoptionsExpression, или пустой объект, если значение не было предоставлено. - Возвращает: <Объект пространства имён модуля> | <vm.Модуль> Возвращение
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. Код, переданный в этот контекст VM, будет иметь своё изолированное пространство имён.
Для запуска простого веб-сервера с помощью модуля http переданный в контекст код должен либо вызвать require('http') самостоятельно, либо иметь ссылку на модуль http.
'use strict';
const vm = require('vm');
const code = `
((require) => {
const http = require('http');
http.createServer((request, response) => {
response.writeHead(200, { 'Content-Type': 'text/plain' });
response.end('Hello World\\n');
}).listen(8124);
console.log('Server running at http://127.0.0.1:8124/');
})`;
vm.runInThisContext(code)(require); require() в данном случае разделяет состояние с контекстом, из которого он получен. Это может создать риски при выполнении ненадёжного кода, например, при нежелательном изменении объектов в контексте.
Что означает "контекстуализация" объекта?
Весь JavaScript, выполняемый в Node.js, выполняется в пределах "контекста". Согласно руководству разработчика V8 Embedder:
В V8 контекст — это среда выполнения, которая позволяет отдельным, не связанным друг с другом, приложениям JavaScript работать в одном экземпляре V8. Вы должны явно указать контекст, в котором вы хотите выполнить любой JavaScript-код.
Когда вызывается метод vm.createContext() , аргумент contextObject (или новый созданный объект, если contextObject равен undefined) внутренне связывается с новым экземпляром V8-контекста. Этот V8-контекст обеспечивает code выполнение с помощью методов модуля vm изолированную глобальную среду, в которой он может работать. Процесс создания V8-контекста и его связи с contextObject именно то, что в данном документе называется "контекстуализацией" объекта.
Таймауты при взаимодействии с асинхронными задачами и промисами
Promise и async function могут планировать выполнение задач JavaScript-движком асинхронно. По умолчанию эти задачи выполняются после завершения всех JavaScript-функций в текущем стеке. Это позволяет обойти функциональность опций timeout и breakOnSigint.
Например, следующий код, выполняемый vm.runInNewContext() с таймаутом в 5 миллисекунд, планирует выполнение бесконечного цикла после разрешения промиса. Запланированный цикл никогда не прерывается таймаутом:
const vm = require('vm');
function loop() {
console.log('entering loop');
while (1) console.log(Date.now());
}
vm.runInNewContext(
'Promise.resolve().then(() => loop());',
{ loop, console },
{ timeout: 5 }
);
// This is printed *before* 'entering loop' (!)
console.log('done executing'); Это можно исправить, передав microtaskMode: 'afterEvaluate' коду, который создает Context:
const vm = require('vm');
function loop() {
while (1) console.log(Date.now());
}
vm.runInNewContext(
'Promise.resolve().then(() => loop());',
{ loop, console },
{ timeout: 5, microtaskMode: 'afterEvaluate' }
); В этом случае, микрозадача, запланированная с помощью promise.then(), будет выполнена до возвращения из vm.runInNewContext(), и будет прервана функциональностью timeout. Это относится только к коду, выполняемому в vm.Context, поэтому, например, vm.runInThisContext() не принимает эту опцию.
Обработчики промисов помещаются в очередь микрозадач контекста, в котором они были созданы. Например, если () => loop() заменить просто на loop в примере выше, то loop будет помещен в глобальную очередь микрозадач, потому что это функция из внешнего (главного) контекста, и поэтому также сможет обойти таймаут.
Если асинхронные функции планирования, такие как process.nextTick(), queueMicrotask(), setTimeout(), setImmediate(), и т. д., доступны внутри vm.Context, функции, переданные им, будут добавлены в глобальные очереди, которые совместно используются всеми контекстами. Поэтому обработчики, переданные таким функциям, также не контролируются таймаутом.
© 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-v16.x/docs/api/vm.html