VM (выполнение 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<string> Код JavaScript для компиляции. -
options<Object> | <string>-
filename<string> Задаёт имя файла, используемое в трассировках стека, создаваемых этим сценарием. По умолчанию:'evalmachine.<anonymous>'. -
lineOffset<number> Задаёт смещение номера строки, отображаемое в трассировках стека, создаваемых этим сценарием. По умолчанию:0. -
columnOffset<number> Задаёт смещение номера столбца первой строки, отображаемое в трассировках стека, создаваемых этим сценарием. По умолчанию:0. -
cachedData<Buffer> | <TypedArray> | <DataView> Предоставляет необязательныйBufferилиTypedArrayлибоDataViewс данными кэша кода V8 для предоставленного исходного кода. Если значение задано, значениеcachedDataRejectedбудет установлено вtrueилиfalseв зависимости от того, принимает ли V8 эти данные. -
produceCachedData<boolean> Если заданоtrueи отсутствуетcachedData, V8 попытается создать данные кэша кода дляcode. В случае успеха будет созданBufferс данными кэша кода V8 и сохранён в свойствеcachedDataвозвращённого экземпляраvm.Script. ЗначениеcachedDataProducedбудет установлено вtrueилиfalseв зависимости от того, удалось ли создать данные кэша кода. Этот параметр устарел; вместо него следует использоватьscript.createCachedData(). По умолчанию:false. -
importModuleDynamically<Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для задания способа загрузки модулей при вычислении этого сценария, когда вызываетсяimport(). Этот параметр входит в экспериментальный API модулей. Не рекомендуется использовать его в производственной среде. Подробную информацию см. в разделе Поддержка динамическогоimport()в API компиляции.
-
Если options — строка, она задаёт имя файла.
Создание нового объекта vm.Script компилирует code, но не выполняет его. Скомпилированный vm.Script впоследствии можно выполнять несколько раз. code не привязывается к глобальному объекту; вместо этого он привязывается перед каждым запуском, только на время этого запуска.
script.cachedDataRejected
- Тип: <boolean> | <undefined>
Если при создании vm.Script было передано значение cachedData, это значение будет установлено в true или false в зависимости от того, принимает ли V8 эти данные. В противном случае значение равно undefined.
script.createCachedData()
- Возвращает: <Buffer>
Создаёт кэш кода, который можно использовать с параметром 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<Object> Контекстуализированный объект, возвращённый методомvm.createContext(). -
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, в указанном 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<Object> | <vm.constants.DONT_CONTEXTIFY> | <undefined> Либоvm.constants.DONT_CONTEXTIFY, либо объект, который будет контекстуализирован. Если задано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> результат последнего выполненного в сценарии оператора.
Этот метод является сокращением для script.runInContext(vm.createContext(options), options). Он выполняет сразу несколько действий:
- Создаёт новый контекст.
- Если
contextObject— объект, он контекстуализируется в новом контексте. ЕслиcontextObjectне определён, создаётся новый объект и контекстуализируется. ЕслиcontextObject— этоvm.constants.DONT_CONTEXTIFY, ничего не контекстуализируйте. - Выполняет скомпилированный код, содержащийся в объекте
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' }]
// This would throw if the context is created from a contextified object.
// vm.constants.DONT_CONTEXTIFY allows creating contexts with ordinary
// global objects that can be frozen.
const freezeScript = new vm.Script('Object.freeze(globalThis); globalThis;');
const frozenContext = freezeScript.runInNewContext(vm.constants.DONT_CONTEXTIFY); copy
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('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
- Тип: <string> | <undefined>
Если сценарий скомпилирован из исходного кода, содержащего специальный комментарий с картой исходного кода, этому свойству будет присвоен URL карты исходного кода.
Модули JavaScript
import vm from 'node:vm';
const script = new vm.Script(`
function myFunc() {}
//# sourceMappingURL=sourcemap.json
`);
console.log(script.sourceMapURL);
// Prints: sourcemap.jsonCommonJS
const vm = require('node:vm');
const script = new vm.Script(`
function myFunc() {}
//# sourceMappingURL=sourcemap.json
`);
console.log(script.sourceMapURL);
// Prints: sourcemap.jsonClass: vm.Module
Эта функция доступна только при включенном флаге команды --experimental-vm-modules.
Класс vm.Module предоставляет низкоуровневый интерфейс для использования модулей ECMAScript в контекстах VM. Он является аналогом класса vm.Script, который точно соответствует записям модулей, определенным в спецификации ECMAScript.
Однако, в отличие от vm.Script, каждый объект vm.Module связан с контекстом с момента его создания.
Использование объекта vm.Module включает три отдельных этапа: создание/разбор, связывание и вычисление. Эти три этапа проиллюстрированы в следующем примере.
Эта реализация находится на более низком уровне, чем загрузчик модулей ECMAScript. Пока также нет способа взаимодействовать с загрузчиком, однако такая поддержка планируется.
Модули JavaScript
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 rootModule = new vm.SourceTextModule(`
import s from 'foo';
s;
print(s);
`, { context: contextifiedObject });
// Step 2
//
// "Link" the imported dependencies of this Module to it.
//
// Obtain the requested dependencies of a SourceTextModule by
// `sourceTextModule.moduleRequests` and resolve them.
//
// Even top-level Modules without dependencies must be explicitly linked. The
// array passed to `sourceTextModule.linkRequests(modules)` can be
// empty, however.
//
// Note: This is a contrived example in that the resolveAndLinkDependencies
// 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.
const moduleMap = new Map([
['root', rootModule],
]);
function resolveAndLinkDependencies(module) {
const requestedModules = module.moduleRequests.map((request) => {
// In a full-fledged module system, the resolveAndLinkDependencies would
// resolve the module with the module cache key `[specifier, attributes]`.
// In this example, we just use the specifier as the key.
const specifier = request.specifier;
let requestedModule = moduleMap.get(specifier);
if (requestedModule === undefined) {
requestedModule = new vm.SourceTextModule(`
// The "secret" variable refers to the global variable we added to
// "contextifiedObject" when creating the context.
export default secret;
`, { context: module.context });
moduleMap.set(specifier, requestedModule);
// Resolve the dependencies of the new module as well.
resolveAndLinkDependencies(requestedModule);
}
return requestedModule;
});
module.linkRequests(requestedModules);
}
resolveAndLinkDependencies(rootModule);
rootModule.instantiate();
// Step 3
//
// Evaluate the Module. The evaluate() method returns a promise which will
// resolve after the module has finished evaluating.
// Prints 42.
await rootModule.evaluate();CommonJS
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 rootModule = new vm.SourceTextModule(`
import s from 'foo';
s;
print(s);
`, { context: contextifiedObject });
// Step 2
//
// "Link" the imported dependencies of this Module to it.
//
// Obtain the requested dependencies of a SourceTextModule by
// `sourceTextModule.moduleRequests` and resolve them.
//
// Even top-level Modules without dependencies must be explicitly linked. The
// array passed to `sourceTextModule.linkRequests(modules)` can be
// empty, however.
//
// Note: This is a contrived example in that the resolveAndLinkDependencies
// 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.
const moduleMap = new Map([
['root', rootModule],
]);
function resolveAndLinkDependencies(module) {
const requestedModules = module.moduleRequests.map((request) => {
// In a full-fledged module system, the resolveAndLinkDependencies would
// resolve the module with the module cache key `[specifier, attributes]`.
// In this example, we just use the specifier as the key.
const specifier = request.specifier;
let requestedModule = moduleMap.get(specifier);
if (requestedModule === undefined) {
requestedModule = new vm.SourceTextModule(`
// The "secret" variable refers to the global variable we added to
// "contextifiedObject" when creating the context.
export default secret;
`, { context: module.context });
moduleMap.set(specifier, requestedModule);
// Resolve the dependencies of the new module as well.
resolveAndLinkDependencies(requestedModule);
}
return requestedModule;
});
module.linkRequests(requestedModules);
}
resolveAndLinkDependencies(rootModule);
rootModule.instantiate();
// Step 3
//
// Evaluate the Module. The evaluate() method returns a promise which will
// resolve after the module has finished evaluating.
// Prints 42.
await rootModule.evaluate();
})();
module.error
- Тип: <any>
Если module.status имеет значение 'errored', это свойство содержит исключение, выброшенное модулем во время вычисления. Если статус имеет любое другое значение, обращение к этому свойству приведет к выбросу исключения.
Значение undefined нельзя использовать в случаях, когда исключение не было выброшено, из-за возможной неоднозначности с throw undefined;.
Соответствует полю [[EvaluationError]] циклических записей модулей в спецификации ECMAScript.
module.evaluate([options])
-
options<Object>-
timeout<integer> Задает количество миллисекунд, в течение которых выполняется вычисление до завершения исполнения. Если выполнение прервано, будет выброшен объектError. Значение должно быть строго положительным целым числом. -
breakOnSigint<boolean> Еслиtrue, получениеSIGINT(Ctrl+C) завершит выполнение и выбросит объектError. Существующие обработчики события, добавленные с помощьюprocess.on('SIGINT'), отключаются во время выполнения скрипта, но продолжают работать после этого. По умолчанию:false.
-
- Возвращает: <Promise> При успешном выполнении разрешается значением
undefined.
Вычисляет модуль и его зависимости. Соответствует полю конкретного метода Evaluate() циклических записей модулей в спецификации ECMAScript.
Если модуль является vm.SourceTextModule, evaluate() необходимо вызвать после инстанцирования модуля; в противном случае evaluate() вернет отклоненный промис.
Для vm.SourceTextModule промис, возвращаемый evaluate(), может быть выполнен либо синхронно, либо асинхронно:
- Если в самом
vm.SourceTextModuleили любой из его зависимостей нет выражений верхнего уровняawait, промис будет выполнен синхронно после вычисления модуля и всех его зависимостей.- Если вычисление выполнится успешно, промис будет синхронно разрешен значением
undefined. - Если при вычислении возникнет исключение, промис будет синхронно отклонен с исключением, из-за которого вычисление завершилось неудачей; это то же исключение, что и
module.error.
- Если вычисление выполнится успешно, промис будет синхронно разрешен значением
- Если в самом
vm.SourceTextModuleили любой из его зависимостей есть выражения верхнего уровняawait, промис будет выполнен асинхронно после вычисления модуля и всех его зависимостей.- Если вычисление выполнится успешно, промис будет асинхронно разрешен значением
undefined. - Если при вычислении возникнет исключение, промис будет асинхронно отклонен с исключением, из-за которого вычисление завершилось неудачей.
- Если вычисление выполнится успешно, промис будет асинхронно разрешен значением
Если модуль является vm.SyntheticModule, evaluate() всегда возвращает промис, который выполняется синхронно; см. спецификацию Evaluate() для записи синтетического модуля:
- Если
evaluateCallback, переданный конструктору, синхронно выбрасывает исключение,evaluate()возвращает промис, который будет синхронно отклонен с этим исключением. - Если
evaluateCallbackне выбрасывает исключение,evaluate()возвращает промис, который будет синхронно разрешен значениемundefined.
evaluateCallback для vm.SyntheticModule выполняется синхронно в рамках вызова evaluate(), а возвращаемое значение отбрасывается. Это означает, что если evaluateCallback является асинхронной функцией, промис, возвращаемый evaluate(), не будет отражать ее асинхронное поведение, а любые отклонения, вызванные асинхронным evaluateCallback, будут потеряны.
evaluate() также можно вызвать повторно после вычисления модуля. В этом случае:
- Если первоначальное вычисление завершилось успешно (
module.statusимеет значение'evaluated'), метод ничего не сделает и вернет промис, разрешаемый значениемundefined. - Если первоначальное вычисление привело к исключению (
module.statusимеет значение'errored'), будет повторно выброшено исключение, возникшее при первоначальном вычислении.
Этот метод нельзя вызвать во время вычисления модуля (module.status имеет значение 'evaluating').
module.link(linker)
-
linker<Function>-
specifier<string> Спецификатор запрашиваемого модуля:import foo from 'foo'; // ^^^^^ the module specifier copy
-
referencingModule<vm.Module> ОбъектModule, для которого вызываетсяlink(). -
extra<Object> -
Возвращает: <vm.Module> | <Promise>
-
- Возвращает: <Promise>
Связывает зависимости модуля. Этот метод необходимо вызвать до вычисления; его можно вызвать только один раз для каждого модуля.
Используйте sourceTextModule.linkRequests(modules) и sourceTextModule.instantiate(), чтобы связывать модули синхронно или асинхронно.
Ожидается, что функция вернет объект Module или Promise, который в конечном итоге разрешится объектом Module. Возвращаемый Module должен удовлетворять следующим двум инвариантам:
- Он должен принадлежать тому же контексту, что и родительский
Module. - Его
statusне должно иметь значение'errored'.
Если status у возвращенного Module имеет значение 'unlinked', этот метод будет рекурсивно вызван для возвращенного Module с той же переданной функцией linker.
link() возвращает Promise, который будет разрешен, когда все экземпляры связывания разрешатся в допустимый Module, или отклонен, если функция связывания выбросит исключение либо вернет недопустимый Module.
Функция связывания приблизительно соответствует определенной реализацией абстрактной операции HostResolveImportedModule в спецификации ECMAScript, но имеет несколько ключевых отличий:
- Функция связывания может быть асинхронной, тогда как HostResolveImportedModule является синхронной.
Фактическая реализация HostResolveImportedModule, используемая при связывании модулей, возвращает модули, связанные в процессе связывания. Поскольку к этому моменту все модули уже будут полностью связаны, согласно спецификации реализация HostResolveImportedModule полностью синхронна.
Соответствует полю конкретного метода Link() циклических записей модулей в спецификации ECMAScript.
module.namespace
- Тип: <Object>
Объект пространства имен модуля. Доступен только после завершения связывания (module.link()).
Соответствует абстрактной операции GetModuleNamespace в спецификации ECMAScript.
module.status
- Тип: <string>
Текущий статус модуля. Возможны следующие значения:
-
'unlinked':module.link()еще не вызывался. -
'linking':module.link()был вызван, но еще не все промисы, возвращенные функцией связывания, разрешились. -
'linked': Модуль успешно связан, и все его зависимости связаны, ноmodule.evaluate()еще не вызывался. -
'evaluating': Модуль вычисляется посредствомmodule.evaluate()для него самого или родительского модуля. -
'evaluated': Модуль успешно вычислен. -
'errored': Модуль вычислен, но было выброшено исключение.
За исключением 'errored', эта строка статуса соответствует полю [[Status]] циклической записи модуля в спецификации. 'errored' соответствует 'evaluated' в спецификации, но при этом [[EvaluationError]] задано значение, отличное от undefined.
Class: vm.SourceTextModule
Эта функция доступна только при включенном флаге команды --experimental-vm-modules.
- Расширяет: <vm.Module>
Класс vm.SourceTextModule предоставляет запись модуля с исходным текстом, определенную в спецификации ECMAScript.
new vm.SourceTextModule(code[, options])
-
code<string> Код модуля JavaScript для разбора -
options-
identifier<string> Строка, используемая в трассировках стека. По умолчанию:'vm:module(i)', гдеi— возрастающий индекс, зависящий от контекста. -
cachedData<Buffer> | <TypedArray> | <DataView> Предоставляет необязательныйBufferилиTypedArrayлибоDataViewс данными кэша кода V8 для указанного исходного кода.codeдолжен совпадать с модулем, из которого был создан этотcachedData. -
context<Object> Объект, преобразованный в контекст, как возвращаемый методомvm.createContext(), в котором следует скомпилировать и вычислить этотModule. Если контекст не указан, модуль вычисляется в текущем контексте выполнения. -
lineOffset<integer> Задает смещение номера строки, отображаемое в трассировках стека, создаваемых этимModule. По умолчанию:0. -
columnOffset<integer> Задает смещение номера столбца первой строки, отображаемое в трассировках стека, создаваемых этимModule. По умолчанию:0. -
initializeImportMeta<Function> Вызывается во время вычисления этогоModuleдля инициализацииimport.meta.-
meta<import.meta> -
module<vm.SourceTextModule>
-
-
importModuleDynamically<Function> Используется для задания способа загрузки модулей во время вычисления этого модуля при вызовеimport(). Этот параметр является частью экспериментального API модулей. Не рекомендуется использовать его в рабочей среде. Подробные сведения см. в разделе Поддержка динамическогоimport()в API компиляции.
-
Создает новый экземпляр SourceTextModule.
Объекты, присвоенные свойствам объекта import.meta, могут позволить модулю получить доступ к информации за пределами указанного context. Используйте vm.runInContext(), чтобы создавать объекты в определенном контексте.
Модули JavaScript
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 = {};
},
});
// The module has an empty `moduleRequests` array.
module.linkRequests([]);
module.instantiate();
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);CommonJS
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 = {};
},
});
// The module has an empty `moduleRequests` array.
module.linkRequests([]);
module.instantiate();
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()
- Возвращает: <Buffer>
Создает кэш кода, который можно использовать с параметром 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
sourceTextModule.dependencySpecifiers
sourceTextModule.moduleRequests.- Тип: <string[]>
Спецификаторы всех зависимостей этого модуля. Возвращаемый массив заморожен, чтобы запретить его изменение.
Соответствует полю [[RequestedModules]] циклических записей модулей в спецификации ECMAScript.
sourceTextModule.hasAsyncGraph()
- Возвращает: <boolean>
Обходит граф зависимостей и возвращает true, если в зависимостях или самом модуле есть выражения верхнего уровня await; в противном случае возвращает false.
При достаточно большом графе поиск может выполняться медленно.
Для этого модуль сначала должен быть инстанцирован. Если модуль еще не инстанцирован, будет выброшена ошибка.
sourceTextModule.hasTopLevelAwait()
- Возвращает: <boolean>
Возвращает значение, указывающее, содержит ли сам модуль какие-либо выражения верхнего уровня await.
Соответствует полю [[HasTLA]] в циклической записи модуля в спецификации ECMAScript.
sourceTextModule.instantiate()
- Возвращает: <undefined>
Инстанцирует модуль со связанными запрошенными модулями.
Разрешает импортированные привязки модуля, включая имена повторно экспортируемых привязок. Если есть привязки, которые невозможно разрешить, синхронно будет выброшена ошибка.
Если среди запрошенных модулей есть циклические зависимости, метод sourceTextModule.linkRequests(modules) необходимо вызвать для всех модулей цикла до вызова этого метода.
sourceTextModule.linkRequests(modules)
-
modules<vm.Module[]> Массив объектовvm.Module, от которых зависит этот модуль. Порядок модулей в массиве соответствует порядкуsourceTextModule.moduleRequests. - Возвращает: <undefined>
Связывает зависимости модуля. Этот метод необходимо вызвать до вычисления; его можно вызвать только один раз для каждого модуля.
Порядок экземпляров модулей в массиве modules должен соответствовать порядку разрешения sourceTextModule.moduleRequests. Если два запроса модуля имеют одинаковые спецификатор и атрибуты импорта, для них должен быть разрешен один и тот же экземпляр модуля, иначе будет выброшен ERR_MODULE_LINK_MISMATCH. Например, при связывании запросов для этого модуля:
import foo from 'foo'; import source Foo from 'foo'; copy
Массив modules должен содержать две ссылки на один и тот же экземпляр, поскольку два запроса модуля идентичны, но относятся к двум фазам.
Если у модуля нет зависимостей, массив modules может быть пустым.
Пользователи могут использовать sourceTextModule.moduleRequests для реализации определенной средой выполнения абстрактной операции HostLoadImportedModule в спецификации ECMAScript и использовать sourceTextModule.linkRequests() для вызова определенной спецификацией операции FinishLoadingImportedModule для модуля со всеми зависимостями в одном пакете.
Создатель SourceTextModule сам определяет, будет ли разрешение зависимостей синхронным или асинхронным.
После связывания каждого модуля в массиве modules вызовите sourceTextModule.instantiate().
sourceTextModule.moduleRequests
- Тип: <ModuleRequest[]> Зависимости этого модуля.
Запрошенные зависимости импорта этого модуля. Возвращаемый массив заморожен, чтобы запретить его изменение.
Например, для следующего исходного текста:
import foo from 'foo';
import fooAlias from 'foo';
import bar from './bar.js';
import withAttrs from '../with-attrs.ts' with { arbitraryAttr: 'attr-val' };
import source Module from 'wasm-mod.wasm'; copy Значение sourceTextModule.moduleRequests будет следующим:
[
{
specifier: 'foo',
attributes: {},
phase: 'evaluation',
},
{
specifier: 'foo',
attributes: {},
phase: 'evaluation',
},
{
specifier: './bar.js',
attributes: {},
phase: 'evaluation',
},
{
specifier: '../with-attrs.ts',
attributes: { arbitraryAttr: 'attr-val' },
phase: 'evaluation',
},
{
specifier: 'wasm-mod.wasm',
attributes: {},
phase: 'source',
},
]; copy Class: vm.SyntheticModule
Эта функция доступна только при включенном флаге команды --experimental-vm-modules.
- Расширяет: <vm.Module>
Класс vm.SyntheticModule предоставляет запись синтетического модуля, определенную в спецификации 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<string[]> Массив имен, которые будут экспортироваться из модуля. -
evaluateCallback<Function> Вызывается при вычислении модуля. -
options-
identifier<string> Строка, используемая в трассировках стека. По умолчанию:'vm:module(i)', гдеi— возрастающий индекс, зависящий от контекста. -
context<Object> Объект, преобразованный в контекст, как возвращаемый методомvm.createContext(), в котором следует скомпилировать и вычислить этотModule.
-
Создает новый экземпляр SyntheticModule.
Объекты, присвоенные экспортам этого экземпляра, могут позволить импортирующим модулям получить доступ к информации за пределами указанного context. Используйте vm.runInContext(), чтобы создавать объекты в определенном контексте.
syntheticModule.setExport(name, value)
-
name<string> Имя экспорта, которому нужно присвоить значение. -
value<any> Значение, присваиваемое экспорту.
Этот метод задает значения слотов привязок экспорта модуля.
Модули JavaScript
import vm from 'node:vm';
const m = new vm.SyntheticModule(['x'], () => {
m.setExport('x', 1);
});
await m.evaluate();
assert.strictEqual(m.namespace.x, 1);CommonJS
const vm = require('node:vm');
(async () => {
const m = new vm.SyntheticModule(['x'], () => {
m.setExport('x', 1);
});
await m.evaluate();
assert.strictEqual(m.namespace.x, 1);
})();Type: ModuleRequest
- Тип: <Object>
-
specifier<string> Спецификатор запрашиваемого модуля. -
attributes<Object> Значение"with", переданное в WithClause в ImportDeclaration, или пустой объект, если значение не было задано. -
phase<string> Фаза запрашиваемого модуля ("source"или"evaluation").
-
ModuleRequest представляет запрос на импорт модуля с заданными атрибутами импорта и фазой.
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 для указанного исходного кода. Он должен быть создан предыдущим вызовомvm.compileFunction()с теми же параметрамиcodeиparams. -
produceCachedData<boolean> Указывает, следует ли создавать новые данные кэша. По умолчанию:false. -
parsingContext<Object> Контекстуализированный объект, в котором должна компилироваться указанная функция. -
contextExtensions<Object[]> Массив, содержащий коллекцию расширений контекста (объектов, оборачивающих текущую область видимости), которые применяются во время компиляции. По умолчанию:[]. -
importModuleDynamically<Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания способа загрузки модулей во время вычисления этой функции, когда вызываетсяimport(). Этот параметр является частью экспериментального API модулей. Мы не рекомендуем использовать его в рабочей среде. Подробные сведения см. в разделе Поддержка динамическогоimport()в API компиляции.
-
- Возвращает: <Function>
Компилирует указанный код в предоставленном контексте (если контекст не указан, используется текущий контекст) и возвращает его, обернутый в функцию с заданными параметрами params.
vm.constants
- Тип: <Object>
Возвращает объект, содержащий часто используемые константы для операций VM.
vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER
Константа, которую можно использовать в качестве параметра importModuleDynamically для vm.Script и vm.compileFunction(), чтобы Node.js использовал загрузчик ESM по умолчанию из основного контекста для загрузки запрошенного модуля.
Подробные сведения см. в разделе Поддержка динамического import() в API компиляции.
vm.createContext([contextObject[, options]])
-
contextObject<Object> | <vm.constants.DONT_CONTEXTIFY> | <undefined> Либоvm.constants.DONT_CONTEXTIFY, либо объект, который будет контекстуализирован. Если заданоundefined, для обратной совместимости будет создан пустой контекстуализированный объект. -
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. -
importModuleDynamically<Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания способа загрузки модулей, когда в этом контексте вызываетсяimport()без ссылающегося скрипта или модуля. Этот параметр является частью экспериментального API модулей. Мы не рекомендуем использовать его в рабочей среде. Подробные сведения см. в разделе Поддержка динамическогоimport()в API компиляции.
-
- Возвращает: <Object> контекстуализированный объект.
Если заданный 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.constants.DONT_CONTEXTIFY в качестве аргумента contextObject. Подробности см. в документации к vm.constants.DONT_CONTEXTIFY.
Метод vm.createContext() в первую очередь предназначен для создания единственного контекста, который можно использовать для запуска нескольких скриптов. Например, при эмуляции веб-браузера этот метод можно использовать для создания единственного контекста, представляющего глобальный объект окна, а затем запускать в этом контексте все теги <script>.
Указанные name и origin контекста доступны через API Inspector.
vm.isContext(object)
Возвращает true, если заданный объект object был контекстуализирован с помощью vm.createContext() или является глобальным объектом контекста, созданного с помощью vm.constants.DONT_CONTEXTIFY.
vm.measureMemory([options])
Измеряет память, известную V8 и используемую всеми контекстами, известными текущему изоляту V8, либо основным контекстом.
-
options<Object> Необязательный.-
mode<string> Либо'summary', либо'detailed'. В режиме сводки возвращаются только данные об измеренной памяти основного контекста. В подробном режиме возвращаются данные об измеренной памяти всех контекстов, известных текущему изоляту V8. По умолчанию:'summary' -
execution<string> Либо'default', либо'eager'. При выполнении по умолчанию промис не будет разрешен, пока не начнется следующая запланированная сборка мусора; это может занять некоторое время (или не произойти вовсе, если программа завершится до следующей сборки мусора). При нетерпеливом выполнении сборка мусора начнется сразу, чтобы измерить память. По умолчанию:'default'
-
- Возвращает: <Promise> Если измерение памяти выполнено успешно, промис будет разрешен объектом, содержащим сведения об использовании памяти. В противном случае он будет отклонен с ошибкой
ERR_CONTEXT_NOT_INITIALIZED.
Формат объекта, которым может быть разрешен возвращаемый Promise, зависит от движка 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<string> Код JavaScript для компиляции и запуска. -
contextifiedObject<Object> Контекстуализированный объект, который будет использоваться в качествеglobalпри компиляции и запускеcode. -
options<Object> | <string>-
filename<string> Задает имя файла, используемое в трассировках стека, создаваемых этим скриптом. По умолчанию:'evalmachine.<anonymous>'. -
lineOffset<number> Задает смещение номера строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию:0. -
columnOffset<number> Задает смещение номера столбца первой строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию:0. -
displayErrors<boolean> Если значениеtrueи при компиляцииcodeвозникает ошибкаError, строка кода, вызвавшая ошибку, добавляется в трассировку стека. По умолчанию:true. -
timeout<integer> Задает количество миллисекунд, в течение которых выполняетсяcodeдо принудительного завершения. При принудительном завершении выполнения будет выброшена ошибкаError. Значение должно быть строго положительным целым числом. -
breakOnSigint<boolean> Если значениеtrue, получениеSIGINT(Ctrl+C) завершит выполнение и выбросит ошибкуError. Существующие обработчики события, добавленные черезprocess.on('SIGINT'), отключаются на время выполнения скрипта, но продолжают работать после его завершения. По умолчанию:false. -
cachedData<Buffer> | <TypedArray> | <DataView> Предоставляет необязательныйBufferилиTypedArray, либоDataViewс данными кэша кода V8 для указанного исходного кода. -
importModuleDynamically<Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания способа загрузки модулей во время вычисления этого скрипта, когда вызываетсяimport(). Этот параметр является частью экспериментального API модулей. Мы не рекомендуем использовать его в рабочей среде. Подробные сведения см. в разделе Поддержка динамическогоimport()в API компиляции.
-
Метод 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<string> Код JavaScript для компиляции и выполнения. -
contextObject<Object> | <vm.constants.DONT_CONTEXTIFY> | <undefined> Либоvm.constants.DONT_CONTEXTIFY, либо объект, который будет контекстирован. Еслиundefined, для обратной совместимости будет создан пустой контекстированный объект. -
options<Object> | <string>-
filename<string> Задает имя файла, используемое в трассировках стека, создаваемых этим скриптом. По умолчанию:'evalmachine.<anonymous>'. -
lineOffset<number> Задает смещение номера строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию:0. -
columnOffset<number> Задает смещение номера столбца первой строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию:0. -
displayErrors<boolean> Если значение равноtrueи при компиляцииcodeвозникаетError, строка кода, вызвавшая ошибку, добавляется в трассировку стека. По умолчанию:true. -
timeout<integer> Задает время в миллисекундах, в течение которого будет выполнятьсяcodeдо прекращения выполнения. Если выполнение будет прекращено, будет выброшено исключениеError. Значение должно быть строго положительным целым числом. -
breakOnSigint<boolean> Еслиtrue, получениеSIGINT(Ctrl+C) прекратит выполнение и выбросит исключение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.
-
-
cachedData<Buffer> | <TypedArray> | <DataView> Предоставляет необязательныеBufferилиTypedArrayлибоDataViewс данными кэша кода V8 для переданного исходного кода. -
importModuleDynamically<Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания способа загрузки модулей при вычислении этого скрипта, если вызываетсяimport(). Этот параметр является частью экспериментального API модулей. Не рекомендуется использовать его в рабочей среде. Подробную информацию см. в разделе Поддержка динамическогоimport()в API компиляции. -
microtaskMode<string> Если задано значениеafterEvaluate, микрозадачи (задачи, запланированные черезPromiseиasync function) будут выполнены сразу после выполнения скрипта. В этом случае они входят в областиtimeoutиbreakOnSigint.
-
- Возвращает: <any> результат выполнения последнего оператора скрипта.
Этот метод является сокращением для (new vm.Script(code, options)).runInContext(vm.createContext(options), options). Если options — строка, она задает имя файла.
Он выполняет сразу несколько действий:
- Создает новый контекст.
- Если
contextObjectявляется объектом, контекстирует его в новом контексте. ЕслиcontextObjectне определен, создает новый объект и контекстирует его. ЕслиcontextObject— этоvm.constants.DONT_CONTEXTIFY, ничего не контекстирует. - Компилирует код как
vm.Script - Выполняет скомпилированный код в созданном контексте. Код не имеет доступа к области видимости, в которой вызывается этот метод.
- Возвращает результат.
В следующем примере компилируется и выполняется код, который увеличивает глобальную переменную и создает новую. Эти глобальные переменные содержатся в 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' }
// This would throw if the context is created from a contextified object.
// vm.constants.DONT_CONTEXTIFY allows creating contexts with ordinary global objects that
// can be frozen.
const frozenContext = vm.runInNewContext('Object.freeze(globalThis); globalThis;', vm.constants.DONT_CONTEXTIFY); copy
vm.runInThisContext(code[, options])
-
code<string> Код JavaScript для компиляции и выполнения. -
options<Object> | <string>-
filename<string> Задает имя файла, используемое в трассировках стека, создаваемых этим скриптом. По умолчанию:'evalmachine.<anonymous>'. -
lineOffset<number> Задает смещение номера строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию:0. -
columnOffset<number> Задает смещение номера столбца первой строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию:0. -
displayErrors<boolean> Если значение равноtrueи при компиляцииcodeвозникаетError, строка кода, вызвавшая ошибку, добавляется в трассировку стека. По умолчанию:true. -
timeout<integer> Задает время в миллисекундах, в течение которого будет выполнятьсяcodeдо прекращения выполнения. Если выполнение будет прекращено, будет выброшено исключениеError. Значение должно быть строго положительным целым числом. -
breakOnSigint<boolean> Еслиtrue, получениеSIGINT(Ctrl+C) прекратит выполнение и выбросит исключениеError. Обработчики события, ранее подключенные черезprocess.on('SIGINT'), отключаются во время выполнения скрипта, но продолжат работать после него. По умолчанию:false. -
cachedData<Buffer> | <TypedArray> | <DataView> Предоставляет необязательныеBufferилиTypedArrayлибоDataViewс данными кэша кода V8 для переданного исходного кода. -
importModuleDynamically<Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания способа загрузки модулей при вычислении этого скрипта, если вызываетсяimport(). Этот параметр является частью экспериментального API модулей. Не рекомендуется использовать его в рабочей среде. Подробную информацию см. в разделе Поддержка динамическогоimport()в API компиляции.
-
- Возвращает: <any> результат выполнения последнего оператора скрипта.
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 используется для обертки глобального объекта нового экземпляра контекста V8 (если contextObject — это undefined, перед контекстированием из текущего контекста будет создан новый объект). Этот контекст V8 предоставляет code, выполняемый с помощью методов модуля node:vm, изолированную глобальную среду, в которой он может работать. Создание контекста V8 и связывание его с contextObject во внешнем контексте — это то, что в данном документе называется «контекстированием» объекта.
Контекстирование приводит к некоторым особенностям значения globalThis в контексте. Например, его нельзя заморозить, и оно не является ссылочно равным значению contextObject во внешнем контексте.
const vm = require('node:vm');
// An undefined `contextObject` option makes the global object contextified.
const context = vm.createContext();
console.log(vm.runInContext('globalThis', context) === context); // false
// A contextified global object cannot be frozen.
try {
vm.runInContext('Object.freeze(globalThis);', context);
} catch (e) {
console.log(e); // TypeError: Cannot freeze
}
console.log(vm.runInContext('globalThis.foo = 1; foo;', context)); // 1 copy Чтобы создать контекст с обычным глобальным объектом и получить доступ к глобальному прокси во внешнем контексте с меньшим количеством особенностей, укажите vm.constants.DONT_CONTEXTIFY в качестве аргумента contextObject.
vm.constants.DONT_CONTEXTIFY
Эта константа, используемая в качестве аргумента contextObject в API vm, указывает Node.js создать контекст, не оборачивая его глобальный объект другим объектом способом, специфичным для Node.js. В результате значение globalThis в новом контексте будет вести себя ближе к обычному.
const vm = require('node:vm');
// Use vm.constants.DONT_CONTEXTIFY to freeze the global object.
const context = vm.createContext(vm.constants.DONT_CONTEXTIFY);
vm.runInContext('Object.freeze(globalThis);', context);
try {
vm.runInContext('bar = 1; bar;', context);
} catch (e) {
console.log(e); // Uncaught ReferenceError: bar is not defined
} copy Если vm.constants.DONT_CONTEXTIFY используется в качестве аргумента contextObject для vm.createContext(), возвращаемый объект будет подобным прокси объектом глобального объекта в новом контексте с меньшим количеством особенностей, специфичных для Node.js. Он будет ссылочно равен значению globalThis в новом контексте, его можно будет изменять извне контекста и использовать для прямого доступа к встроенным объектам нового контекста.
const vm = require('node:vm');
const context = vm.createContext(vm.constants.DONT_CONTEXTIFY);
// Returned object is reference equal to globalThis in the new context.
console.log(vm.runInContext('globalThis', context) === context); // true
// Can be used to access globals in the new context directly.
console.log(context.Array); // [Function: Array]
vm.runInContext('foo = 1;', context);
console.log(context.foo); // 1
context.bar = 1;
console.log(vm.runInContext('bar;', context)); // 1
// Can be frozen and it affects the inner context.
Object.freeze(context);
try {
vm.runInContext('baz = 1; baz;', context);
} catch (e) {
console.log(e); // Uncaught ReferenceError: baz is not defined
} copy Взаимодействие тайм-аута с асинхронными задачами и промисами
Promise и async function могут планировать задачи, выполняемые движком 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.Context, поэтому, например, vm.runInThisContext() не принимает этот параметр.
Обратные вызовы промисов помещаются в очередь микрозадач контекста, в котором они были созданы. Например, если в приведенном выше примере заменить () => loop() просто на loop, то loop попадет в глобальную очередь микрозадач, поскольку это функция из внешнего (основного) контекста, а значит, она также сможет обойти тайм-аут.
Если внутри vm.Context доступны функции асинхронного планирования, такие как process.nextTick(), queueMicrotask(), setTimeout(), setImmediate() и т. д., переданные им функции будут добавляться в глобальные очереди, общие для всех контекстов. Поэтому выполнение обратных вызовов, переданных этим функциям, также нельзя контролировать с помощью тайм-аута.
Если microtaskMode имеет значение 'afterEvaluate', будьте осторожны при совместном использовании промисов между контекстами
В режиме 'afterEvaluate' у Context есть собственная очередь микрозадач, отдельная от глобальной очереди микрозадач внешнего (основного) контекста. Хотя этот режим необходим для обеспечения timeout и поддержки breakOnSigint с асинхронными задачами, он также усложняет совместное использование промисов между контекстами.
В приведенном ниже примере промис создается во внутреннем контексте и передается во внешний. Когда внешний контекст вызывает await для этого промиса, поток выполнения внешнего контекста неожиданным образом нарушается: оператор log никогда не выполняется.
import * as vm from 'node:vm';
const inner_context = vm.createContext({}, { microtaskMode: 'afterEvaluate' });
// runInContext() returns a Promise created in the inner context.
const inner_promise = vm.runInContext(
'Promise.resolve()',
context,
);
// As part of performing `await`, the JavaScript runtime must enqueue a task
// on the microtask queue of the context where `inner_promise` was created.
// A task is added on the inner microtask queue, but **it will not be run
// automatically**: this task will remain pending indefinitely.
//
// Since the outer microtask queue is empty, execution in the outer module
// falls through, and the log statement below is never executed.
await inner_promise;
console.log('this will NOT be printed'); copy Чтобы успешно использовать промисы совместно между контекстами с разными очередями микрозадач, необходимо обеспечить выполнение задач во внутренней очереди микрозадач каждый раз, когда внешний контекст помещает задачу во внутреннюю очередь микрозадач.
Задачи в очереди микрозадач заданного контекста выполняются при каждом вызове runInContext() или SourceTextModule.evaluate() для скрипта или модуля, использующего этот контекст. В нашем примере обычный поток выполнения можно восстановить, запланировав второй вызов runInContext() до await inner_promise.
// Schedule `runInContext()` to manually drain the inner context microtask
// queue; it will run after the `await` statement below.
setImmediate(() => {
vm.runInContext('', context);
});
await inner_promise;
console.log('OK'); copy Примечание: Строго говоря, в этом режиме node:vm отступает от буквального текста спецификации ECMAScript для постановки заданий в очередь, позволяя асинхронным задачам из разных контекстов выполняться в порядке, отличном от порядка их постановки в очередь.
Поддержка динамического import() в API компиляции
Следующие API поддерживают параметр importModuleDynamically для включения динамического import() в коде, скомпилированном модулем vm.
new vm.Scriptvm.compileFunction()new vm.SourceTextModulevm.runInThisContext()vm.runInContext()vm.runInNewContext()vm.createContext()
Этот параметр по-прежнему является частью экспериментального API модулей. Не рекомендуется использовать его в рабочей среде.
Если параметр importModuleDynamically не указан или имеет значение undefined
Если этот параметр не указан или имеет значение undefined, код, содержащий import(), все равно можно скомпилировать с помощью API vm, однако при выполнении скомпилированного кода и фактическом вызове import() результат будет отклонен с ошибкой ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING.
Если importModuleDynamically имеет значение vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER
В настоящее время этот параметр не поддерживается для vm.SourceTextModule.
При использовании этого параметра, если в скомпилированном коде запускается import(), Node.js использует загрузчик ESM по умолчанию из основного контекста для загрузки запрошенного модуля и передает его выполняемому коду.
Это предоставляет компилируемому коду доступ к встроенным модулям Node.js, таким как fs или http. Если код выполняется в другом контексте, обратите внимание, что объекты, созданные модулями, загруженными из основного контекста, по-прежнему относятся к основному контексту, а не к встроенным классам instanceof в новом контексте.
CommonJS
const { Script, constants } = require('node:vm');
const script = new Script(
'import("node:fs").then(({readFile}) => readFile instanceof Function)',
{ importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER });
// false: URL loaded from the main context is not an instance of the Function
// class in the new context.
script.runInNewContext().then(console.log);Модули JavaScript
import { Script, constants } from 'node:vm';
const script = new Script(
'import("node:fs").then(({readFile}) => readFile instanceof Function)',
{ importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER });
// false: URL loaded from the main context is not an instance of the Function
// class in the new context.
script.runInNewContext().then(console.log);Этот параметр также позволяет скрипту или функции загружать пользовательские модули:
Модули JavaScript
import { Script, constants } from 'node:vm';
import { resolve } from 'node:path';
import { writeFileSync } from 'node:fs';
// Write test.js and test.txt to the directory where the current script
// being run is located.
writeFileSync(resolve(import.meta.dirname, 'test.mjs'),
'export const filename = "./test.json";');
writeFileSync(resolve(import.meta.dirname, 'test.json'),
'{"hello": "world"}');
// Compile a script that loads test.mjs and then test.json
// as if the script is placed in the same directory.
const script = new Script(
`(async function() {
const { filename } = await import('./test.mjs');
return import(filename, { with: { type: 'json' } })
})();`,
{
filename: resolve(import.meta.dirname, 'test-with-default.js'),
importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER,
});
// { default: { hello: 'world' } }
script.runInThisContext().then(console.log);CommonJS
const { Script, constants } = require('node:vm');
const { resolve } = require('node:path');
const { writeFileSync } = require('node:fs');
// Write test.js and test.txt to the directory where the current script
// being run is located.
writeFileSync(resolve(__dirname, 'test.mjs'),
'export const filename = "./test.json";');
writeFileSync(resolve(__dirname, 'test.json'),
'{"hello": "world"}');
// Compile a script that loads test.mjs and then test.json
// as if the script is placed in the same directory.
const script = new Script(
`(async function() {
const { filename } = await import('./test.mjs');
return import(filename, { with: { type: 'json' } })
})();`,
{
filename: resolve(__dirname, 'test-with-default.js'),
importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER,
});
// { default: { hello: 'world' } }
script.runInThisContext().then(console.log);При загрузке пользовательских модулей с помощью загрузчика по умолчанию из основного контекста есть несколько особенностей:
- Разрешаемый модуль будет искать путь относительно параметра
filename, переданного вvm.Scriptилиvm.compileFunction(). Разрешение пути может работать сfilename, заданным в виде абсолютного пути или строки URL. Еслиfilename— строка, которая не является ни абсолютным путем, ни URL, либо если он не определен, поиск будет выполняться относительно текущего рабочего каталога процесса. В случаеvm.createContext()поиск всегда выполняется относительно текущего рабочего каталога, поскольку этот параметр используется только при отсутствии скрипта или модуля-источника. - Для любого
filename, разрешающегося в определенный путь, после загрузки процессом конкретного модуля из этого пути результат может быть кэширован, и последующие загрузки того же модуля из того же пути будут возвращать тот же объект. Еслиfilename— строка URL, запись кэша не будет найдена, если параметры запроса отличаются. Дляfilename, не являющихся строками URL, в настоящее время нет способа обойти кэширование.
Если importModuleDynamically является функцией
Если importModuleDynamically является функцией, она будет вызвана при вызове import() в скомпилированном коде, чтобы пользователи могли настроить способ компиляции и вычисления запрошенного модуля. В настоящее время для работы этого параметра экземпляр Node.js необходимо запускать с флагом --experimental-vm-modules. Если флаг не задан, этот обратный вызов будет проигнорирован. Если вычисляемый код действительно вызывает import(), результат будет отклонен с ошибкой ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG.
Обратный вызов importModuleDynamically(specifier, referrer, importAttributes) имеет следующую сигнатуру:
-
specifier<string> спецификатор, переданный вimport() -
referrer<vm.Script> | <Function> | <vm.SourceTextModule> | <Object> Источник — это скомпилированныйvm.Scriptдляnew vm.Script,vm.runInThisContext,vm.runInContextиvm.runInNewContext. Дляvm.compileFunctionэто скомпилированныйFunction, дляnew vm.SourceTextModule— скомпилированныйvm.SourceTextModule, а дляvm.createContext()— контекстObject. -
importAttributes<Object> Значение"with", переданное в необязательный параметрoptionsExpression, или пустой объект, если значение не было предоставлено. -
phase<string> Фаза динамического импорта ("source"или"evaluation"). - Возвращает: <Module Namespace Object> | <vm.Module> Рекомендуется возвращать
vm.Module, чтобы воспользоваться отслеживанием ошибок и избежать проблем с пространствами имен, содержащими экспорты функцийthen.
Модули JavaScript
// This script must be run with --experimental-vm-modules.
import { Script, SyntheticModule } from 'node:vm';
const script = new Script('import("foo.json", { with: { type: "json" } })', {
async importModuleDynamically(specifier, referrer, importAttributes) {
console.log(specifier); // 'foo.json'
console.log(referrer); // The compiled script
console.log(importAttributes); // { type: 'json' }
const m = new SyntheticModule(['bar'], () => { });
await m.link(() => { });
m.setExport('bar', { hello: 'world' });
return m;
},
});
const result = await script.runInThisContext();
console.log(result); // { bar: { hello: 'world' } }CommonJS
// This script must be run with --experimental-vm-modules.
const { Script, SyntheticModule } = require('node:vm');
(async function main() {
const script = new Script('import("foo.json", { with: { type: "json" } })', {
async importModuleDynamically(specifier, referrer, importAttributes) {
console.log(specifier); // 'foo.json'
console.log(referrer); // The compiled script
console.log(importAttributes); // { type: 'json' }
const m = new SyntheticModule(['bar'], () => { });
await m.link(() => { });
m.setExport('bar', { hello: 'world' });
return m;
},
});
const result = await script.runInThisContext();
console.log(result); // { bar: { hello: 'world' } }
})();
© 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-v24.x/docs/api/vm.html