VM (выполнение JavaScript)
[Стабильно: 2 - Стабильно]
Стабильно: 2 Стабильность: 2 - Стабильно
Исходный код: 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) // Контекстуализация объекта.
const code = 'x += 40; var y = 17;'
// `x` и `y` являются глобальными переменными в контексте.
// Изначально, x имеет значение 2, потому что это значение context.x.
vm.runInContext(code, context)
console.log(context.x) // 42
console.log(context.y) // 17
console.log(x) // 1; y не определена.
Класс: vm.Script
Добавлено в: v0.3.1
Экземпляры класса vm.Script
содержат предварительно скомпилированные скрипты, которые могут быть выполнены в определенных контекстах.
new vm.Script(code[, options])
[История]
Версия | Изменения |
---|---|
v21.7.0, v20.12.0 | Добавлена поддержка vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER . |
v17.0.0, v16.12.0 | Добавлена поддержка атрибутов импорта для параметра importModuleDynamically . |
v10.6.0 | produceCachedData устарел в пользу script.createCachedData() . |
v5.7.0 | Теперь поддерживаются параметры cachedData и produceCachedData . |
v0.3.1 | Добавлено в: v0.3.1 |
code
<строка> JavaScript-код для компиляции.options
<Объект> | <строка>filename
<строка> Указывает имя файла, используемое в трассировках стека, созданных этим скриптом. По умолчанию:'evalmachine.\<anonymous\>'
.lineOffset
<число> Указывает смещение номера строки, отображаемое в трассировках стека, созданных этим скриптом. По умолчанию:0
.columnOffset
<число> Указывает смещение номера столбца первой строки, отображаемое в трассировках стека, созданных этим скриптом. По умолчанию:0
.cachedData
<Buffer> | <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
Добавлено в: v5.7.0
Когда cachedData
предоставляется для создания vm.Script
, это значение будет установлено в true
или false
в зависимости от принятия данных V8. В противном случае значение равно undefined
.
script.createCachedData()
Добавлено в: v10.6.0
- Возвращает: <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()
// В `cacheWithoutAdd` функция `add()` помечена для полной компиляции
// при вызове.
script.runInThisContext()
const cacheWithAdd = script.createCachedData()
// `cacheWithAdd` содержит полностью скомпилированную функцию `add()`.
script.runInContext(contextifiedObject[, options])
[История]
Версия | Изменения |
---|---|
v6.3.0 | Теперь поддерживается опция breakOnSigint . |
v0.3.1 | Добавлено в: v0.3.1 |
contextifiedObject
<Object> Объект с контекстом, возвращаемый методомvm.createContext()
.options
<Object>displayErrors
<boolean> Если значениеtrue
, при возникновении ошибкиError
во время компиляцииcode
, строка кода, вызывающая ошибку, прикрепляется к трассировке стека. По умолчанию:true
.timeout
<integer> Указывает количество миллисекунд для выполненияcode
перед прекращением выполнения. Если выполнение прерывается, будет выброшена ошибкаError
. Это значение должно быть строго положительным целым числом.breakOnSigint
<boolean> Еслиtrue
, получениеSIGINT
(+) приведет к завершению выполнения и выбросу ошибки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)
// Выводит: { animal: 'cat', count: 12, name: 'kitty' }
Использование опций timeout
или breakOnSigint
приведет к запуску новых циклов событий и соответствующих потоков, которые имеют ненулевые накладные расходы на производительность.
script.runInNewContext([contextObject[, options]])
[История]
Версия | Изменения |
---|---|
v22.8.0, v20.18.0 | Аргумент contextObject теперь принимает vm.constants.DONT_CONTEXTIFY . |
v14.6.0 | Теперь поддерживается параметр microtaskMode . |
v10.0.0 | Теперь поддерживается параметр contextCodeGeneration . |
v6.3.0 | Теперь поддерживается параметр breakOnSigint . |
v0.3.1 | Добавлено в: v0.3.1 |
contextObject
<Object> | <vm.constants.DONT_CONTEXTIFY> | <undefined> Либоvm.constants.DONT_CONTEXTIFY
, либо объект, который будет контекстуализирован. Еслиundefined
, для обратной совместимости будет создан пустой контекстуализированный объект.options
<Object>displayErrors
<boolean> Еслиtrue
, при возникновении ошибкиError
во время компиляцииcode
, строка кода, вызвавшая ошибку, прикрепляется к трассировке стека. По умолчанию:true
.timeout
<integer> Задает количество миллисекунд для выполненияcode
перед завершением выполнения. Если выполнение прерывается, будет выброшена ошибкаError
. Это значение должно быть строго положительным целым числом.breakOnSigint
<boolean> Еслиtrue
, получениеSIGINT
(+) приведет к завершению выполнения и выбросу ошибки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)
. Он делает несколько вещей одновременно:
В следующем примере компилируется код, который устанавливает глобальную переменную, а затем выполняет код несколько раз в разных контекстах. Глобальные переменные устанавливаются и содержатся в каждом отдельном context
.
const vm = require('node:vm')
const script = new vm.Script('globalVar = "set"')
const contexts = [{}, {}, {}]
contexts.forEach(context => {
script.runInNewContext(context)
})
console.log(contexts)
// Выведет: [{ globalVar: 'set' }, { globalVar: 'set' }, { globalVar: 'set' }]
// Это вызовет ошибку, если контекст создан из контекстуализированного объекта.
// vm.constants.DONT_CONTEXTIFY позволяет создавать контексты с обычными
// глобальными объектами, которые могут быть заморожены.
const freezeScript = new vm.Script('Object.freeze(globalThis); globalThis;')
const frozenContext = freezeScript.runInNewContext(vm.constants.DONT_CONTEXTIFY)
script.runInThisContext([options])
[История]
Версия | Изменения |
---|---|
v6.3.0 | Теперь поддерживается параметр breakOnSigint . |
v0.3.1 | Добавлено в: v0.3.1 |
options
<Object>displayErrors
<boolean> Если значениеtrue
, и при компиляцииcode
возникает ошибкаError
, строка кода, вызвавшая ошибку, добавляется к трассировке стека. По умолчанию:true
.timeout
<integer> Указывает количество миллисекунд для выполненияcode
перед завершением выполнения. Если выполнение прерывается, будет выброшена ошибкаError
. Это значение должно быть строго положительным целым числом.breakOnSigint
<boolean> Еслиtrue
, получениеSIGINT
(+) завершит выполнение и выбросит ошибкуError
. Существующие обработчики события, которые были прикреплены с помощьюprocess.on('SIGINT')
, отключаются во время выполнения скрипта, но продолжают работать после этого. По умолчанию:false
.
Возвращает: <any> результат последнего выполненного в скрипте оператора.
Выполняет скомпилированный код, содержащийся в vm.Script
, в контексте текущего глобального объекта 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
script.sourceMapURL
Добавлено в: v19.1.0, v18.13.0
Если скрипт скомпилирован из источника, содержащего комментарий с волшебной строкой source map, это свойство будет установлено в URL source map.
import vm from 'node:vm'
const script = new vm.Script(`
function myFunc() {}
//# sourceMappingURL=sourcemap.json
`)
console.log(script.sourceMapURL)
// Выведет: sourcemap.json
const vm = require('node:vm')
const script = new vm.Script(`
function myFunc() {}
//# sourceMappingURL=sourcemap.json
`)
console.log(script.sourceMapURL)
// Выведет: sourcemap.json
Класс: vm.Module
Добавлено в: v13.0.0, v12.16.0
[Стабильность: 1 - Экспериментальный]
Стабильность: 1 Стабильность: 1 - Экспериментальный
Эта функция доступна только при включенном флаге командной строки --experimental-vm-modules
.
Класс vm.Module
предоставляет низкоуровневый интерфейс для использования модулей ECMAScript в контекстах VM. Это аналог класса vm.Script
, который точно отражает записи модулей, как определено в спецификации ECMAScript.
Однако, в отличие от vm.Script
, каждый объект vm.Module
привязан к контексту с момента его создания. Операции с объектами vm.Module
по своей природе асинхронны, в отличие от синхронной природы объектов vm.Script
. Использование функций 'async' может помочь в работе с объектами vm.Module
.
Использование объекта vm.Module
требует трех отдельных шагов: создание/разбор, компоновка и вычисление. Эти три шага показаны в следующем примере.
Эта реализация находится на более низком уровне, чем загрузчик модулей ECMAScript. Пока нет возможности взаимодействовать с загрузчиком, хотя поддержка планируется.
import vm from 'node:vm'
const contextifiedObject = vm.createContext({
secret: 42,
print: console.log,
})
// Шаг 1
//
// Создайте модуль, создав новый объект `vm.SourceTextModule`. Это
// разбирает предоставленный исходный текст, вызывая `SyntaxError`, если что-то пойдет
// не так. По умолчанию модуль создается в верхнем контексте. Но здесь мы
// указываем `contextifiedObject` в качестве контекста, которому принадлежит этот модуль.
//
// Здесь мы пытаемся получить экспорт по умолчанию из модуля "foo" и
// поместить его в локальное связывание "secret".
const bar = new vm.SourceTextModule(
`
import s from 'foo';
s;
print(s);
`,
{ context: contextifiedObject }
)
// Шаг 2
//
// "Свяжите" импортированные зависимости этого модуля с ним.
//
// Предоставленный callback компоновки (the "linker") принимает два аргумента:
// родительский модуль (`bar` в данном случае) и строку, которая является спецификатором
// импортированного модуля. Callback должен вернуть модуль,
// соответствующий предоставленному спецификатору, с определенными требованиями, описанными
// в `module.link()`.
//
// Если компоновка не началась для возвращаемого модуля, тот же callback компоновки
// будет вызван для возвращаемого модуля.
//
// Даже модули верхнего уровня без зависимостей должны быть явно связаны. The
// предоставленный callback никогда не будет вызван, однако.
//
// Метод link() возвращает Promise, который будет выполнен, когда все
// Promises, возвращаемые компоновщиком, будут выполнены.
//
// Примечание: это искусственный пример, поскольку функция компоновщика создает новый
// модуль "foo" каждый раз, когда он вызывается. В полноценной системе модулей,
// кэш, вероятно, будет использоваться для избежания дублированных модулей.
async function linker(specifier, referencingModule) {
if (specifier === 'foo') {
return new vm.SourceTextModule(
`
// Переменная "secret" ссылается на глобальную переменную, которую мы добавили к
// "contextifiedObject" при создании контекста.
export default secret;
`,
{ context: referencingModule.context }
)
// Использование `contextifiedObject` вместо `referencingModule.context`
// здесь также сработает.
}
throw new Error(`Невозможно разрешить зависимость: ${specifier}`)
}
await bar.link(linker)
// Шаг 3
//
// Вычислите модуль. Метод evaluate() возвращает promise, который
// будет выполнен после завершения вычисления модуля.
// Выведет 42.
await bar.evaluate()
const vm = require('node:vm')
const contextifiedObject = vm.createContext({
secret: 42,
print: console.log,
})
;(async () => {
// Шаг 1
//
// Создайте модуль, создав новый объект `vm.SourceTextModule`. Это
// разбирает предоставленный исходный текст, вызывая `SyntaxError`, если что-то пойдет
// не так. По умолчанию модуль создается в верхнем контексте. Но здесь мы
// указываем `contextifiedObject` в качестве контекста, которому принадлежит этот модуль.
//
// Здесь мы пытаемся получить экспорт по умолчанию из модуля "foo" и
// поместить его в локальное связывание "secret".
const bar = new vm.SourceTextModule(
`
import s from 'foo';
s;
print(s);
`,
{ context: contextifiedObject }
)
// Шаг 2
//
// "Свяжите" импортированные зависимости этого модуля с ним.
//
// Предоставленный callback компоновки (the "linker") принимает два аргумента:
// родительский модуль (`bar` в данном случае) и строку, которая является спецификатором
// импортированного модуля. Callback должен вернуть модуль,
// соответствующий предоставленному спецификатору, с определенными требованиями, описанными
// в `module.link()`.
//
// Если компоновка не началась для возвращаемого модуля, тот же callback компоновки
// будет вызван для возвращаемого модуля.
//
// Даже модули верхнего уровня без зависимостей должны быть явно связаны. The
// предоставленный callback никогда не будет вызван, однако.
//
// Метод link() возвращает Promise, который будет выполнен, когда все
// Promises, возвращаемые компоновщиком, будут выполнены.
//
// Примечание: это искусственный пример, поскольку функция компоновщика создает новый
// модуль "foo" каждый раз, когда он вызывается. В полноценной системе модулей,
// кэш, вероятно, будет использоваться для избежания дублированных модулей.
async function linker(specifier, referencingModule) {
if (specifier === 'foo') {
return new vm.SourceTextModule(
`
// Переменная "secret" ссылается на глобальную переменную, которую мы добавили к
// "contextifiedObject" при создании контекста.
export default secret;
`,
{ context: referencingModule.context }
)
// Использование `contextifiedObject` вместо `referencingModule.context`
// здесь также сработает.
}
throw new Error(`Невозможно разрешить зависимость: ${specifier}`)
}
await bar.link(linker)
// Шаг 3
//
// Вычислите модуль. Метод evaluate() возвращает promise, который
// будет выполнен после завершения вычисления модуля.
// Выведет 42.
await bar.evaluate()
})()
module.dependencySpecifiers
Спецификаторы всех зависимостей этого модуля. Возвращаемый массив заморожен, чтобы запретить любые изменения в нем.
Соответствует полю [[RequestedModules]]
записей Циклического модуля в спецификации ECMAScript.
module.error
Если module.status
имеет значение 'errored'
, это свойство содержит исключение, выброшенное модулем во время вычисления. Если статус — что-либо другое, обращение к этому свойству приведет к выбросу исключения.
Значение undefined
не может использоваться для случаев, когда исключение не было выброшено, из-за возможной неоднозначности с throw undefined;
.
Соответствует полю [[EvaluationError]]
записей Циклического модуля в спецификации ECMAScript.
module.evaluate([options])
options
<Object>timeout
<integer> Задает количество миллисекунд для вычисления перед завершением выполнения. Если выполнение прерывается, будет выброшено исключениеError
. Это значение должно быть строго положительным целым числом.breakOnSigint
<boolean> Еслиtrue
, получениеSIGINT
(+) завершит выполнение и выбросит исключениеError
. Существующие обработчики события, которые были прикреплены черезprocess.on('SIGINT')
, отключаются во время выполнения скрипта, но продолжают работать после этого. По умолчанию:false
.
Возвращает: <Promise> Выполняется с
undefined
в случае успеха.
Вычислить модуль.
Это должно быть вызвана после того, как модуль был связан; в противном случае он отклонит. Его также можно вызвать, когда модуль уже был вычислен, в этом случае он либо ничего не будет делать, если первоначальное вычисление завершилось успешно (module.status
равно 'evaluated'
), либо он перебросит исключение, которое вызвало первоначальное вычисление (module.status
равно 'errored'
).
Этот метод не может быть вызван, пока модуль вычисляется (module.status
равно 'evaluating'
).
Соответствует полю Конкретного метода Evaluate() записей Циклического модуля в спецификации ECMAScript.
module.identifier
Идентификатор текущего модуля, установленный в конструкторе.
module.link(linker)
[История]
Версия | Изменения |
---|---|
v21.1.0, v20.10.0, v18.19.0 | Опция extra.assert переименована в extra.attributes . Прежнее имя по-прежнему поддерживается для обратной совместимости. |
linker
<Функция>specifier
<строка> Спецификатор запрашиваемого модуля:referencingModule
<vm.Module> ОбъектModule
, для которого вызывается методlink()
.extra
<Объект>attributes
<Объект> Данные из атрибута: согласно ECMA-262, хосты должны генерировать ошибку, если присутствует неподдерживаемый атрибут.assert
<Объект> Псевдоним дляextra.attributes
.Возвращает: <vm.Module> | <Promise>
Возвращает: <Promise>
Связывание зависимостей модулей. Этот метод должен быть вызван перед вычислением и может быть вызван только один раз для каждого модуля.
Функция должна возвращать объект Module
или Promise
, который в конечном итоге разрешается в объект Module
. Возвращаемый Module
должен удовлетворять следующим двум инвариантам:
- Он должен принадлежать тому же контексту, что и родительский
Module
. - Его
status
не должен быть'errored'
.
Если status
возвращаемого Module
равен 'unlinked'
, этот метод будет рекурсивно вызван для возвращаемого Module
с той же предоставленной функцией linker
.
link()
возвращает Promise
, который либо разрешается, когда все экземпляры связывания разрешаются в допустимый Module
, либо отклоняется, если функция linker вызывает исключение или возвращает недопустимый Module
.
Функция linker приблизительно соответствует определяемой реализацией абстрактной операции HostResolveImportedModule в спецификации ECMAScript, с некоторыми ключевыми различиями:
- Функция linker может быть асинхронной, в то время как HostResolveImportedModule является синхронной.
Фактическая реализация HostResolveImportedModule, используемая во время связывания модулей, возвращает модули, связанные во время связывания. Поскольку к этому моменту все модули уже были бы полностью связаны, реализация HostResolveImportedModule является полностью синхронной согласно спецификации.
Соответствует полю Link() конкретного метода циклических записей модулей в спецификации ECMAScript.
module.namespace
Объект пространства имён модуля. Он доступен только после завершения компоновки (module.link()
).
Соответствует абстрактной операции GetModuleNamespace в спецификации ECMAScript.
module.status
Текущий статус модуля. Будет одним из:
'unlinked'
:module.link()
ещё не вызывался.'linking'
:module.link()
был вызван, но не все Promise, возвращаемые функцией компоновщика, ещё разрешены.'linked'
: Модуль был успешно скомпонован, и все его зависимости скомпонованы, ноmodule.evaluate()
ещё не вызывался.'evaluating'
: Модуль оценивается черезmodule.evaluate()
самого себя или родительского модуля.'evaluated'
: Модуль был успешно оценен.'errored'
: Модуль был оценен, но было выброшено исключение.
За исключением 'errored'
, эта строка состояния соответствует полю [[Status]]
записи Циклический модуль в спецификации. 'errored'
соответствует 'evaluated'
в спецификации, но с [[EvaluationError]]
, установленным в значение, отличное от undefined
.
Класс: vm.SourceTextModule
Добавлено в: v9.6.0
[Стабильно: 1 - Экспериментально]
Стабильно: 1 Стабильность: 1 - Экспериментально
Эта функция доступна только при включенном флаге командной строки --experimental-vm-modules
.
- Расширяет: <vm.Module>
Класс vm.SourceTextModule
предоставляет Запись модуля исходного текста, как определено в спецификации ECMAScript.
new vm.SourceTextModule(code[, options])
[История]
Версия | Изменения |
---|---|
v17.0.0, v16.12.0 | Добавлена поддержка атрибутов импорта для параметра importModuleDynamically . |
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()
, чтобы создавать объекты в определённом контексте.
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) {
// Примечание: этот объект создаётся в верхнем контексте. Таким образом,
// Object.getPrototypeOf(import.meta.prop) указывает на
// Object.prototype в верхнем контексте, а не в
// контекстуализированном объекте.
meta.prop = {}
},
})
// Поскольку модуль не имеет зависимостей, функция компоновщика никогда не будет вызвана.
await module.link(() => {})
await module.evaluate()
// Теперь Object.prototype.secret будет равен 42.
//
// Чтобы исправить эту проблему, замените
// meta.prop = {};
// выше на
// meta.prop = vm.runInContext('{}', contextifiedObject);
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) {
// Примечание: этот объект создаётся в верхнем контексте. Таким образом,
// Object.getPrototypeOf(import.meta.prop) указывает на
// Object.prototype в верхнем контексте, а не в
// контекстуализированном объекте.
meta.prop = {}
},
})
// Поскольку модуль не имеет зависимостей, функция компоновщика никогда не будет вызвана.
await module.link(() => {})
await module.evaluate()
// Теперь Object.prototype.secret будет равен 42.
//
// Чтобы исправить эту проблему, замените
// meta.prop = {};
// выше на
// meta.prop = vm.runInContext('{}', contextifiedObject);
})()
sourceTextModule.createCachedData()
Добавлено в: v13.7.0, v12.17.0
- Возвращает: <Buffer>
Создает кэш кода, который может использоваться с опцией cachedData
конструктора SourceTextModule
. Возвращает Buffer
. Этот метод может вызываться любое количество раз до того, как модуль будет оценен.
Кэш кода SourceTextModule
не содержит никаких наблюдаемых состояний JavaScript. Кэш кода можно безопасно сохранять вместе с исходным кодом скрипта и использовать для многократного создания новых экземпляров SourceTextModule
.
Функции в исходном коде SourceTextModule
могут быть помечены как лениво компилируемые, и они не компилируются при создании SourceTextModule
. Эти функции будут компилироваться при их первом вызове. Кэш кода сериализует метаданные, которые V8 в настоящее время знает о SourceTextModule
, которые он может использовать для ускорения будущих компиляций.
// Создаем начальный модуль
const module = new vm.SourceTextModule('const a = 1;')
// Создаем кэшированные данные из этого модуля
const cachedData = module.createCachedData()
// Создаем новый модуль, используя кэшированные данные. Код должен быть тем же.
const module2 = new vm.SourceTextModule('const a = 1;', { cachedData })
Класс: vm.SyntheticModule
Добавлено в: v13.0.0, v12.16.0
[Стабильно: 1 - Экспериментально]
Стабильно: 1 Стабильность: 1 - Экспериментально
Эта функция доступна только при включенном флаге командной строки --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)
})
// Использование `module` при компоновке...
new vm.SyntheticModule(exportNames, evaluateCallback[, options])
Добавлено в: v13.0.0, v12.16.0
exportNames
<string[]> Массив имен, которые будут экспортированы из модуля.evaluateCallback
<Function> Вызывается при вычислении модуля.options
identifier
<string> Строка, используемая в трассировках стека. По умолчанию:'vm:module(i)'
, гдеi
— индекс возрастания, зависящий от контекста.context
<Object> Контекстуализированный объект, возвращаемый методомvm.createContext()
, для компиляции и вычисления этогоModule
.
Создает новый экземпляр SyntheticModule
.
Объекты, назначенные экспорту этого экземпляра, могут позволить импортерам модуля получать доступ к информации за пределами указанного context
. Используйте vm.runInContext()
, чтобы создавать объекты в определенном контексте.
syntheticModule.setExport(name, value)
Добавлено в: v13.0.0, v12.16.0
name
<string> Имя экспорта для установки.value
<any> Значение, которое нужно установить для экспорта.
Этот метод используется после связывания модуля для установки значений экспорта. Если он вызывается до связывания модуля, будет выброшена ошибка ERR_VM_MODULE_STATUS
.
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)
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]])
[История]
Версия | Изменения |
---|---|
v21.7.0, v20.12.0 | Добавлена поддержка vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER . |
v19.6.0, v18.15.0 | Возвращаемое значение теперь включает cachedDataRejected с той же семантикой, что и версия vm.Script , если был передан параметр cachedData . |
v17.0.0, v16.12.0 | Добавлена поддержка атрибутов импорта для параметра importModuleDynamically . |
v15.9.0 | Опция importModuleDynamically добавлена снова. |
v14.3.0 | Удаление importModuleDynamically из-за проблем совместимости. |
v14.1.0, v13.14.0 | Теперь поддерживается опция importModuleDynamically . |
v10.10.0 | Добавлено в: v10.10.0 |
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
Добавлен в: v21.7.0, v20.12.0
Возвращает объект, содержащий часто используемые константы для операций VM.
vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER
Добавлен в: v21.7.0, v20.12.0
[Стабильно: 1 - Экспериментально]
Стабильно: 1 Стабильность: 1.1 - Активная разработка
Константа, которая может использоваться в качестве параметра importModuleDynamically
для vm.Script
и vm.compileFunction()
, чтобы Node.js использовал загрузчик ESM по умолчанию из основного контекста для загрузки запрошенного модуля.
Для получения подробной информации см. Поддержка динамического import()
в API компиляции.
vm.createContext([contextObject[, options]])
[История]
Версия | Изменения |
---|---|
v22.8.0, v20.18.0 | Аргумент contextObject теперь принимает vm.constants.DONT_CONTEXTIFY . |
v21.7.0, v20.12.0 | Добавлена поддержка vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER . |
v21.2.0, v20.11.0 | Теперь поддерживается параметр importModuleDynamically . |
v14.6.0 | Теперь поддерживается параметр microtaskMode . |
v10.0.0 | Первый аргумент больше не может быть функцией. |
v10.0.0 | Теперь поддерживается параметр codeGeneration . |
v0.3.1 | Добавлено в: v0.3.1 |
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)
// Выведет: { globalVar: 2 }
console.log(global.globalVar)
// Выведет: 3
Если contextObject
опущен (или передан явно как undefined
), будет возвращен новый, пустой контекстуализированный объект.
Когда глобальный объект в вновь созданном контексте контекстуализирован, он имеет некоторые особенности по сравнению с обычными глобальными объектами. Например, его нельзя заморозить. Чтобы создать контекст без особенностей контекстуализации, передайте vm.constants.DONT_CONTEXTIFY
в качестве аргумента contextObject
. Подробности см. в документации по vm.constants.DONT_CONTEXTIFY
.
Метод vm.createContext()
в основном полезен для создания одного контекста, который можно использовать для запуска нескольких скриптов. Например, при эмуляции веб-браузера этот метод можно использовать для создания одного контекста, представляющего глобальный объект окна, а затем запускать все теги \<script\>
вместе в этом контексте.
Предоставленные name
и origin
контекста отображаются через API инспектора.
vm.isContext(object)
Добавлено в: v0.11.7
Возвращает true
, если заданный объект object
был контекстуализирован с помощью vm.createContext()
или если это глобальный объект контекста, созданного с помощью vm.constants.DONT_CONTEXTIFY
.
vm.measureMemory([options])
Добавлено в: v13.10.0
[Стабильность: 1 - Экспериментально]
Стабильность: 1 Стабильность: 1 - Экспериментально
Измеряет объем памяти, известный V8 и используемый всеми контекстами, известными текущему изоляту V8, или основным контекстом.
options
<Object> Необязательно.mode
<string> Либо'summary'
, либо'detailed'
. В режиме summary возвращается только память, измеренная для основного контекста. В подробном режиме возвращается память, измеренная для всех контекстов, известных текущему изоляту V8. По умолчанию:'summary'
execution
<string> Либо'default'
, либо'eager'
. При выполнении по умолчанию promise не разрешается до начала следующей запланированной сборки мусора, что может занять некоторое время (или никогда, если программа завершится до следующей сборки мусора). При усердном выполнении сборка мусора будет запущена немедленно для измерения памяти. По умолчанию:'default'
Возвращает: <Promise> Если память успешно измерена, promise разрешается с объектом, содержащим информацию об использовании памяти. В противном случае он отклоняется с ошибкой
ERR_CONTEXT_NOT_INITIALIZED
.
Формат объекта, который может разрешить возвращенный Promise, специфичен для движка V8 и может изменяться от одной версии V8 к другой.
Возвращаемый результат отличается от статистики, возвращаемой v8.getHeapSpaceStatistics()
, тем, что vm.measureMemory()
измеряет память, доступную для каждого контекста, специфичного для V8, в текущем экземпляре движка V8, тогда как результат v8.getHeapSpaceStatistics()
измеряет память, занимаемую каждым пространством кучи в текущем экземпляре V8.
const vm = require('node:vm')
// Измерение памяти, используемой основным контекстом.
vm.measureMemory({ mode: 'summary' })
// Это то же самое, что и vm.measureMemory()
.then(result => {
// Текущий формат:
// {
// total: {
// jsMemoryEstimate: 2418479, jsMemoryRange: [ 2418479, 2745799 ]
// }
// }
console.log(result)
})
const context = vm.createContext({ a: 1 })
vm.measureMemory({ mode: 'detailed', execution: 'eager' }).then(result => {
// Ссылка на контекст здесь, чтобы он не был очищен сборщиком мусора
// до завершения измерения.
console.log(context.a)
// {
// total: {
// jsMemoryEstimate: 2574732,
// jsMemoryRange: [ 2574732, 2904372 ]
// },
// current: {
// jsMemoryEstimate: 2438996,
// jsMemoryRange: [ 2438996, 2768636 ]
// },
// other: [
// {
// jsMemoryEstimate: 135736,
// jsMemoryRange: [ 135736, 465376 ]
// }
// ]
// }
console.log(result)
})
vm.runInContext(code, contextifiedObject[, options])
[История]
Версия | Изменения |
---|---|
v21.7.0, v20.12.0 | Добавлена поддержка vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER . |
v17.0.0, v16.12.0 | Добавлена поддержка атрибутов импорта для параметра importModuleDynamically . |
v6.3.0 | Теперь поддерживается параметр breakOnSigint . |
v0.3.1 | Добавлено в: v0.3.1 |
code
<строка> JavaScript-код для компиляции и выполнения.contextifiedObject
<Объект> Контекстуализированный объект, который будет использоваться в качествеglobal
при компиляции и выполненииcode
.options
<Объект> | <строка>filename
<строка> Указывает имя файла, используемое в трассировках стека, создаваемых этим скриптом. По умолчанию:'evalmachine.<anonymous>'
.lineOffset
<число> Указывает смещение номера строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию:0
.columnOffset
<число> Указывает смещение номера столбца первой строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию:0
.displayErrors
<булево> Еслиtrue
, то при возникновении ошибкиError
во время компиляцииcode
, строка кода, вызвавшая ошибку, прикрепляется к трассировке стека. По умолчанию:true
.timeout
<целое число> Указывает количество миллисекунд для выполненияcode
перед прекращением выполнения. Если выполнение прерывается, будет выброшена ошибкаError
. Это значение должно быть строго положительным целым числом.breakOnSigint
<булево> Еслиtrue
, получениеSIGINT
(+) приведет к прекращению выполнения и выбросу ошибкиError
. Существующие обработчики события, прикрепленные черезprocess.on('SIGINT')
, отключаются во время выполнения скрипта, но продолжают работать после этого. По умолчанию:false
.cachedData
<Buffer> | <TypedArray> | <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)
// Выведет: { globalVar: 1024 }
vm.runInNewContext(code[, contextObject[, options]])
[История]
Версия | Изменения |
---|---|
v22.8.0, v20.18.0 | Аргумент contextObject теперь принимает vm.constants.DONT_CONTEXTIFY . |
v21.7.0, v20.12.0 | Добавлена поддержка vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER . |
v17.0.0, v16.12.0 | Добавлена поддержка атрибутов импорта для параметра importModuleDynamically . |
v14.6.0 | Теперь поддерживается параметр microtaskMode . |
v10.0.0 | Теперь поддерживается параметр contextCodeGeneration . |
v6.3.0 | Теперь поддерживается параметр breakOnSigint . |
v0.3.1 | Добавлено в: v0.3.1 |
code
<строка> JavaScript-код для компиляции и выполнения.contextObject
<Объект> | <vm.constants.DONT_CONTEXTIFY> | <неопределено> Либоvm.constants.DONT_CONTEXTIFY
, либо объект, который будет контекстуализирован. Еслиundefined
, для обратной совместимости будет создан пустой контекстуализированный объект.filename
<строка> Указывает имя файла, используемое в трассировках стека, создаваемых этим скриптом. По умолчанию:'evalmachine.\<anonymous\>'
.lineOffset
<число> Указывает смещение номера строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию:0
.columnOffset
<число> Указывает смещение номера столбца первой строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию:0
.displayErrors
<булево> Еслиtrue
, при возникновении ошибкиError
во время компиляцииcode
, строка кода, вызвавшая ошибку, добавляется к трассировке стека. По умолчанию:true
.timeout
<целое число> Указывает количество миллисекунд для выполненияcode
перед завершением выполнения. Если выполнение прерывается, будет выброшена ошибкаError
. Это значение должно быть строго положительным целым числом.breakOnSigint
<булево> Еслиtrue
, получениеSIGINT
(+) приведет к завершению выполнения и выбросу ошибки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
<Buffer> | <TypedArray> | <DataView> Предоставляет необязательныйBuffer
илиTypedArray
, илиDataView
с данными кэша кода V8 для предоставленного источника.importModuleDynamically
<Функция> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания того, как должны загружаться модули во время вычисления этого скрипта, когда вызываетсяimport()
. Этот параметр является частью экспериментального API модулей. Мы не рекомендуем использовать его в рабочей среде. Для получения подробной информации см. Поддержка динамическогоimport()
в API компиляции.microtaskMode
<строка> Если установлено вafterEvaluate
, микрозадачи (задачи, запланированные черезPromise
иasync function
) будут выполняться сразу после выполнения скрипта. В этом случае они включены в области действияtimeout
иbreakOnSigint
.
Возвращает: <любое> результат последнего выполненного оператора в скрипте.
Этот метод является сокращением для (new vm.Script(code, options)).runInContext(vm.createContext(options), options)
. Если options
является строкой, то она указывает имя файла.
Он делает несколько вещей одновременно:
В следующем примере компилируется и выполняется код, который увеличивает глобальную переменную и устанавливает новую. Эти глобальные переменные содержатся в contextObject
.
const vm = require('node:vm')
const contextObject = {
animal: 'cat',
count: 2,
}
vm.runInNewContext('count += 1; name = "kitty"', contextObject)
console.log(contextObject)
// Выведет: { animal: 'cat', count: 3, name: 'kitty' }
// Это вызовет ошибку, если контекст создан из контекстуализированного объекта.
// vm.constants.DONT_CONTEXTIFY позволяет создавать контексты с обычными глобальными объектами, которые
// могут быть заморожены.
const frozenContext = vm.runInNewContext('Object.freeze(globalThis); globalThis;', vm.constants.DONT_CONTEXTIFY)
vm.runInThisContext(code[, options])
[История]
Версия | Изменения |
---|---|
v21.7.0, v20.12.0 | Добавлена поддержка vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER . |
v17.0.0, v16.12.0 | Добавлена поддержка атрибутов импорта для параметра importModuleDynamically . |
v6.3.0 | Теперь поддерживается опция breakOnSigint . |
v0.3.1 | Добавлено в: v0.3.1 |
code
<строка> JavaScript-код для компиляции и выполнения.filename
<строка> Указывает имя файла, используемое в трассировках стека, создаваемых этим скриптом. По умолчанию:'evalmachine.\<anonymous\>'
.lineOffset
<число> Указывает смещение номера строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию:0
.columnOffset
<число> Указывает смещение номера столбца первой строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию:0
.displayErrors
<булево> Если значениеtrue
, то при возникновении ошибкиError
во время компиляцииcode
, строка кода, вызывающая ошибку, прикрепляется к трассировке стека. По умолчанию:true
.timeout
<целое число> Указывает количество миллисекунд для выполненияcode
перед прекращением выполнения. Если выполнение прерывается, будет выброшена ошибкаError
. Это значение должно быть строго положительным целым числом.breakOnSigint
<булево> Еслиtrue
, получениеSIGINT
(+) приведет к прекращению выполнения и выбросу ошибкиError
. Существующие обработчики события, прикрепленные с помощьюprocess.on('SIGINT')
, отключаются во время выполнения скрипта, но продолжают работать после этого. По умолчанию:false
.cachedData
<Buffer> | <TypedArray> | <DataView> Предоставляет необязательный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}'`)
// Выведет: vmResult: 'vm', localVar: 'initial value'
const evalResult = eval('localVar = "eval";')
console.log(`evalResult: '${evalResult}', localVar: '${localVar}'`)
// Выведет: evalResult: 'eval', localVar: 'eval'
Поскольку 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)
require()
в данном случае разделяет состояние с контекстом, из которого он передаётся. Это может привести к рискам при выполнении ненадежного кода, например, к нежелательному изменению объектов в контексте.
Что означает «контекстуализация» объекта?
Весь JavaScript, выполняемый в Node.js, работает в рамках «контекста». Согласно Руководству разработчика V8:
Когда метод vm.createContext()
вызывается с объектом, аргумент contextObject
будет использоваться для обертывания глобального объекта нового экземпляра контекста V8 (если contextObject
равен undefined
, новый объект будет создан из текущего контекста перед его контекстуализацией). Этот контекст V8 предоставляет коду, выполняемому с помощью методов модуля node:vm
, изолированную глобальную среду, в которой он может работать. Процесс создания контекста V8 и его связывания с contextObject
во внешнем контексте — это то, что в данном документе называется «контекстуализацией» объекта.
Контекстуализация внесёт некоторые особенности в значение globalThis
в контексте. Например, его нельзя заморозить, и он не является ссылочно равным contextObject
во внешнем контексте.
const vm = require('node:vm')
// Неопределённый параметр `contextObject` контекстуализирует глобальный объект.
const context = vm.createContext()
console.log(vm.runInContext('globalThis', context) === context) // false
// Контекстуализированный глобальный объект нельзя заморозить.
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
Для создания контекста с обычным глобальным объектом и получения доступа к глобальному прокси во внешнем контексте с меньшим количеством особенностей укажите vm.constants.DONT_CONTEXTIFY
в качестве аргумента contextObject
.
vm.constants.DONT_CONTEXTIFY
Эта константа, используемая в качестве аргумента contextObject
в API vm, предписывает Node.js создавать контекст без обертывания его глобального объекта другим объектом специфическим для Node.js образом. В результате значение globalThis
внутри нового контекста будет вести себя более похоже на обычное.
const vm = require('node:vm')
// Используйте vm.constants.DONT_CONTEXTIFY для замораживания глобального объекта.
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
}
Когда vm.constants.DONT_CONTEXTIFY
используется в качестве аргумента contextObject
для vm.createContext()
, возвращаемый объект представляет собой похожий на прокси объект для глобального объекта в вновь созданном контексте с меньшим количеством специфичных для Node.js особенностей. Он ссылочно эквивалентен значению globalThis
в новом контексте, может быть изменен извне контекста и может использоваться для прямого доступа к встроенным функциям в новом контексте.
const vm = require('node:vm')
const context = vm.createContext(vm.constants.DONT_CONTEXTIFY)
// Возвращаемый объект ссылочно эквивалентен globalThis в новом контексте.
console.log(vm.runInContext('globalThis', context) === context) // true
// Может использоваться для прямого доступа к глобальным переменным в новом контексте.
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
// Может быть заморожен, и это влияет на внутренний контекст.
Object.freeze(context)
try {
vm.runInContext('baz = 1; baz;', context)
} catch (e) {
console.log(e) // Uncaught ReferenceError: baz is not defined
}
Взаимодействие таймаута с асинхронными задачами и Promise
Promise
и async function
могут планировать задачи, выполняемые движком JavaScript асинхронно. По умолчанию эти задачи выполняются после завершения выполнения всех функций JavaScript в текущем стеке. Это позволяет обойти функциональность параметров timeout
и breakOnSigint
.
Например, следующий код, выполненный vm.runInNewContext()
с таймаутом 5 миллисекунд, планирует бесконечный цикл для выполнения после разрешения promise. Планируемый цикл никогда не прерывается таймаутом:
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 })
// Это выводится *до* 'entering loop' (!)
console.log('done executing')
Это можно исправить, передав 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' }
)
В этом случае микрозадача, запланированная через promise.then()
, будет выполнена до возврата из vm.runInNewContext()
и будет прервана функциональностью timeout
. Это относится только к коду, выполняющемуся в vm.Context
, поэтому, например, vm.runInThisContext()
не принимает этот параметр.
Обратные вызовы Promise помещаются в очередь микрозадач контекста, в котором они были созданы. Например, если () => loop()
заменить просто на loop
в приведенном выше примере, то loop
будет помещен в глобальную очередь микрозадач, поскольку это функция из внешнего (главного) контекста, и, таким образом, также сможет избежать таймаута.
Если асинхронные функции планирования, такие как process.nextTick()
, queueMicrotask()
, setTimeout()
, setImmediate()
и т. д., становятся доступными внутри vm.Context
, функции, переданные им, будут добавлены в глобальные очереди, которые являются общими для всех контекстов. Поэтому обратные вызовы, переданные этим функциям, также не контролируются с помощью таймаута.
Поддержка динамического import()
в API компиляции
Следующие API поддерживают опцию importModuleDynamically
для включения динамического import()
в коде, скомпилированном модулем vm
.
new vm.Script
vm.compileFunction()
new vm.SourceTextModule
vm.runInThisContext()
vm.runInContext()
vm.runInNewContext()
vm.createContext()
Эта опция все еще является частью экспериментального API модулей. Мы не рекомендуем использовать ее в production-окружении.
Когда опция 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
встроенных классов в новом контексте.
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, загруженный из основного контекста, не является экземпляром класса Function
// в новом контексте.
script.runInNewContext().then(console.log)
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, загруженный из основного контекста, не является экземпляром класса Function
// в новом контексте.
script.runInNewContext().then(console.log)
Эта опция также позволяет скрипту или функции загружать пользовательские модули:
import { Script, constants } from 'node:vm'
import { resolve } from 'node:path'
import { writeFileSync } from 'node:fs'
// Запись test.js и test.txt в директорию, где выполняется текущий скрипт.
writeFileSync(resolve(import.meta.dirname, 'test.mjs'), 'export const filename = "./test.json";')
writeFileSync(resolve(import.meta.dirname, 'test.json'), '{"hello": "world"}')
// Компиляция скрипта, который загружает test.mjs, а затем test.json
// как если бы скрипт находился в той же директории.
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)
const { Script, constants } = require('node:vm')
const { resolve } = require('node:path')
const { writeFileSync } = require('node:fs')
// Запись test.js и test.txt в директорию, где выполняется текущий скрипт.
writeFileSync(resolve(__dirname, 'test.mjs'), 'export const filename = "./test.json";')
writeFileSync(resolve(__dirname, 'test.json'), '{"hello": "world"}')
// Компиляция скрипта, который загружает test.mjs, а затем test.json
// как если бы скрипт находился в той же директории.
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)
Существует несколько предостережений при загрузке пользовательских модулей с помощью загрузчика по умолчанию из основного контекста:
Когда 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
. Это скомпилированнаяFunction
дляvm.compileFunction
, скомпилированныйvm.SourceTextModule
дляnew vm.SourceTextModule
и контекстныйObject
дляvm.createContext()
.importAttributes
<Object> Значение"with"
, переданное в необязательный параметрoptionsExpression
, или пустой объект, если значение не было предоставлено.- Возвращаемое значение: <Module Namespace Object> | <vm.Module> Рекомендуется возвращать
vm.Module
, чтобы воспользоваться преимуществами отслеживания ошибок и избежать проблем с пространствами имен, содержащими экспортируемые функцииthen
.
// Этот скрипт должен запускаться с --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) // Скомпилированный скрипт
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' } }
// Этот скрипт должен запускаться с --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) // Скомпилированный скрипт
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' } }
})()