Виртуальная машина (выполнение 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<Буфер> | <TypedArray> | <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<Функция> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания способа загрузки модулей при оценке этого скрипта, когда вызываетсяimport(). Эта опция является частью экспериментального API модулей. Мы не рекомендуем использовать её в рабочей среде. Для получения подробной информации см. Поддержка динамическихimport()в API компиляции.
-
Если 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, если при компиляцииcodeвозникаетError, строка кода, вызвавшая ошибку, добавляется в трассировку стека. По умолчанию: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<Object> Объект, который будет контекстуализирован. Еслиundefined, будет создан новый объект. -
options<Object>-
displayErrors<boolean> Когдаtrue, если во время компиляцииcodeвозникает ошибкаError, строка кода, вызвавшая ошибку, будет добавлена в трассировку стека. По умолчанию:true. -
timeout<integer> Указывает количество миллисекунд для выполненияcodeперед завершением выполнения. Если выполнение завершится, будет выброшено исключениеError. Это значение должно быть строго положительным целым числом. -
breakOnSigint<boolean> Еслиtrue, получениеSIGINT(Ctrl+C) завершит выполнение и выбросит исключениеError. Существующие обработчики события, прикреплённые с помощьюprocess.on('SIGINT'), будут отключены во время выполнения скрипта, но продолжат работать после него. По умолчанию:false. -
contextName<string> Читаемое человеком имя нового контекста. По умолчанию:'VM Context i', гдеi— возрастающий числовой индекс созданного контекста. -
contextOrigin<Object>-
strings<boolean> Если установлено в false, любые вызовыevalили конструкторов функций (Function,GeneratorFunction, и т.д.) выбросят исключениеEvalError. По умолчанию:true. -
wasm<boolean> Если установлено в false, любая попытка скомпилировать модуль WebAssembly выбросит исключениеWebAssembly.CompileError. По умолчанию:true.
-
-
microtaskMode<string> Если установлено вafterEvaluate, микрозадачи (задачи, запланированные с помощьюPromiseиasync function) будут выполняться немедленно после выполнения скрипта. В этом случае они включаются вtimeoutиbreakOnSigintобласти.
-
- Возвращает: <any> результат последнего выполненного оператора в скрипте.
Сначала контекстуализирует переданный contextObject, выполняет скомпилированный код, содержащийся в объекте vm.Script в созданном контексте и возвращает результат. Выполнение кода не имеет доступа к локальной области видимости.
В следующем примере компилируется код, который устанавливает глобальную переменную, а затем выполняется несколько раз в разных контекстах. Глобальные переменные устанавливаются в каждом отдельном context.
const vm = require('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<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
Если скрипт скомпилирован из источника, содержащего магический комментарий карты исходного кода, это свойство будет установлено в 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. Использование асинхронных функций может помочь в манипулировании объектами 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, получениеSIGINT(Ctrl+C) завершит выполнение и выбросит исключениеError. Существующие обработчики события, присоединенные черезprocess.on('SIGINT'), будут отключены во время выполнения скрипта, но продолжат работу после этого. По умолчанию:false.
-
- Возвращает: <Обещание> Успешное выполнение возвращает
undefined.
Оценить модуль.
Это должно быть вызвано после связывания модуля; в противном случае произойдет отклонение. Это также может быть вызвано, когда модуль уже был оценен, в этом случае он либо ничего не сделает, если начальная оценка завершилась успешно (module.status равно 'evaluated'), либо повторно выбросит исключение, которое привело к начальной оценке (module.status равно 'errored').
Этот метод не может быть вызван, пока модуль оценивается (module.status равно 'evaluating').
Соответствует полю Evaluate() конкретного метода записей циклических модулей в спецификации ECMAScript.
module.identifier
Идентификатор текущего модуля, заданный в конструкторе.
module.link(linker)
-
linker<Функция>-
specifier<строка> Указатель запрашиваемого модуля:import foo from 'foo'; // ^^^^^ the module specifier copy
-
referencingModule<vm.Модуль> ОбъектModule, на котором вызываетсяlink(). -
extra<Объект> -
Возвращает: <vm.Модуль> | <Обещание>
-
- Возвращает: <Обещание>
Связать зависимости модуля. Этот метод должен быть вызван перед оценкой и может быть вызван только один раз на модуль.
Функция должна вернуть объект Module или Promise, который в конечном итоге разрешится в объект Module. Возвращаемый Module должен удовлетворять следующим двум инвариантам:
- Он должен принадлежать к тому же контексту, что и родительский
Module. - Его
statusне должен быть'errored'.
Если Module возвращенного status равно 'unlinked', этот метод будет рекурсивно вызван на возвращаемом Module с той же предоставленной функцией linker.
link() возвращает Promise, который будет разрешен, когда все экземпляры связывания разрешатся на допустимый Module, или отклонен, если функция связывания выбросит исключение или вернет недопустимый Module.
Функция связывания примерно соответствует определенной реализацией абстрактной операции HostResolveImportedModule в спецификации ECMAScript с некоторыми ключевыми отличиями:
- Функция связывания может быть асинхронной, в то время как HostResolveImportedModule является синхронной.
Фактическая реализация HostResolveImportedModule, используемая при связывании модулей, возвращает модули, связанные во время связывания. Поскольку на этом этапе все модули уже будут полностью связаны, реализация HostResolveImportedModule полностью синхронна в соответствии со спецификацией.
Соответствует полю Link() конкретного метода записей циклических модулей в спецификации ECMAScript.
module.namespace
Объект пространства имен модуля. Он доступен только после завершения связывания (module.link()).
Соответствует абстрактной операции GetModuleNamespace в спецификации ECMAScript.
module.status
Текущий статус модуля. Может быть одним из следующих:
-
'unlinked':module.link()еще не был вызван. -
'linking':module.link()был вызван, но еще не были разрешены все Обещания, возвращенные функцией связывания. -
'linked': Модуль был связан успешно, и все его зависимости связаны, ноmodule.evaluate()еще не был вызван. -
'evaluating': Модуль оценивается с помощьюmodule.evaluate()на самом себе или родительском модуле. -
'evaluated': Модуль был успешно оценен. -
'errored': Модуль был оценен, но было выброшено исключение.
За исключением 'errored', эта строка состояния соответствует полю [[Status]] записи циклического модуля спецификации. 'errored' соответствует 'evaluated' в спецификации, но с [[EvaluationError]] установлено значение, отличное от undefined.
Класс: vm.SourceTextModule
Эта функция доступна только при включенном флаге команды --experimental-vm-modules.
- Расширяет: <vm.Модуль>
Класс vm.SourceTextModule предоставляет запись Записи модуля исходного текста, как определено в спецификации ECMAScript.
new vm.SourceTextModule(code[, options])
-
code<строка> JavaScript-код модуля для парсинга -
options-
identifier<строка> Строка, используемая в трассировках стека. По умолчанию:'vm:module(i)', гдеi— контекстно-зависимый восходящий индекс. -
cachedData<Буфер> | <Массив типов> | <DataView> Предоставляет необязательныйBufferилиTypedArray, илиDataView, содержащий данные кэша кода V8 для предоставленного источника.codeдолжен совпадать с модулем, из которого был создан этотcachedData. -
context<Объект> Контекстуализированный объект, возвращаемый методомvm.createContext(), для компиляции и оценки этогоModuleв. Если контекст не указан, модуль оценивается в текущем контексте выполнения. -
lineOffset<целое число> Указывает смещение номера строки, отображаемое в трассировках стека, созданных этимModule. По умолчанию:0. -
columnOffset<целое число> Указывает смещение номера столбца первой строки, отображаемое в трассировках стека, созданных этимModule. По умолчанию:0. -
initializeImportMeta<Функция> Вызывается во время оценки этогоModuleдля инициализацииimport.meta.-
meta<import.meta> -
module<vm.SourceTextModule>
-
-
importModuleDynamically<Функция> Используется для указания того, как модули должны загружаться во время оценки этого модуля при вызовеimport(). Этот параметр является частью экспериментального API модулей. Не рекомендуется использовать его в рабочей среде. Подробную информацию см. в разделе Поддержка динамическихimport()в API компиляции.
-
Создает новый экземпляр 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 предоставляет Запись синтетического модуля, как определено в спецификации 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)
Этот метод используется после связывания модуля для установки значений экспортов. Если он вызывается до связывания модуля, будет выброшено исключение 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<Функция> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания, как модули должны загружаться во время оценки этой функции, когда вызываетсяimport(). Этот параметр является частью экспериментального API модулей. Мы не рекомендуем использовать его в производственной среде. Более подробная информация представлена в разделе Поддержка динамическогоimport()в API компиляции. - Возвращает: <Функция>
Компилирует заданный код в указанный контекст (если контекст не указан, используется текущий контекст) и возвращает его, обернутый в функцию с указанными params.
vm.constants
Возвращает объект, содержащий часто используемые константы для операций с виртуальной машиной.
vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER
Константа, которая может быть использована в качестве параметра importModuleDynamically для vm.Script и vm.compileFunction(), чтобы Node.js использовал стандартный загрузчик ESM из основного контекста для загрузки запрошенного модуля.
Для получения подробной информации, см. Поддержку динамического import() в API компиляции.
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. -
importModuleDynamically<Функция> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания способа загрузки модулей, когдаimport()вызывается в этом контексте без ссылки на скрипт или модуль. Эта опция является частью экспериментального API модулей. Мы не рекомендуем использовать её в производственной среде. Для получения подробной информации см. Поддержку динамическогоimport()в API компиляции.
-
- Возвращает: <Объект> обёртку объекта.
Если задан 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<Объект> - Возвращает: <логическое_значение>
Возвращает true , если данный object объект был обёрнут с помощью vm.createContext().
vm.measureMemory([options])
Измерьте память, известную V8 и используемую всеми контекстами, известными текущему изоляту V8, или основным контекстом.
-
options<Объект> Необязательно.-
mode<строка> Либо'summary', либо'detailed'. В режиме сводки будет возвращена только измеренная память основного контекста. В подробном режиме — память всех контекстов, известных текущему изоляту V8. По умолчанию:'summary' -
execution<строка> Либо'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<строка> 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<Функция> | <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<строка> JavaScript-код для компиляции и выполнения. -
contextObject<объект> Объект, который будет контекстуализирован. Еслиundefined, будет создан новый объект. -
options<объект> | <строка>-
filename<строка> Указывает имя файла, используемое в отслеживании стека, создаваемом этим скриптом. По умолчанию:'evalmachine.<anonymous>'. -
lineOffset<число> Указывает смещение номера строки, отображаемое в отслеживании стека, создаваемом этим скриптом. По умолчанию:0. -
columnOffset<число> Указывает смещение номера столбца первой строки, отображаемое в отслеживании стека, создаваемом этим скриптом. По умолчанию:0. -
displayErrors<логическое значение> Еслиtrue, если при компиляцииcodeвозникаетError, строка кода, вызвавшая ошибку, добавляется в отслеживание стека. По умолчанию:true. -
timeout<целое число> Указывает количество миллисекунд для выполненияcodeперед завершением выполнения. Если выполнение завершается, будет брошеноError. Это значение должно быть положительным целым числом. -
breakOnSigint<логическое значение> Еслиtrue, получениеSIGINT(Ctrl+C) завершит выполнение и вызоветError. Существующие обработчики события, присоединенные черезprocess.on('SIGINT'), отключаются во время выполнения скрипта, но продолжают работать после него. По умолчанию:false. -
contextName<строка> Читабельное имя нового контекста. По умолчанию:'VM Context i', гдеi— возрастающий числовой индекс созданного контекста. -
contextOrigin<строка> Происхождение, соответствующее созданному контексту для отображения. Происхождение должно быть отформатировано как URL, но с указанием только схемы, хоста и порта (при необходимости), аналогично значению свойстваurl.originобъектаURL. Важно отметить, что эта строка должна опускать конечный слэш, поскольку он обозначает путь. По умолчанию:''. -
contextCodeGeneration<объект>-
strings<логическое значение> Если установлено в значение false, любые вызовыevalили конструкторов функций (Function,GeneratorFunction, и т.д.) приведут к броскуEvalError. По умолчанию:true. -
wasm<логическое значение> Если установлено в значение false, любая попытка скомпилировать модуль WebAssembly приведет к броскуWebAssembly.CompileError. По умолчанию:true.
-
-
cachedData<Буфер> | <Массив типов> | <DataView> Предоставляет необязательныйBufferилиTypedArray, илиDataViewс данными кэша кода V8 для предоставленного источника. -
importModuleDynamically<Функция> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания способа загрузки модулей во время оценки этого скрипта при вызовеimport(). Этот параметр является частью экспериментального API модулей. Не рекомендуется использовать его в производственной среде. Подробная информация представлена в разделе Поддержка динамическогоimport()в API компиляции. -
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<Буфер> | <Массив_типов> | <Представление_данных> Предоставляет необязательныйBufferилиTypedArray, илиDataViewс данными кэша кода V8 для предоставленного источника. -
importModuleDynamically<Функция> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания способа загрузки модулей во время оценки этого скрипта, когда вызываетсяimport(). Эта опция является частью экспериментального API модулей. Не рекомендуется использовать ее в рабочей среде. Для получения подробной информации см. Поддержка динамическогоimport()в API компиляции.
-
- Возвращает: <любой> результат последнего оператора, выполненного в скрипте.
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. Код, переданный в этот контекст виртуальной машины, будет иметь свою изолированную область видимости.
Для запуска простого веб-сервера с помощью модуля 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 и есть то, что в этом документе называется «контекстуализацией» объекта.
Взаимодействие таймаутов с асинхронными задачами и промисами
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.runInThisContext() не использует эту опцию.
Обработчики промисов помещаются в очередь микробзадач контекста, в котором они были созданы. Например, если () => loop() заменить просто на loop в приведенном выше примере, то loop будет помещен в глобальную очередь микробзадач, поскольку это функция из внешнего (главного) контекста, и поэтому также сможет избежать таймаута.
Если доступны асинхронные функции планирования, такие как process.nextTick(), queueMicrotask(), setTimeout(), setImmediate() и т. д. внутри контекста виртуальной машины, функции, им переданные, будут добавлены в глобальные очереди, которые разделяются всеми контекстами. Следовательно, обработчики, переданные этим функциям, также не контролируются таймаутом.
Поддержка динамического импорта в API компиляции
Следующие API поддерживают параметр importModuleDynamically, чтобы включить динамический импорт в коде, скомпилированном модулем vm.
new vm.Scriptvm.compileFunction()new vm.SourceTextModulevm.runInThisContext()vm.runInContext()vm.runInNewContext()vm.createContext()
Этот параметр всё ещё является частью экспериментального API модулей. Не рекомендуется использовать его в рабочей среде.
Если параметр importModuleDynamically не указан или не определён
Если этот параметр не указан или имеет значение 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 в новом контексте.
Модули CJS
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);
Модули MJS
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); Этот параметр также позволяет скрипту или функции загружать пользовательские модули:
Модули MJS
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);
Модули CJS
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<строка> спецификатор, переданныйimport() -
referrer<vm.Script> | <Функция> | <vm.SourceTextModule> | <Объект> Ссылка — это скомпилированныйvm.Scriptдляnew vm.Script,vm.runInThisContext,vm.runInContextиvm.runInNewContext. Это скомпилированныйFunctionдляvm.compileFunction, скомпилированныйvm.SourceTextModuleдляnew vm.SourceTextModuleи контекстObjectдляvm.createContext(). -
importAttributes<Объект> Значение"with", переданное необязательному параметруoptionsExpression, или пустой объект, если значение не было предоставлено. - Возвращает: <Объект пространства имён модуля> | <vm.Module> Рекомендуется возвращать
vm.Moduleдля использования отслеживания ошибок и предотвращения проблем с пространствами имён, содержащимиthenэкспорт функций.
Модули MJS
// 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' } }
Модули CJS
// 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/api/vm.html