Виртуальная машина (выполнение JavaScript)
Исходный код: lib/vm.js
Модуль node:vm позволяет компилировать и запускать код в контекстах виртуальной машины V8.
Модуль node:vm не является механизмом безопасности. Не используйте его для запуска небезопасного кода.
Код JavaScript может быть скомпилирован и запущен немедленно или скомпилирован, сохранён и запущен позже.
Часто используется для запуска кода в другом контексте V8. Это означает, что вызванный код имеет другой глобальный объект, чем вызывающий код.
Можно предоставить контекст, определяя объект. Вызванный код рассматривает любую собственность в контексте как глобальную переменную. Любые изменения глобальных переменных, вызванные вызванным кодом, отражаются в объекте контекста.
const vm = require('node: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. copyКласс: 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.Module> Рекомендуется возвращать
vm.Module, чтобы воспользоваться отслеживанием ошибок и избежать проблем с пространствами имён, содержащимиthenэкспорт функций.
-
-
Если options является строкой, то она указывает имя файла.
Создание нового объекта vm.Script компилирует code, но не выполняет его. Скомпилированный vm.Script можно выполнить позже несколько раз. code не привязан к какому-либо глобальному объекту; он привязывается перед каждым запуском, только для этого запуска.
script.cachedDataRejected
Когда cachedData предоставляется для создания vm.Script, это значение будет установлено в true или false в зависимости от принятия данных V8. В противном случае значение равно undefined.
script.createCachedData()
- Возвращает: <Буфер>
Создаёт кэш кода, который может быть использован с параметром cachedData конструктора Script. Возвращает Buffer. Этот метод можно вызывать в любое время и любое количество раз.
Кэш кода объекта Script не содержит никаких наблюдаемых JavaScript состояний. Кэш кода безопасно сохранять вместе с исходным кодом скрипта и использовать для создания новых экземпляров Script несколько раз.
Функции в исходном коде Script могут быть помечены как лениво компилируемые, и они не компилируются при создании Script. Эти функции будут скомпилированы при первом вызове. Кэш кода сериализует метаданные, которые V8 в настоящее время знает об Script, которые могут быть использованы для ускорения будущих компиляций.
const script = new vm.Script(`
function add(a, b) {
return a + b;
}
const x = add(1, 2);
`);
const cacheWithoutAdd = script.createCachedData();
// In `cacheWithoutAdd` the function `add()` is marked for full compilation
// upon invocation.
script.runInThisContext();
const cacheWithAdd = script.createCachedData();
// `cacheWithAdd` contains fully compiled function `add()`. copy
script.runInContext(contextifiedObject[, options])
-
contextifiedObject<Объект> Контекстуализированный объект, возвращаемый методомvm.createContext(). -
options<Объект>-
displayErrors<логическое значение> Еслиtrue, при возникновенииErrorво время компиляцииcode, строка кода, вызвавшая ошибку, добавляется к трассировке стека. По умолчанию:true. -
timeout<целое число> Указывает количество миллисекунд для выполненияcodeдо завершения выполнения. Если выполнение завершается, будет брошеноError. Это значение должно быть строго положительным целым числом. -
breakOnSigint<логическое значение> Еслиtrue, получениеSIGINT(Ctrl+C) завершит выполнение и выброситError. Существующие обработчики события, присоединённые черезprocess.on('SIGINT'), отключаются во время выполнения скрипта, но продолжают работать после этого. По умолчанию:false.
-
- Возвращает: <любой тип> результат последнего оператора, выполненного в скрипте.
Выполняет скомпилированный код, содержащийся в объекте vm.Script, в данном contextifiedObject и возвращает результат. Выполнение кода не имеет доступа к локальному пространству имён.
Следующий пример компилирует код, который увеличивает глобальную переменную, устанавливает значение другой глобальной переменной, а затем выполняет код несколько раз. Глобальные переменные содержатся в объекте context.
const vm = require('node: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' } copy Использование параметров timeout или breakOnSigint приведёт к запуску новых циклов событий и соответствующих потоков, что повлияет на производительность.
script.runInNewContext([contextObject[, options]])
-
contextObject<Объект> Объект, который будет контекстуализирован. Еслиundefined, будет создан новый объект. -
options<Объект>-
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.
-
-
microtaskMode<строка> Если установлено вafterEvaluate, микрозадачи (задачи, запланированные черезPromiseиasync function) будут выполнены сразу после выполнения скрипта. В этом случае они включены вtimeoutиbreakOnSigintобласти.
-
- Возвращает: <любой> результат последнего выполненного оператора в скрипте.
Сначала контекстуализирует указанный contextObject, выполняет скомпилированный код, содержащийся в объекте vm.Script в созданном контексте и возвращает результат. Выполняемый код не имеет доступа к локальной области.
В следующем примере компилируется код, устанавливающий глобальную переменную, а затем код выполняется несколько раз в разных контекстах. Глобальные переменные устанавливаются и содержатся в каждом отдельном context.
const vm = require('node: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' }] copy
script.runInThisContext([options])
-
options<Объект>-
displayErrors<логическое значение> Еслиtrue, при компиляцииcodeв случае возникновенияError, строка кода, вызвавшая ошибку, будет добавлена в трассировку стека. По умолчанию:true. -
timeout<целое число> Указывает количество миллисекунд для выполненияcodeперед завершением выполнения. Если выполнение будет прервано, будет выброшено исключениеError. Это значение должно быть положительным целым числом. -
breakOnSigint<логическое значение> Еслиtrue, получениеSIGINT(Ctrl+C) завершит выполнение и выбросит исключениеError. Существующие обработчики события, присоединённые с помощьюprocess.on('SIGINT'), будут отключены во время выполнения скрипта, но продолжат работать после этого. По умолчанию:false.
-
- Возвращает: <любой> результат последнего выполненного оператора в скрипте.
Выполняет скомпилированный код, содержащийся в vm.Script в контексте текущего объекта global. Выполняемый код не имеет доступа к локальной области, но имеет доступ к текущему объекту global.
В следующем примере компилируется код, увеличивающий переменную global, а затем этот код выполняется несколько раз:
const vm = require('node: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 copy
script.sourceMapURL
При компиляции скрипта из источника, содержащего магический комментарий карты исходного кода, это свойство будет установлено в URL карты исходного кода.
Модули MJS
import vm from 'node:vm';
const script = new vm.Script(`
function myFunc() {}
//# sourceMappingURL=sourcemap.json
`);
console.log(script.sourceMapURL);
// Prints: sourcemap.json
Модули CJS
const vm = require('node:vm');
const script = new vm.Script(`
function myFunc() {}
//# sourceMappingURL=sourcemap.json
`);
console.log(script.sourceMapURL);
// Prints: sourcemap.json Класс: vm.Module
Эта функция доступна только при включенном флаге команды --experimental-vm-modules.
Класс vm.Module предоставляет низкоуровневый интерфейс для использования ECMAScript-модулей в контекстах VM. Он является аналогом класса vm.Script, который тесно отражает Записи модулей, как определено в спецификации ECMAScript.
В отличие от vm.Script, каждый объект vm.Module привязан к контексту с момента своего создания. Операции с объектами vm.Module являются асинхронными, в отличие от синхронной природы объектов vm.Script. Использование функций 'async' может помочь в работе с объектами vm.Module.
Использование объекта vm.Module требует трех этапов: создание/парсинг, связывание и оценка. Эти три шага проиллюстрированы в следующем примере.
Эта реализация находится на более низком уровне, чем загрузчик ECMAScript-модулей. Пока нет возможности взаимодействовать с загрузчиком, хотя поддержка запланирована.
МОДУЛИ MJS
import vm from 'node: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('node: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, получение сигнала прерывания (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 copy
-
referencingModule<vm.Module> ОбъектModule, на котором вызываетсяlink(). -
extra<объект>-
assert<объект> Данные из утверждения:import foo from 'foo' assert { name: 'value' }; // ^^^^^^^^^^^^^^^^^ the assertion copyСогласно ECMA-262, хосты должны игнорировать утверждения, которые они не поддерживают, в отличие от, например, выдачи ошибки, если присутствует неподдерживаемое утверждение.
-
-
Возвращает: <vm.Module> | <Promise>
-
- Возвращает: <Promise>
Связать зависимости модуля. Этот метод должен быть вызван до оценки и может быть вызван только один раз на модуль.
Функция должна вернуть объект Module или промис, который в конечном итоге разрешится в объект Module. Возвращаемый объект Module должен удовлетворять следующим двум инвариантам:
- Он должен принадлежать тому же контексту, что и родительский
Module. - Его
statusне должен быть'errored'.
Если status возвращаемого Module имеет значение 'unlinked', этот метод будет рекурсивно вызываться на возвращаемом Module с той же функцией linker.
link() возвращает Promise который либо разрешится, когда все экземпляры связывания разрешатся на допустимый Module, или отклонится, если функция связывания бросит исключение или вернёт недопустимый Module.
Функция связывания примерно соответствует реализации HostResolveImportedModule абстрактной операции в спецификации ECMAScript с некоторыми ключевыми различиями:
- Функция связывания может быть асинхронной, тогда как 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.Модуль>
Класс 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.Модуль> -
importAssertions<Объект> значение"assert", переданное в необязательный параметрoptionsExpression, или пустой объект, если значение не было предоставлено. - Возвращает: <Объект пространства имен модуля> | <vm.Модуль> Возврат
vm.Moduleрекомендуется для отслеживания ошибок и для устранения проблем с именованными пространствами, которые содержат экспорт функцийthen.
-
-
Создает новый экземпляр SourceTextModule.
Свойства, назначенные объекту import.meta, которые являются объектами, могут позволить модулю получить доступ к информации за пределами указанного context. Используйте vm.runInContext() для создания объектов в определенном контексте.
Модули MJS
import vm from 'node: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('node: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. Этот метод можно вызывать любое количество раз до оценки модуля.
Кэш кода SourceTextModule не содержит JavaScript-наблюдаемых состояний. Кэш кода безопасно сохранять вместе с исходным кодом скрипта и использовать для создания новых экземпляров SourceTextModule несколько раз.
Функции в исходном коде SourceTextModule могут быть помечены как компилируемые по мере необходимости, и они не компилируются при создании SourceTextModule. Эти функции будут компилироваться при первом вызове. Кэш кода сериализует метаданные, о которых V8 в данный момент знает SourceTextModule, что может ускорить будущие компиляции.
// Create an initial module
const module = new vm.SourceTextModule('const a = 1;');
// Create cached data from this module
const cachedData = module.createCachedData();
// Create a new module using the cached data. The code must be the same.
const module2 = new vm.SourceTextModule('const a = 1;', { cachedData }); copy Класс: vm.SyntheticModule
Эта функция доступна только при включенном флаге команды --experimental-vm-modules.
- Расширяет: <vm.Модуль>
Класс vm.SyntheticModule предоставляет Synthetic Module Record, как определено в спецификации WebIDL. Цель синтетических модулей — обеспечить общий интерфейс для экспонирования источников, не являющихся JavaScript, в графах ECMAScript-модулей.
const vm = require('node: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... copy
new vm.SyntheticModule(exportNames, evaluateCallback[, options])
-
exportNames<массив строк> Массив имён, которые будут экспортированы из модуля. -
evaluateCallback<Функция> Вызывается при оценке модуля. -
options-
identifier<строка> Строка, используемая в отслеживании стека. По умолчанию:'vm:module(i)', гдеi— контекстно-специфический возрастающий индекс. -
context<Объект> Контекстуализированный объект, возвращаемый методомvm.createContext(), для компиляции и вычисления этогоModuleв.
-
Создаёт новый экземпляр SyntheticModule.
Объекты, назначенные экспортам этого экземпляра, могут позволить импортёрам модуля получать доступ к информации вне указанного context. Используйте vm.runInContext() для создания объектов в определённом контексте.
syntheticModule.setExport(name, value)
-
name<строка> Имя экспорта для установки. -
value<любое> Значение, на которое должен быть установлен экспорт.
Этот метод используется после связывания модуля для установки значений экспортов. Если он вызывается до связывания модуля, будет выброшена ошибка ERR_VM_MODULE_STATUS.
Модули MJS
import vm from 'node: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('node: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 для предоставленного источника. Это должно быть получено в результате предыдущего вызоваvm.compileFunction()с теми жеcodeиparams. -
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('node: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 copy Если contextObject опущено (или явно передано как undefined), будет возвращён новый пустой контекстуализированный объект.
Метод vm.createContext() в первую очередь полезен для создания единого контекста, который можно использовать для выполнения нескольких скриптов. Например, при эмуляции веб-браузера, метод можно использовать для создания единственного контекста, представляющего глобальный объект окна, а затем выполнить все теги <script> вместе в этом контексте.
Предоставленные name и origin контекста отображаются через API инспектора.
vm.isContext(object)
-
object<Объект> - Returns: <логическое значение>
Возвращает true , если данный object объект был контекстуализирован с помощью vm.createContext().
vm.measureMemory([options])
Измерьте память, известную V8 и используемую всеми контекстами, известными текущему изоляту V8, или основным контекстом.
-
options<Объект> Необязательно.-
mode<строка> Либо'summary', либо'detailed'. В режиме сводки будет возвращена только память, измеренная для основного контекста. В детальном режиме будет возвращена память, измеренная для всех контекстов, известных текущему изоляту V8. По умолчанию:'summary' -
execution<строка> Либо'default', либо'eager'. При стандартном выполнении обещание не будет разрешено до начала следующего запланированного сбора мусора, что может занять некоторое время (или никогда, если программа завершится до следующего сбора мусора). При немедленном выполнении сборка мусора будет запущена немедленно для измерения памяти. По умолчанию:'default'
-
- Возвращает: <Обещание> Если память успешно измерена, обещание будет разрешено объектом, содержащим информацию об использовании памяти. В противном случае оно будет отклонено с ошибкой
ERR_CONTEXT_NOT_INITIALIZED.
Формат объекта, с которым возвращаемое обещание может быть разрешено, специфичен для движка V8 и может изменяться от одной версии V8 к другой.
Возвращаемый результат отличается от статистики, возвращаемой v8.getHeapSpaceStatistics() , потому что vm.measureMemory() измеряет память, доступную для каждого контекста V8 в текущем экземпляре движка V8, в то время как результат v8.getHeapSpaceStatistics() измеряет память, занимаемую каждым пространством кучи в текущем экземпляре V8.
const vm = require('node: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);
}); copy
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 для указанного источника. -
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('node: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 } copy
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 для предоставленного исходного кода. -
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('node:vm');
const contextObject = {
animal: 'cat',
count: 2,
};
vm.runInNewContext('count += 1; name = "kitty"', contextObject);
console.log(contextObject);
// Prints: { animal: 'cat', count: 3, name: 'kitty' } copy
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 для предоставленного источника. -
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('node: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' copy Так как vm.runInThisContext() не имеет доступа к локальному объёму, localVar не изменяется. В отличие от eval(), имеющего доступ к локальному объёму, значение localVar изменяется. Таким образом, vm.runInThisContext() очень похож на косвенный eval() вызов, например (0,eval)('code').
Пример: Запуск HTTP-сервера внутри виртуальной машины
При использовании либо script.runInThisContext(), либо vm.runInThisContext(), код выполняется в текущем глобальном контексте V8. Код, переданный в этот контекст VM, будет иметь свой изолированный объём.
Для запуска простого веб-сервера с помощью модуля node:http код, переданный в контекст, должен либо вызвать require('node:http') самостоятельно, либо иметь ссылку на модуль node:http.
'use strict';
const vm = require('node:vm');
const code = `
((require) => {
const http = require('node: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); copy require() в данном случае разделяет состояние с контекстом, из которого он получен. Это может создать риски при выполнении недоверенного кода, например, при нежелательном изменении объектов в контексте.
Что означает "контекстуализация" объекта?
Весь JavaScript, выполняемый в Node.js, выполняется в области "контекста". Согласно Руководству по встраиванию V8:
В V8, контекст — это среда выполнения, которая позволяет отдельным, не связанным между собой, приложениям JavaScript выполняться в единственной инстанции V8. Вам необходимо явно указать контекст, в котором вы хотите выполнить любой код JavaScript.
Когда вызывается метод vm.createContext(), аргумент contextObject (или новый созданный объект, если contextObject равен undefined ) внутренне связывается с новой инстанцией контекста V8. Этот контекст V8 предоставляет code выполнить методы модуля node:vm в изолированной глобальной среде, в которой он может функционировать. Процесс создания контекста V8 и его связывания с contextObject и есть то, что в этом документе описывается как "контекстуализация" объекта.
Взаимодействие таймаутов с асинхронными задачами и обещаниями
Обещания и асинхронные задачи могут планировать задачи, выполняемые JavaScript-движком асинхронно. По умолчанию эти задачи выполняются после того, как все JavaScript-функции в текущем стеке закончат свою работу. Это позволяет обойти возможности параметров timeout и breakOnSigint.
Например, следующий код, выполненный vm.runInNewContext() с таймаутом 5 миллисекунд, планирует бесконечный цикл для выполнения после разрешения обещания. Планируемый цикл никогда не прерывается таймаутом:
const vm = require('node: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'); copy Это можно исправить, передав microtaskMode: 'afterEvaluate' в код, создающий Context:
const vm = require('node:vm');
function loop() {
while (1) console.log(Date.now());
}
vm.runInNewContext(
'Promise.resolve().then(() => loop());',
{ loop, console },
{ timeout: 5, microtaskMode: 'afterEvaluate' },
); copy В этом случае микрозадача, запланированная через promise.then(), будет выполнена до возврата из vm.runInNewContext(), и будет прервана функционалом timeout. Это относится только к коду, выполняемому в контексте, поэтому, например, 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-v18.x/docs/api/vm.html