Виртуальная машина (выполнение 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> - Возвращает: <Объект пространства имён модуля> | <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<Object> Объект, который будет контекстуализирован. Еслиundefined, будет создан новый объект. -
options<Object>-
displayErrors<boolean> Когдаtrue, если при компиляцииcodeвозникаетError, строка кода, вызвавшая ошибку, будет добавлена в трассировку стека. По умолчанию:true. -
timeout<integer> Указывает количество миллисекунд, в течение которых будет выполнятьсяcodeперед завершением выполнения. Если выполнение завершится, будет выброшено исключениеError. Это значение должно быть строго положительным целым числом. -
breakOnSigint<boolean> Еслиtrue, получениеSIGINT(клавиши Ctrl+C) завершит выполнение и выбросит исключениеError. Существующие обработчики события, присоединенные с помощьюprocess.on('SIGINT'), будут отключены во время выполнения скрипта, но продолжат работать после него. По умолчанию:false. -
contextName<string> Читабельное имя нового контекста. По умолчанию:'VM Context i', гдеi— восходящий числовой индекс созданного контекста. -
contextOrigin<string> Происхождение, соответствующее новому контексту для отображения. Происхождение должно быть отформатировано как URL, но содержать только схему, хост и порт (при необходимости), аналогично значению свойстваurl.originобъектаURL. Важно, что эта строка должна опускать конечный слэш, поскольку он обозначает путь. По умолчанию:''. -
contextCodeGeneration<Object>-
strings<boolean> Если установлено в false, любые вызовыevalили конструкторов функций (Function,GeneratorFunction, и т.д.) приведут к выбросу ошибкиEvalError. По умолчанию:true. -
wasm<boolean> Если установлено в false, любая попытка скомпилировать модуль WebAssembly приведёт к выбросуWebAssembly.CompileError. По умолчанию:true.
-
-
microtaskMode<string> Если установлено вafterEvaluate, микрозадачи (задачи, запланированные черезPromiseиasync function) будут выполняться немедленно после завершения выполнения скрипта. В этом случае они включаются вtimeoutиbreakOnSigintобласти.
-
- Возвращает: <any> результат последнего выполненного оператора в скрипте.
Сначала контекстуализирует заданный 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<Object>-
displayErrors<boolean> Когдаtrue, если при компиляцииcodeвозникаетError, строка кода, вызвавшая ошибку, добавляется в трассировку стека. По умолчанию:true. -
timeout<integer> Указывает количество миллисекунд, в течение которых выполняетсяcodeперед завершением выполнения. Если выполнение завершается, выбросится исключениеError. Это значение должно быть строго положительным целым числом. -
breakOnSigint<boolean> Еслиtrue, получениеSIGINT(клавиши Ctrl+C) завершит выполнение и выбросит исключениеError. Существующие обработчики события, присоединённые с помощьюprocess.on('SIGINT'), будут отключены во время выполнения скрипта, но продолжат работать после него. По умолчанию:false.
-
- Возвращает: <any> результат последнего оператора в скрипте.
Выполняет скомпилированный код, содержащийся в 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.measureMemory([options])
Измеряет память, известную V8 и используемую всеми контекстами, известными текущему изоляту V8, или основному контексту.
-
options<Object> Дополнительно.-
mode<string> Либо'summary', либо'detailed'. В режиме сводки возвращается только измеренная память для основного контекста. В подробном режиме возвращается измеренная память для всех контекстов, известных текущему изоляту V8. По умолчанию:'summary' -
execution<string> Либо'default', либо'eager'. При стандартном выполнении обещание не будет разрешено, пока не начнется следующий запланированный сбор мусора, что может занять некоторое время (или никогда, если программа завершит работу до следующего сбора мусора). При принудительном выполнении сбор мусора запускается сразу для измерения памяти. По умолчанию:'default'
-
- Возвращает: <Promise> Если память успешно измерено, обещание будет разрешено объектом, содержащим информацию об использовании памяти.
Формат объекта, с которым может быть разрешено возвращённое 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.Module
Данная функция доступна только при включенном флаге команды --experimental-vm-modules.
Класс vm.Module предоставляет низкоуровневый интерфейс для использования модулей ECMAScript в контекстах VM. Он является аналогом класса vm.Script, который тесно соответствует записям модулей Module Record, как определено в спецификации ECMAScript.
В отличие от vm.Script, каждый объект vm.Module привязан к контексту с момента его создания. Операции с объектами vm.Module по своей природе асинхронны, в отличие от синхронного характера объектов vm.Script. Использование асинхронных функций может помочь при работе с объектами vm.Module.
Использование объекта vm.Module требует трех этапов: создание/парсинг, связывание и оценка. Эти три шага проиллюстрированы в следующем примере.
Эта реализация находится на более низком уровне, чем загрузчик модулей ECMAScript. Пока нет возможности взаимодействовать с Загрузчиком, хотя поддержка планируется.
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(); 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]] записей модулей Cyclic Module Record в спецификации ECMAScript.
module.error
Если статус module.status — 'errored', это свойство содержит исключение, выброшенное модулем во время оценки. Если статус отличается, доступ к этому свойству приведет к исключению.
Значение undefined нельзя использовать в случаях, когда нет исключения, из-за возможной неоднозначности с throw undefined;.
Соответствует полю [[EvaluationError]] записей модулей Cyclic Module Record в спецификации 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 записей модулей Cyclic Module Record в спецификации ECMAScript.
module.identifier
Идентификатор текущего модуля, заданный в конструкторе.
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() concrete method записей модулей Cyclic Module Record в спецификации 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]] записи модуля Cyclic Module Record в спецификации. 'errored' соответствует 'evaluated' в спецификации, но с [[EvaluationError]] установлено значением, которое не undefined.
Класс: vm.SourceTextModule
Эта функция доступна только при включенном флаге командной строки --experimental-vm-modules.
- Расширяет: <vm.Модуль>
Класс vm.SourceTextModule предоставляет запись модуля исходного текста (Source Text Module Record) согласно спецификации 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.Модуль> - Возвращает: <Объект пространства имён модуля> | <vm.Модуль> Рекомендуется возвращать
vm.Module, чтобы воспользоваться отслеживанием ошибок и избежать проблем с пространствами имён, которые содержат экспорт функцийthen.
-
-
Создаёт новый экземпляр SourceTextModule.
Свойства объекта import.meta, являющиеся объектами, могут позволить модулю получить доступ к информации за пределами указанного context. Используйте vm.runInContext() для создания объектов в определённом контексте.
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); 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.Модуль>
Класс 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<массив строк> Массив имён, которые будут экспортированы из модуля. -
evaluateCallback<Функция> Вызывается при оценке модуля. -
options-
identifier<строка> Строка, используемая в отслеживании стека.
'vm:module(i)', гдеi— контекстно-зависимый возрастающий индекс. -
Создаёт новый экземпляр SyntheticModule.
Объекты, присвоенные экспорту этого экземпляра, могут позволить импортёрам модуля получить доступ к информации за пределами указанного context. Используйте vm.runInContext() для создания объектов в определённом контексте.
syntheticModule.setExport(name, value)
-
name<строка> Имя экспорта для установки. -
value<любой тип> Значение, на которое нужно установить экспорт.
Этот метод используется после привязки модуля для установки значений экспортов. Если он вызывается до привязки модуля, будет выброшена ошибка ERR_VM_MODULE_STATUS.
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); 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.
-
-
microtaskMode<string> Если установлено вafterEvaluate, микрозадачи (задачи, запланированные черезPromiseиasync function) будут выполняться немедленно после того, как скрипт пройдет черезscript.runInContext(). В этом случае они включены в области видимостиtimeoutиbreakOnSigint.
-
- Возвращает: <Object> объект в контексте.
Если задан 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 если предоставленный object объект был контексте с помощью vm.createContext().
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.Скрипт> - Возвращает: <Объект пространства имён модуля> | <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<объект>-
strings<логическое значение> Если установлено в false, любые вызовыevalили конструкторов функций (Function,GeneratorFunction, и т. д.) выбросятEvalError. По умолчанию:true. -
wasm<логическое значение> Если установлено в false, любая попытка скомпилировать модуль WebAssembly выброситWebAssembly.CompileError. По умолчанию:true.
-
-
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> - Возвращает: <объект пространства имён модуля> | <vm.Module> Возврат
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.Скрипт> - Возвращает: <Объект пространства имён модуля> | <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:
В 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-v14.x/docs/api/vm.html